2015-07-20 28 views
5

Vấn đề ở bàn tay khó mô tả nên mã được đặt lên phía trước để rõ ràng hơn.Nhiều sự thừa kế Cuộc gọi ảo Sự mơ hồ

struct Base 
{ 
    int b; 
    virtual void foo(){cout << b << endl;} 
    Base(int x) : b(x){} 
}; 

struct Derived1 : Base //not virtual 
{ 
    virtual void foo(){/*Derived2's code*/} 
    Derived1() : Base(1){} 
}; 

struct Derived2 : Base //not virtual 
{ 
    virtual void foo(){/*Derived2's code*/} 
    Derived2() : Base(2){} 
}; 

struct MultiInheritance : Derived1, Derived2 
{ 
    void bar1() 
    { 
     //needs to access Derived1's Base foo() 
    } 
    void bar2() 
    { 
     //needs to access Derived2's Base foo() 
    } 
}; 

Giả sử rằng trong một số kịch bản kỳ lạ lạ, tôi sẽ muốn có một lớp cơ sở MultiInheritance có hai lớp cơ sở Derived1Derived2 rằng có một chung phi ảo cơ sở lớp Base.

Có hai Base trong MultiInheritance, làm cách nào để chỉ định lớp học nào Base tôi muốn truy cập trong MultiInheritance?

Đoạn mã trên dường như hoạt động tốt bằng cách truyền nhiều lần, nhưng tôi không chắc liệu đây có phải là hành vi được xác định hay không. Nếu nó được, làm thế nào là điều này được thực hiện bởi trình biên dịch để thực hiện các nhu cầu của đa hình? Một mặt tất cả các cuộc gọi virtual tất cả các cuộc gọi sẽ kết quả trong cùng một bảng chức năng virtual, nhưng mặt khác nếu nó sẽ không xuất ra các câu trả lời khác nhau.

EDIT

tôi muốn nhấn mạnh rằng Base lớp bắt buộc phải được phi ảo

EDIT2

lời xin lỗi sâu, tôi nghiêm túc bóp méo bản thân mình. Mã trên được cập nhật tốt hơn phản ánh câu hỏi ban đầu của tôi.

+0

Mà một trong những bạn xem xét các * đúng * 'Base'? Hoặc là câu hỏi của bạn làm thế nào để làm cho nó để chỉ có một 'Base'? – Barry

+0

Phân tích của bạn là chính xác và trình biên dịch sẽ xây dựng một vtable chính xác phản ánh sự tồn tại của hai lớp cơ sở. Để tránh điều đó, bạn cần thừa kế ảo và cấu trúc vtable sẽ trở nên phức tạp hơn. –

+1

Không có sự mơ hồ trong ví dụ của bạn, nó hoạt động chính xác như nó nên bởi cuốn sách. "Bảng ảo" là chi tiết triển khai. Không ai nói bất cứ nơi nào mà mỗi thực hiện phải có một vtbl cho mỗi lớp, hoặc rằng phải có một mục nhập cho mỗi chữ ký hàm trong mỗi vtable. Trong thực tế, rất có thể ít nhất một trong những điều trên là không chính xác cho bất kỳ triển khai nào sử dụng vtables. –

Trả lời

0

Dưới đây là ví dụ minh họa hơn.

#include <iostream> 
using namespace std; 

template <typename Res, typename Arg> 
Res& as(Arg& arg) 
{ 
    return arg; 
} 

struct Base 
{ 
    virtual void foo() = 0; 
}; 

struct Derived1 : Base {}; 
struct Derived2 : Base {}; 

struct MoreDerived1 : Derived1 
{ 
    void foo() { cout << "Derived1\n"; } 
}; 

struct MoreDerived2 : Derived2 
{ 
    void foo() { cout << "Derived2\n"; } 
}; 

struct MultiInheritance : MoreDerived1, MoreDerived2 
{ 
    void bar1() { as<Derived1>(*this).foo(); } 
    void bar2() { as<Derived2>(*this).foo(); } 
}; 

int main() 
{ 
    MultiInheritance m; 
    m.bar1(); 
    m.bar2(); 
} 

Ví dụ này minh họa rằng:

  1. Bạn không cần phải xác định các cơ sở bạn cần một cách rõ ràng bằng một con đường thừa kế đầy đủ, nó là đủ để đi xuống một subobject rằng có cơ sở rõ ràng subobject
  2. Cơ chế chức năng ảo hoạt động ở đây. Nó sẽ không hoạt động nếu bạn cố gắng gọi Derived1::foo().

Chức năng as helper chỉ là một đường cú pháp, bạn có thể chỉ cần cũng nói

Derived1& d = *this; 
d.foo(); 
2

Điều này được gọi là vấn đề kim cương.

http://www.cprogramming.com/tutorial/virtual_inheritance.html

Nếu bạn muốn giữ lại căn cứ phi ảo và nhận được hành vi mà bạn đang tìm kiếm bây giờ, bạn có thể làm điều này trong MultipleInheritance theo cách sau để đảm bảo bạn đang gọi foo() chức năng từ lớp cơ sở đúng

struct MultiInheritance : Derived1, Derived2 
{ 
    void bar1() 
    { 
     Derived1::foo(); 
    } 
    void bar2() 
    { 
     Derived2::foo(); 
    } 
}; 
0

có hai cơ sở trong MultiInheritance, làm thế nào để xác định các cơ sở lớp tôi muốn truy cập trong MultiInheritance?

Bạn có một sự mơ hồ trong đó cơ sở đối tượng bạn đang gọi trong

void MultiInheritance::bar1(){ 
    foo(); 
} 

Cách giải quyết này là bằng cách nói với trình biên dịch nơi để tìm kiếm foo.

void MultiInheritance::bar1(){ 
    Derived1::foo(); // The same foo() as in your question. 
} 

Đây là những gì được thực hiện bằng cách này bạn

void MultiInheritance::bar1() 
{ 
    Derived1& d = *this; 

được mô tả trong tiêu chuẩn bởi §10.2.12. Điều này được xác định rõ. Vì chuỗi của bạn là

void MultiInheritance::bar1() 
{ 
    Derived1& d = *this; 
    Base& b = *this; 

bởi cùng một đoạn.

Đáng tiếc là các nhà điều hành có độ phân giải phạm vi không thể giúp bạn có được để nhảy từ MultiInhteritance trực tiếp đến cơ sở như

MultiInheritance::foo(){ 
    Derived1::Base::foo(); 

được mô tả một lớp lồng nhauBase.

Để đến foo() thuộc về Cơ sở, bạn sử dụng cú pháp độ phân giải phạm vi trong MultiInheritance, cũng như trong Derived1 và Derived2.

Derived1()::foo(){ 
    Base::foo; 

Nếu điều này không phù hợp thì tùy chọn bạn đề xuất là tùy chọn còn lại.

Nếu có, trình biên dịch này được thực hiện như thế nào để đáp ứng nhu cầu của đa hình? Trên một cuộc gọi ảo một tay tất cả sẽ dẫn đến cùng một bảng chức năng ảo, nhưng mặt khác nếu nó sẽ không xuất các câu trả lời khác nhau.

Thực hiện trình biên dịch khác nhau theo trình biên dịch và như người nhận xét đã nêu: Sử dụng vtables cho chức năng ảo là chi tiết triển khai. Và nếu triển khai sử dụng vtable cho các chức năng ảo, thì việc triển khai cần phải tính đến kịch bản này.

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