Đầu tiên: không có chức năng ảo, có thể không có vptr
ở tất cả các lớp. 8 byte bạn đang thấy là một tạo tác cách thức thừa kế ảo được triển khai.
Thường có thể có một số lớp trong một cấu trúc phân cấp để chia sẻ cùng một vptr
. Để điều này xảy ra, cần phải bù đắp chúng trong lớp cuối cùng là và danh sách các mục nhập vtable trong lớp cơ sở là một chuỗi ban đầu danh sách các mục nhập vtable trong lớp dẫn xuất .
Cả hai điều kiện được đáp ứng trong hầu như tất cả các triển khai cho đơn thừa kế .Bất kể thừa kế sâu bao nhiêu, thường chỉ có chỉ một số vptr
, được chia sẻ giữa tất cả các lớp.
Trong trường hợp của đa kế thừa, sẽ luôn có ít nhất một lớp mà các yêu cầu này không được đáp ứng, kể từ khi hai cơ sở lớp không thể có một địa chỉ bắt đầu phổ biến, và trừ khi họ có chính xác các chức năng ảo giống nhau, chỉ một vtable của một người có thể có thể là một chuỗi ban đầu của một chuỗi khác là .
Thừa kế ảo thêm một dấu nháy khác, vì vị trí của cơ sở ảo liên quan đến lớp kế thừa từ nó sẽ thay đổi tùy thuộc vào phần còn lại của cấu trúc phân cấp. Hầu hết các triển khai tôi đã thấy sử dụng một con trỏ riêng biệt cho điều này, mặc dù nó có thể được đặt thông tin này trong vtable là tốt.
Nếu chúng ta lấy hệ thống cấp bậc của bạn, thêm chức năng ảo để chúng ta có nhất định của việc có một vptr
, chúng tôi nhận thấy rằng B
và D
vẫn có thể chia sẻ một vtable
, nhưng cả hai A
và C
cần riêng vtables
. Điều này có nghĩa là nếu các lớp học của bạn có chức năng ảo, bạn sẽ cần ít nhất ba vptr
. (Từ đây tôi kết luận rằng việc triển khai của bạn đang sử dụng con trỏ riêng biệt tới cơ sở ảo. Với B
và D
chia sẻ số cùng một con trỏ và C
với con trỏ riêng của nó Và tất nhiên, A
không không cần một con trỏ đến chính nó.)
Nếu bạn đang cố gắng để phân tích chính xác những gì đang xảy ra, tôi muốn đề nghị thêm một hàm ảo mới trong mỗi lớp, và thêm một con trỏ có kích thước loại không thể thiếu đó ban đầu bạn có một giá trị đã biết khác nhau cho mỗi lớp . (Sử dụng các hàm tạo để thiết lập giá trị.) Sau đó tạo một cá thể của lớp , lấy địa chỉ của nó, sau đó xuất địa chỉ cho mỗi lớp cơ sở . Và sau đó đổ lớp: các giá trị cố định đã biết sẽ giúp đỡ trong xác định vị trí của các phần tử khác nhau. Một cái gì đó như:
struct VB
{
int vb;
VB() : vb(1) {}
virtual ~VB() {}
virtual void fvb() {}
};
struct Left : virtual VB
{
int left;
Left() : left(2) {}
virtual ~Left() {}
virtual void fvb() {}
virtual void fleft() {}
};
struct Right : virtual VB
{
int right;
Right() : right(3) {}
virtual ~Right() {}
virtual void fvb() {}
virtual void fright() {}
};
struct Derived : Left, Right
{
int derived;
Derived() : derived(5) {}
virtual ~Derived() {}
virtual void fvb() {}
virtual void fleft() {}
virtual void fright() {}
virtual void fderived() {}
};
Bạn có thể muốn thêm một Derived2
, mà xuất phát từ Derived
và xem gì xảy ra với các địa chỉ tương đối giữa ví dụ Left
và VB
tùy thuộc vào việc đối tượng có loại Derived
hoặc Derived2
.
Bạn cần phải nói gì biên dịch và kiến trúc bạn đang ở trên. –
Không có gì trong các cuộc đàm phán tiêu chuẩn về con trỏ ảo mà họ không thực sự tồn tại. Vì vậy, kích thước là 8 bởi vì trình biên dịch cần nó được 8. Đó là một chi tiết thực hiện và có rất ít điểm suy đoán về nó vì nó có thể khác nhau trên trình biên dịch khác. –