19

Thừa kế đơn lẻ dễ thực hiện. Ví dụ: trong C, thừa kế có thể được mô phỏng là:Sự đa thừa kế của C++ được triển khai như thế nào?

struct Base { int a; } 
struct Descendant { Base parent; int b; } 

Nhưng với nhiều kế thừa, trình biên dịch phải sắp xếp nhiều cha mẹ trong lớp mới xây dựng. Làm thế nào là nó được thực hiện?

Vấn đề tôi thấy phát sinh là: liệu bố mẹ có được sắp xếp theo AB hoặc BA hay thậm chí là cách khác không? Và sau đó, nếu tôi thực hiện một dàn diễn viên:

SecondBase * base = (SecondBase *) &object_with_base1_and_base2_parents; 

Trình biên dịch phải xem xét có thay đổi hay không con trỏ ban đầu. Những điều phức tạp tương tự được yêu cầu với ảo.

+0

http://en.wikipedia.org/wiki/Diamond_problem – Dario

+0

Mô phỏng C C sẽ quên con trỏ VTable (chi tiết triển khai). –

+1

@Dario: Bài viết này đề cập đến các vấn đề quá tải trong đa thừa kế nhưng không chứa bất cứ điều gì về bố cục đối tượng và truyền các đối tượng trong C++. – mmmmmmmm

Trả lời

10

Các giấy tờ sau đây từ các tác giả của C++ mô tả một thực thể đa kế thừa:

Multiple Inheritance for C++ - Bjarne Stroustrup

+3

Liên kết thay thế: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.23.4735&rep=rep1&type=pdf –

+0

Sau đọc cả hai, tùy thuộc vào những gì bạn đang sau. Điều này (của _Stroustrup_) là khá chi tiết và bao gồm các lý thuyết. Một trong những đề xuất bởi _Nemanja_ dưới đây (_MSDN_ của một) là đơn giản và bao gồm một thực hiện thực tế. –

+0

Cả hai liên kết đều đã chết, nhưng https://www.usenix.org/publications/compsystems/1989/fall_stroustrup.pdf có hiệu quả đối với tôi. –

5

this pretty old MSDN article về cách triển khai trong VC++.

+1

Trong khi liên kết này có thể trả lời câu hỏi, tốt hơn nên bao gồm các phần thiết yếu của câu trả lời ở đây và cung cấp liên kết để tham khảo. Câu trả lời chỉ liên kết có thể trở thành không hợp lệ nếu trang được liên kết thay đổi. - [Từ đánh giá] (/ đánh giá/bài đăng chất lượng thấp/18125623) – CDspace

0

Hoàn toàn của nó xuống trình biên dịch như thế nào nó được thực hiện, nhưng tôi tin rằng nó thường được thực hiện thông qua cấu trúc heirarchical của vtables.

1

Cha mẹ được sắp xếp theo thứ tự mà họ đang định:

class Derived : A, B {} // A comes first, then B 

class Derived : B, A {} // B comes first, then A 

Trường hợp thứ hai của bạn được xử lý theo cách thức cụ thể của trình biên dịch. Một phương pháp phổ biến là sử dụng con trỏ lớn hơn kích thước con trỏ của nền tảng, để lưu trữ dữ liệu bổ sung.

+0

Điều này có thể phổ biến, nhưng tôi không nghĩ rằng nó là bắt buộc. –

+0

Tôi đã thấy nó theo cách khác. Tất cả phụ thuộc vào việc thực hiện. –

+0

Thứ tự chỉ có ảnh hưởng đến thứ tự các lời gọi hàm khởi tạo. Nhưng bố cục không được chỉ định. Tôi đã thử nghiệm một thời gian trước đây, và GCC đặt căn cứ trống đầu tiên trong bộ nhớ, để tận dụng tối ưu hóa lớp cơ sở trống. –

1

Đây là một vấn đề thú vị mà thực sự không phải là C++ cụ thể. Mọi thứ trở nên phức tạp hơn khi bạn có một ngôn ngữ với nhiều công văn cũng như nhiều thừa kế (ví dụ: CLOS).

Mọi người đã lưu ý rằng có nhiều cách khác nhau để tiếp cận vấn đề. Bạn có thể tìm đọc một chút thông tin về Meta-Object giao thức (Giẻ lau sàn) hấp dẫn trong bối cảnh này ...

+1

Tôi nghĩ rằng việc triển khai dễ dàng hơn nhiều nếu ngôn ngữ không hỗ trợ các con trỏ "thô" để tham chiếu các đối tượng và không có khả năng tương thích ngược POD. Bởi vì nếu họ thực hiện việc đưa con trỏ đến các đối tượng thì rất phức tạp. Một ngôn ngữ có thể thêm nhiều thông tin meta vào lớp tham chiếu và thể hiện lớp như nó muốn. Trong C++ điều này không thể được thực hiện rất dễ dàng. – mmmmmmmm

5

Và sau đó, nếu tôi làm một dàn diễn viên:

SecondBase base = (SecondBase *) object_with_base1_and_base2_parents; 

Trình biên dịch phải xem xét liệu có nên thay đổi hoặc không phải là con trỏ ban đầu. Những điều phức tạp tương tự với ảo.

Với sự thừa kế không phải là virut thì điều này ít phức tạp hơn bạn nghĩ - tại thời điểm diễn viên được biên dịch, trình biên dịch biết bố cục chính xác của lớp dẫn xuất (sau khi tất cả, trình biên dịch đã làm bố cục). Thông thường tất cả những gì xảy ra là một bù đắp cố định (có thể bằng không cho một trong các lớp cơ sở) được cộng/trừ từ con trỏ lớp dẫn xuất.

Với sự thừa kế virut có thể phức tạp hơn một chút - nó có thể liên quan đến việc lấy một khoản bù đắp từ một vtbl (hoặc tương tự).

Sách của Stan Lippman, "Inside the C++ Object Model" có mô tả rất tốt về cách thức hoạt động của công cụ này (và thường thực sự).

0

tôi đã thực hiện thí nghiệm đơn giản:

class BaseA { int a; }; 
class BaseB { int b; }; 
class Descendant : public BaseA, BaseB {}; 
int main() { 
     Descendant d; 
     BaseB * b = (BaseB*) &d; 
     Descendant *d2 = (Descendant *) b; 
     printf("Descendant: %p, casted BaseB: %p, casted back Descendant: %p\n", &d, b, d2); 
} 

Output là:

Descendant: 0xbfc0e3e0, casted BaseB: 0xbfc0e3e4, casted back Descendant: 0xbfc0e3e0 

Đó là tốt để nhận ra rằng đúc tĩnh không có nghĩa là "thay đổi kiểu mà không cần chạm vào nội dung". (Vâng, khi các kiểu dữ liệu không phù hợp với nhau, thì cũng sẽ có sự can thiệp vào nội dung, nhưng đó là tình huống khác nhau IMO).