2008-12-28 24 views
12

Các mã sau in 20, tức là sizeof (z) là 20.Câu hỏi về đa kế thừa, lớp cơ sở ảo, và kích thước đối tượng trong C++

#include <iostream.h> 
class Base 
{ 
     public: 
      int a; 
}; 

class X:virtual public Base 
{ 
     public: 
      int x; 
}; 

class Y:virtual public Base 
{ 
     public: 
      int y; 
}; 

class Z:public X,public Y 
{ 
}; 

int main() 
{ 
Z z; 
cout << sizeof(z) <<endl; 
} 

Trong khi đó nếu tôi không sử dụng các lớp cơ sở ảo ở đây, tức là cho đoạn mã sau: sizeof (z) là 16.

#include <iostream.h> 
class Base 
{ 
     public: 
      int a; 
}; 

class X:public Base 
{ 
     public: 
      int x; 
}; 

class Y:public Base 
{ 
     public: 
      int y; 
}; 

class Z:public X,public Y 
{ 
}; 

int main() 
{ 
Z z; 
cout << sizeof(z) <<endl; 
} 

tại sao là sizeof (z) hơn (20) trong trường hợp đầu tiên? Không phải là 12, vì Base sẽ được bao gồm chỉ một lần trong Z?

Trả lời

20

Hãy xem bố cục lớp của hai trường hợp.

Nếu không có ảo, bạn có hai lớp cơ sở ("X" và "Y") với mỗi số nguyên và mỗi lớp đó được tích hợp vào lớp cơ sở "Cơ sở" cũng có số nguyên. Đó là 4 số nguyên, mỗi bit 32 bit, tổng cộng 16 byte của bạn.

Offset Size Type Scope Name 
    0  4 int Base  a 
    4  4 int  X  x 
    8  4 int Base  a 
    12  4 int  Y  y 
    16 size (Z members would come at the end) 

(Edit:. Tôi đã viết một chương trình trong DJGPP để có được cách bố trí và tinh chỉnh bảng để giải thích cho nó)

Bây giờ hãy nói về các lớp cơ sở ảo: họ thay thế các ví dụ thực tế của lớp với một con trỏ tới một cá thể được chia sẻ. Lớp "Z" của bạn chỉ có một lớp "Base" và cả hai trường hợp "X" và "Y" đều trỏ vào lớp đó. Do đó, bạn có số nguyên trong X, Y và Z, nhưng bạn chỉ có số nguyên Z. Điều đó có nghĩa là bạn có ba số nguyên hoặc 12 byte. Nhưng X và Y cũng có một con trỏ để chia sẻ Z (nếu không họ sẽ không biết nơi để tìm thấy nó). Trên máy 32 bit, hai con trỏ sẽ bổ sung thêm 8 byte. Điều này tổng cộng 20 mà bạn nhìn thấy. Bố trí bộ nhớ có thể trông giống như thế này (Tôi chưa xác minh nó ... ARM có ví dụ về thứ tự là X, Y, Z, rồi Cơ sở):

Offset Size  Type Scope Name Value (sort of) 
    0  4 Base offset  X  ? 16 (or ptr to vtable) 
    4  4   int  X  x 
    8  4 Base offset  Y  ? 16 (or ptr to vtable) 
    12  4   int  Y  y 
    16  4   int Base  a 
    20 size (Z members would come before the Base) 

Vì vậy, sự khác biệt bộ nhớ là kết hợp hai thứ: một số nguyên ít hơn và hai con trỏ khác. Trái với câu trả lời khác, tôi không tin vtables trả bất kỳ (chỉnh sửa) trực tiếp (/ chỉnh sửa) cuộn trong này, vì không có chức năng ảo.

Chỉnh sửa: ppinsider đã cung cấp thêm thông tin về trường hợp gcc, trong đó ông chứng minh rằng gcc triển khai con trỏ đến lớp cơ sở ảo bằng cách sử dụng vtable trống khác (tức là, không có hàm ảo). Bằng cách đó, nếu có các hàm ảo, nó sẽ không yêu cầu một con trỏ bổ sung trong cá thể lớp, đòi hỏi nhiều bộ nhớ hơn. Tôi nghi ngờ nhược điểm là một hướng dẫn bổ sung để đến lớp cơ sở.

