1. 什麼是菱形繼承
基類A,B繼承A,C繼承A,D繼承BC。D中的B對象有A,D中的C對象還有A,重複了,如果調用函數,則不知道是B中的函數還是C中的函數。
#include <iostream>
using namespace std;
class A {
public:
A() { a = 1; }
~A() {}
void fun() { cout << "in class A" << endl; }
int a;
};
class B : public A {
public:
B() { b = 2; }
~B() {}
void fun() { cout << "in class B" << endl; }
int b;
};
class C : public A {
public:
C() { c = 3; }
~C() {}
void fun() { cout << "in class C" << endl; }
int c;
};
class D : public B, public C {
public:
D() { d = 4; }
~D() {}
int d;
};
int main() {
D od;
//od.fun();//會報錯,fun不明確,不知道是誰的fun
auto add_ba = &od.B::A::a;
auto add_ca = &od.C::A::a;
cout << "addr of a in B = " << add_ba << endl;
cout << "addr of a in C = " << add_ca << endl;
return 0;
}
輸出:
B中有個A對象,C中有個A對象,兩個A對象不一樣,D中就有兩個A對象。
菱形繼承存在二義性與數據冗餘問題。
使用虛繼承解決,虛繼承的做法如下。
2. 虛繼承
虛繼承的具體操作方式如下:
#include <iostream>
using namespace std;
class A {
public:
A() { a = 1; }
~A() {}
void fun() { cout << "in class A" << endl; }
int a;
};
class B : virtual public A {
public:
B() { b = 2; }
~B() {}
void fun() { cout << "in class B" << endl; }
int b;
};
class C : virtual public A {
public:
C() { c = 3; }
~C() {}
void fun() { cout << "in class C" << endl; }
int c;
};
class D : public B, public C {
public:
D() { d = 4; }
~D() {}
int d;
};
int main() {
D od;
//od.fun();
auto add_ba = &od.B::A::a;
auto add_ca = &od.C::A::a;
cout << "addr of a in B = " << add_ba << endl;
cout << "addr of a in C = " << add_ca << endl;
return 0;
}
實際中的內存關係如下:
此時D類對象od的佈局如左側所示,先是B類對象(B類對象此時只包含一個指針和b),然後是C類對象(C類對象此時只包含一個指針和c),然後是D類獨有的d,最後是A類對象中的a。
此時的結構有些奇怪:
B中的a與C中的a地址相同:
沒有了冗餘現象,具體來說是通過B類對象和C類對象的指針實現的,指針指向的區域存儲的是該類對象到基類對象的一個偏移量,如B中的指針就是存儲的B類對象到A類對象的偏移量,一個指針4個字節,偏移量爲20說明相差5個指針的距離。
sizeod(od)=24
,abcd佔16個字節,B中的指針和C中的指針一共8字節,加起來爲24.