Если динамическая диспетчеризация отсутствует (полиморфизм), «методы» - это просто сладкие функции, возможно, с неявным дополнительным параметром. Соответственно, экземпляры классов без полиморфного поведения по существу являются C struct
s для генерации кода.
Для классической динамической диспетчеризации в системе статического типа существует в основном одна доминирующая стратегия: vtables. Каждый экземпляр получает один дополнительный указатель, который ссылается на (ограниченное представление) своего типа, наиболее важно vtable: массив указателей на функции, по одному на метод. Поскольку полный набор методов для каждого типа (в цепочке наследования) известен во время компиляции, можно присвоить методам последовательные индексы (0..N для N методов) и вызывать методы, просматривая указатель на функцию в vtable, использующий этот индекс (снова передавая ссылку на экземпляр как дополнительный параметр).
Для более динамических языков на основе классов сами классы обычно являются первоклассными объектами, и каждый объект вместо этого имеет ссылку на свой объект класса. Объект класса, в свою очередь, владеет методами в зависимости от языка (в Ruby методы являются основной частью объектной модели, в Python они являются просто функциональными объектами с крошечными обертками вокруг них). Классы обычно хранят ссылки на свои суперклассы и делегируют поиск унаследованных методов этим классам, чтобы помочь метапрограммированию, которое добавляет и изменяет методы.
Есть много других систем, которые не основаны на классах, но они существенно различаются, поэтому я выберу только одну интересную альтернативу разработки: когда вы можете добавлять новые (наборы) методов ко всем типам по желанию в любом месте программы ( например, классы типов в Haskell и traits в 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)
. На самом деле это обобщение вышеуказанной стратегии.