Chúng tôi có thể mong đợi tất cả các trình biên dịch để thực hiện việc này, nhưng có lẽ không. Trang ARM thảo luận về các lớp cơ sở ảo mà không đề cập đến vtables. Cụ thể là địa chỉ các lớp cơ sở ảo với các hàm ảo và có sơ đồ chỉ ra một bố cục bộ nhớ, nơi có các con trỏ từ các phần X và Y tách biệt với các con trỏ đến vtable. Tôi khuyên mọi người không nên cho rằng con trỏ tới Base sẽ được thực hiện dưới dạng bảng.

+0

Đánh dấu, bạn tạo bố cục đó như thế nào? –

+0

Nếu bạn có nghĩa là định dạng bảng, nó chỉ là mã. Tôi chỉ đoán được nội dung. Tôi sẽ chỉnh sửa câu trả lời của mình với nhiều chi tiết hơn và tham khảo câu trả lời của ppinsider. – markets

+1

tôi rất thích đọc nó. cảm ơn +1 –

9

Câu trả lời của Mark Santesson là khá nhiều về số tiền, nhưng khẳng định rằng không có vtables nào là không chính xác. Bạn có thể sử dụng g ++ -fdump-class-hierarchy để hiển thị những gì đang diễn ra.Đây là trường hợp không có ảo:

Class Base 
    size=4 align=4 
    base size=4 base align=4 
Base (0x19a8400) 0 

Class X 
    size=8 align=4 
    base size=8 base align=4 
X (0x19a8440) 0 
    Base (0x19a8480) 0 

Class Y 
    size=8 align=4 
    base size=8 base align=4 
Y (0x19a84c0) 0 
    Base (0x19a8500) 0 

Class Z 
    size=16 align=4 
    base size=16 base align=4 
Z (0x19b1800) 0 
    X (0x19a8540) 0 
    Base (0x19a8580) 0 
    Y (0x19a85c0) 8 
    Base (0x19a8600) 8 

Đặc biệt chú ý đến đối số "kích thước cơ sở". Bây giờ trường hợp virtuals, và chỉ hiển thị Z:

Class Z 
    size=20 align=4 
    base size=16 base align=4 
Z (0x19b3000) 0 
    vptridx=0u vptr=((& Z::_ZTV1Z) + 12u) 
    X (0x19a8840) 0 
     primary-for Z (0x19b3000) 
     subvttidx=4u 
    Base (0x19a8880) 16 virtual 
     vbaseoffset=-0x0000000000000000c 
    Y (0x19a88c0) 8 
     subvttidx=8u vptridx=12u vptr=((& Z::_ZTV1Z) + 24u) 
    Base (0x19a8880) alternative-path 

Lưu ý các "cơ sở kích thước" là như nhau, nhưng "kích thước" là một con trỏ hơn, và lưu ý rằng bây giờ có một con trỏ vtable! Điều này lần lượt chứa các vtables xây dựng cho các lớp phụ huynh, và tất cả sự kỳ diệu liên lớp (vtables xây dựng, và bảng bảng ảo (VTT)), như mô tả ở đây:

http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

Lưu ý rằng chức năng thực tế công văn vtable sẽ trống.

+0

nhưng nếu không có lớp nào có chức năng ảo thì tại sao lại có vtable? – wilhelmtell

+0

Chạy mã thông qua g ++ với tùy chọn đó và bạn sẽ thấy lý do tại sao. Nhìn vào liên kết tôi đã hiểu để hiểu tất cả; tốt hơn nhiều so với tôi cố gắng băm lại nó ở đây! – user23167

+0

"base" có nghĩa là gì trong "base size = 16 base align = 4"? Nó có nghĩa là tất cả các lớp cơ sở? – ibread

Các vấn đề liên quan