Jeśli nie ma dynamicznej wysyłki (polimorfizm), „metody” są tylko funkcjami cukierkowymi, być może z niejawnym dodatkowym parametrem. Odpowiednio, instancje klas bez zachowania polimorficznego są zasadniczo Cs struct
do celów generowania kodu.
Dla klasycznej dynamicznej wysyłki w systemie typu statycznego istnieje zasadniczo jedna dominująca strategia: vtables. Każda instancja otrzymuje jeden dodatkowy wskaźnik, który odnosi się do (ograniczonej reprezentacji) jego typu, a przede wszystkim vtable: tablica wskaźników funkcji, po jednej na metodę. Ponieważ pełny zestaw metod dla każdego typu (w łańcuchu dziedziczenia) jest znany w czasie kompilacji, można przypisywać kolejne indeksy (0..N dla N metod) do metod i wywoływać metody, patrząc na wskaźnik funkcji w vtable przy użyciu tego indeksu (ponownie przekazując odwołanie do instancji jako dodatkowy parametr).
W przypadku bardziej dynamicznych języków opartych na klasach zazwyczaj same klasy są obiektami pierwszej klasy, a każdy obiekt zamiast tego ma odniesienie do swojego obiektu klasy. Z kolei obiekt klasy jest właścicielem metod w pewien sposób zależny od języka (w Rubim metody są podstawową częścią modelu obiektowego, w Pythonie są tylko obiektami funkcyjnymi z niewielkimi opakowaniami wokół nich). Klasy zwykle przechowują również odniesienia do swoich nadklas (klas) i delegują wyszukiwanie odziedziczonych metod do tych klas, aby wspomóc metaprogramowanie, które dodaje i zmienia metody.
Istnieje wiele innych systemów, które nie są oparte na klasach, ale różnią się znacznie, więc wybiorę tylko jedną ciekawą alternatywę projektową: kiedy możesz dodawać nowe (zestawy) metod do wszystkich typów w dowolnym miejscu w programie ( np. klasy typów w Haskell i cechy w Rust), pełny zestaw metod nie jest znany podczas kompilacji. Aby rozwiązać ten problem, tworzy się tabelę vt na cechę i przekazuje ją, gdy wymagana jest implementacja cechy. Oznacza to, że kod taki jak ten:
void needs_a_trait(SomeTrait &x) { x.method2(1); }
ConcreteType x = ...;
needs_a_trait(x);
sprowadza się do tego:
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);
Oznacza to również, że informacje vtable nie są osadzone w obiekcie. Jeśli chcesz odwoływać się do „instancji cechy”, która zachowa się poprawnie, gdy na przykład będzie przechowywana w strukturach danych zawierających wiele różnych typów, możesz utworzyć gruby wskaźnik (instance_pointer, trait_vtable)
. To właściwie uogólnienie powyższej strategii.