Якщо немає динамічної диспетчеризації (поліморфізм), "методи" - це лише цукристі функції, можливо з неявним додатковим параметром. Відповідно, екземпляри класів без поліморфної поведінки по суті є C struct
s з метою генерування коду.
Для класичної динамічної диспетчеризації в системі статичного типу в основному існує одна переважаюча стратегія: vtables. Кожен екземпляр отримує один додатковий покажчик, який посилається на (обмежене представлення) його типу, головне на vtable: Масив функціональних покажчиків, по одному на метод. Оскільки повний набір методів для кожного типу (у ланцюжку успадкування) відомий під час компіляції, можна призначити послідовні індекси (0..N для N методів) методам і викликати методи, шукаючи вказівник функції в vtable з використанням цього індексу (знову передаючи посилання на екземпляр як додатковий параметр).
Для більш динамічних мов на основі класів зазвичай самі класи є об'єктами першого класу, і кожен об'єкт замість цього має посилання на свій клас класу. Об'єкт класу, у свою чергу, володіє методами в залежності від мови (у Ruby методи є основною частиною об'єктної моделі, в Python вони просто функціонують об'єктами з крихітними обгортками навколо них). Класи, як правило, також зберігають посилання на свої суперкласи, і делегують пошук спадкових методів тим класам, щоб допомогти метапрограмуванню, яке додає та змінює методи.
Є багато інших систем, які не базуються на класах, але вони суттєво відрізняються, тому я виберу лише одну цікаву альтернативу дизайну: Коли ви можете додавати нові (набори) методів для всіх типів за бажанням у будь-якій точці програми ( наприклад, класи типів в Haskell і риси в Rust), повний набір методів не відомий під час компіляції. Щоб вирішити цю проблему, створюється vtable на кожну ознаку та передає їх навколо, коли потрібна реалізація ознаки. Тобто, такий код:
void needs_a_trait(SomeTrait &x) { x.method2(1); }
ConcreteType x = ...;
needs_a_trait(x);
складається до цього:
functionpointer SomeTrait_ConcreteType_vtable[] = { &method1, &method2, ... };
void needs_a_trait(void *x, functionpointer vtable[]) { vtable[1](x, 1); }
ConcreteType x = ...;
needs_a_trait(x, SomeTrait_ConcreteType_vtable);
Це також означає, що vtable інформація не вбудована в об’єкт. Якщо ви хочете посилатися на "екземпляр ознаки", який буде вести себе правильно, коли, наприклад, зберігається в структурах даних, що містять багато різних типів, можна створити жирний покажчик (instance_pointer, trait_vtable)
. Це фактично узагальнення вищезгаданої стратегії.