2010-02-12 29 views
6

Tôi có một lớp đơn giản trong C++ có một số nguyên và một vtable:MSVC Object Layout Quirk

class Something { 

    virtual void sampleVirtualMethod(); 

    int someInteger; 
}; 

Nếu bạn nhìn vào cách bố trí đối tượng cho MSVC (sử dụng/d1reportSingleClassLayout) bạn nhận được:

class Something  size(8): 
     +--- 
0  | {vfptr} 
4  | someInteger 
     +--- 

Điều này có ý nghĩa hoàn toàn. 4 byte cho con trỏ vtable và 4 byte cho số nguyên. Điều lạ là khi tôi thêm một đôi đến lớp:

class Something {  
    virtual void sampleVirtualMethod(); 
    int someInteger; 
    **double someDouble;** 
}; 

tôi nhận bố trí này đối tượng:

class Something  size(24): 
     +--- 
0  | {vfptr} 
8  | someInteger 
     | <alignment member> (size=4) 
16  | someDouble 
     +--- 

Tại sao chênh lệch giữa 0 và bù đắp someInteger 8 thay vì 4? Vtable có tăng lên 8 byte không? Không có vấn đề thứ tự khi tôi thêm một đôi, điều này xảy ra.

Cảm ơn.

+0

Tôi không thể không tự hỏi tại sao bạn lại quan tâm đến điều này. Điều này có gây ra sự cố trực tiếp cho những gì bạn đang làm việc không? –

+3

Tại sao * không * quan tâm đến nó? Thật ngạc nhiên với OP (và với tôi). Đâu là tác hại trong việc cố gắng học những điều mới? ;) – jalf

+0

Tôi đang cố gắng hợp nhất các mô hình đối tượng giữa các tệp nhị phân và tệp nhị phân của studio trực quan. Nó thực sự là một vấn đề :). Điều kỳ lạ là cả hai GCC và Visual Studio cả hai lay ra các đối tượng như thế này trong khi llvm thì không. – Mason

Trả lời

0

Tôi không thể trả lời câu hỏi của bạn trực tiếp vì không có lý do chính đáng cho hành vi của trình biên dịch. Thay vào đó, tôi sẽ đắm mình trong suy đoán hoang dã vì chưa có câu trả lời.

tôi nghi ngờ một lỗi trong các thuật toán sắp xếp mà đi một cái gì đó như thế này:

  • sự liên kết của một cấu trúc (ví dụ, vị trí của các thành viên đầu tiên) nên có ít nhất rộng như sự liên kết của thành viên rộng nhất
  • oops, quên đếm con trỏ bảng chức năng ảo như một thành viên

Nếu lỗi này tồn tại tôi nghi ngờ nó được còn sót lại từ những ngày đầu của trình biên dịch như một trình biên dịch C 4-byte liên kết. Bây giờ, mặc định cho trình biên dịch là /Zp8 có nghĩa là mọi cấu trúc đều được căn chỉnh ít nhất là 8 byte, vì vậy sẽ không cần sửa chữa căn chỉnh của thành viên "đầu tiên" trong trường hợp này.

Kính trọng, Sherm

1

Tôi nghi ngờ rằng this answer có gì để làm với nó. Trích dẫn từ câu trả lời dirkgently 's, trích dẫn tài liệu GCC:

Lưu ý rằng sự liên kết của bất kỳ struct hoặc công đoàn loại nhất định là yêu cầu của tiêu chuẩn ISO C để có ít nhất một bội số hoàn hảo của các bội số chung nhỏ nhất của sự sắp xếp của tất cả các thành viên của struct hoặc union trong câu hỏi.

Theo quy tắc đó, khi bạn đã thêm gấp đôi 8 byte, trình biên dịch phải sắp xếp mọi thứ trên bội số 8 byte. Bạn có thể ghi đè lên rằng với gói #pragma(), tất nhiên, mặc dù nó sẽ kém hiệu quả hơn nếu bạn kết thúc với một thành viên 8 byte trên một cái gì đó khác với ranh giới 8 byte.

+1

Không, không. Điều duy nhất mà trích dẫn nói là vì 'double' member * toàn bộ struct * nên được căn chỉnh ở ranh giới 8 byte. Nhưng nó không giải thích tại sao sự liên kết của các thành viên số nguyên thay đổi. Cấu trúc này phải phù hợp với 16 byte với tất cả các yêu cầu liên kết được đáp ứng một cách hoàn hảo. Nhưng trình biên dịch đã tạo ra 24. Tại sao? – AnT

+0

Thật thú vị, trang này của ví dụ liên kết cho mã x64 trong VC++ 8 cho thấy cùng một điều với một cấu trúc có chứa một int và một đôi, nhưng tiếc là không nói lý do tại sao. http://msdn.microsoft.com/en-us/library/71kf49f1(VS.80).aspx Rõ ràng, trình biên dịch đang chọn căn chỉnh các thành viên trên mẫu số chung thấp nhất, là 8, do đó, sự cần thiết cho sự liên kết. Có lẽ là nơi để giải thích trong câu đó. – aalpern

+0

aaplem - liên kết đó hiển thị cùng một thứ với int và double. Đó có thể là vì bạn muốn tăng gấp đôi để được 8 byte liên kết. Tôi luôn nghĩ rằng có 4 byte liên kết đôi là siêu chậm trên x86. – Mason

2

This blog post thảo luận cùng một vấn đề và bao gồm lời giải thích trong nhận xét của Jan Gray, người đã viết mã trình bày trình biên dịch MS C++ một thời gian dài trước đây.

Để diễn giải, vfptr được chèn vào bố cục lớp chỉ sau khi các thành viên dữ liệu khác được đặt.Tùy thuộc vào yêu cầu liên kết của các thành viên dữ liệu, điều này có nghĩa là không cần thiết có thể đưa vào đệm. Điều này cũng chỉ xảy ra nếu lớp không có lớp cơ sở với vfptr.

Do đó khắc phục, được trình bày trong các bài viết trên blog:

class EmptyBase 
{ 
protected: 
    virtual ~EmptyBase() {} 
}; 

class Something : public EmptyBase {  
    virtual void sampleVirtualMethod(); 
    int someInteger; 
    **double someDouble;** 
}; 

sizeof(Something) nên 16 trong trường hợp này.