2012-06-15 24 views
7

Tôi biết bố cục bộ nhớ của đa kế thừa không được xác định, vì vậy tôi không nên dựa vào nó. Tuy nhiên, tôi có thể dựa vào nó trong một trường hợp đặc biệt không. Nghĩa là, một lớp chỉ có một lớp siêu "thực". Tất cả những người khác là "các lớp trống", tức là các lớp không có trường hoặc phương thức ảo (nghĩa là chúng chỉ có các phương thức không phải ảo). Trong trường hợp này, các lớp bổ sung này không nên thêm bất kỳ thứ gì vào bố cục bộ nhớ của lớp. (Nói ngắn gọn hơn, trong từ ngữ C++ 11, lớp học có bố cục tiêu chuẩn)C++ Nhiều bố trí bộ nhớ thừa kế với "Lớp trống"

Tôi có thể phỏng đoán rằng tất cả các lớp học sẽ không có bù trừ? Ví dụ .:

#include <iostream> 

class X{ 

    int a; 
    int b; 
}; 

class I{}; 

class J{}; 

class Y : public I, public X, public J{}; 

int main(){ 

    Y* y = new Y(); 
    X* x = y; 
    I* i = y; 
    J* j = y; 

    std::cout << sizeof(Y) << std::endl 
        << y << std::endl 
        << x << std::endl 
        << i << std::endl 
        << j << std::endl; 
} 

Ở đây, Y là lớp với X là lớp cơ sở thực mà thôi. Đầu ra của chương trình (khi biên soạn trên linux với g ++ 4.6) như sau:

0x233f010

0x233f010

0x233f010

0x233f010

Như tôi đã kết luận, không có poi điều chỉnh nter. Nhưng liệu việc triển khai này có cụ thể hay không hoặc tôi có thể dựa vào nó hay không. Tức là, nếu tôi nhận được một đối tượng thuộc loại I (và tôi chỉ biết các lớp này tồn tại), tôi có thể sử dụng một số reinterpret_cast để truyền cho X không?

Hy vọng của tôi là tôi có thể dựa vào nó vì thông số kỹ thuật cho biết kích thước của một đối tượng ít nhất phải là một byte. Do đó, trình biên dịch không thể chọn bố cục khác. Nếu nó sẽ bố trí IJ phía sau các thành viên của X, thì kích thước của chúng sẽ bằng 0 (vì chúng không có thành viên). Vì vậy, sự lựa chọn hợp lý duy nhất là sắp xếp tất cả các lớp siêu mà không có bù đắp.

Tôi có đúng hay tôi đang chơi với đám cháy nếu tôi sử dụng reinterpret_cast từ I đến X tại đây?

+3

Bố cục bộ nhớ của thừa kế, một hoặc nhiều, căn cứ trống hoặc không, không được xác định. –

+1

Tôi muốn nói rằng bạn đang chơi với lửa, bởi vì bạn đang cố gắng tạo ra một cấu trúc rất lạ chắc chắn sẽ gây đau đớn và đau khổ cho bất cứ ai phải hiểu nó trong tương lai, ngay cả khi nó không phá vỡ khi bạn nâng cấp trình biên dịch của bạn! – Rook

+0

may mắn, câu lệnh của bạn không còn đúng trong C++ 11, hãy xem câu trả lời :) – gexicide

Trả lời

8

Trong C++ 11 trình biên dịch được yêu cầu sử dụng tối ưu hóa lớp cơ sở trống cho bố cục tiêu chuẩn tiêu chuẩn loại. thấy https://stackoverflow.com/a/10789707/981959

Ví dụ cụ thể của bạn tất cả các loại đang có các lớp học bố trí tiêu chuẩn và không có lớp cơ sở chung hoặc thành viên (xem bên dưới) để bạn có thể dựa vào hành vi mà trong C++ 11 (và trong thực tế, Tôi nghĩ rằng nhiều trình biên dịch đã tuân theo quy tắc đó, chắc chắn G ++ đã làm, và những người khác theo sau Itanium C++ ABI.)

Lưu ý: đảm bảo bạn không có bất kỳ địa chỉ riêng biệt nào, ví dụ

struct I {}; 

struct J : I {}; 
struct K : I { }; 

struct X { int i; }; 

struct Y : J, K, X { }; 

#include <iostream> 

Y y; 

int main() 
{ 
    std::cout << &y << ' ' << &y.i << ' ' << (X*)&y << ' ' << (I*)(J*)&y << ' ' << (I*)(K*)&y << '\n'; 

} 

in:

0x600d60 0x600d60 0x600d60 0x600d60 0x600d61 

Đối với loại Y chỉ một trong những I căn cứ có thể được bù đắp bằng không, vì vậy mặc dù X sub-object là bù đắp bằng không (ví dụ:offsetof(Y, i) là số không) và một trong số các cơ sở I có cùng địa chỉ nhưng cơ sở I khác (ít nhất bằng G ++ và Clang ++) một byte vào đối tượng, vì vậy nếu bạn có số I*, bạn không thể reinterpret_cast đến vì bạn sẽ không biết I sub-object nó chỉ ra, các I tại offset 0 hoặc I tại offset 1.

đó là OK cho trình biên dịch để đưa thứ hai I sub-object tại offset 1 (tức là bên trong các int) vì I không có thành viên dữ liệu không tĩnh, vì vậy bạn không thể actuall y dereference hoặc truy cập bất cứ điều gì tại địa chỉ đó, chỉ nhận được một con trỏ đến đối tượng tại địa chỉ đó. Nếu bạn đã thêm thành viên dữ liệu không tĩnh vào I thì Y sẽ không còn là bố cục chuẩn và sẽ không phải sử dụng EBO và offsetof(Y, i) sẽ không còn là 0.

+0

hoàn hảo, vì vậy nó hoạt động :) – gexicide

+0

lưu ý các caveat quan trọng tôi mở rộng trên trong chỉnh sửa của tôi mặc dù –

+0

có, tất nhiên. Tôi phải đảm bảo rằng lớp học có bố cục chuẩn. Cảm ơn! – gexicide