Nếu không có công văn động (đa hình), "phương thức" chỉ là các hàm có đường, có lẽ với một tham số bổ sung ngầm định. Theo đó, các thể hiện của các lớp không có hành vi đa hình về cơ bản là C struct
s cho mục đích tạo mã.
Đối với công văn động cổ điển trong một hệ thống kiểu tĩnh, về cơ bản có một chiến lược chiếm ưu thế: vtables. Mỗi phiên bản có một con trỏ bổ sung tham chiếu đến (một đại diện giới hạn) loại của nó, quan trọng nhất là vtable: Một mảng các con trỏ hàm, một cho mỗi phương thức. Do tập hợp đầy đủ các phương thức cho mọi loại (trong chuỗi thừa kế) được biết đến tại thời điểm biên dịch, nên người ta có thể gán các chỉ số liên tiếp (0..N cho các phương thức N) cho các phương thức và gọi các phương thức bằng cách tra cứu con trỏ hàm trong vtable sử dụng chỉ mục này (một lần nữa chuyển tham chiếu thể hiện dưới dạng tham số bổ sung).
Đối với các ngôn ngữ dựa trên lớp động hơn, bản thân các lớp thường là các đối tượng hạng nhất và mỗi đối tượng thay vào đó có một tham chiếu đến đối tượng lớp của nó. Đến lượt, đối tượng lớp sở hữu các phương thức theo một số cách phụ thuộc ngôn ngữ (trong Ruby, các phương thức là một phần cốt lõi của mô hình đối tượng, trong Python chúng chỉ là các đối tượng chức năng với các hàm bao nhỏ xung quanh chúng). Các lớp thường lưu trữ các tham chiếu đến (các) siêu lớp của chúng và ủy thác việc tìm kiếm các phương thức được kế thừa cho các lớp đó để hỗ trợ siêu lập trình có thêm và thay đổi các phương thức.
Có nhiều hệ thống khác không dựa trên các lớp, nhưng chúng khác nhau đáng kể, vì vậy tôi sẽ chỉ chọn một phương án thiết kế thú vị: Khi bạn có thể thêm các phương thức (bộ) mới cho tất cả các loại theo bất kỳ nơi nào trong chương trình ( ví dụ: các lớp loại trong Haskell và các đặc điểm trong Rust), toàn bộ các phương thức không được biết trong khi biên dịch. Để giải quyết điều này, người ta tạo ra một vtable cho mỗi tính trạng và chuyển chúng xung quanh khi yêu cầu thực hiện tính trạng. Đó là, mã như thế này:
void needs_a_trait(SomeTrait &x) { x.method2(1); }
ConcreteType x = ...;
needs_a_trait(x);
được tổng hợp xuống đây:
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);
Điều này cũng có nghĩa là thông tin vtable không được nhúng trong đối tượng. Nếu bạn muốn tham chiếu đến một "thể hiện của một đặc điểm" sẽ hành xử chính xác khi, ví dụ, được lưu trữ trong các cấu trúc dữ liệu có chứa nhiều loại khác nhau, người ta có thể tạo một con trỏ béo (instance_pointer, trait_vtable)
. Đây thực sự là một khái quát của chiến lược trên.