多重繼承:
就是一個派生類多個基類,幾乎與單繼承是一致的。
唯一考點:
菱形繼承結構(B類C類繼承A類,D類繼承A類。),產生問題,派生類有多份基類的數據。 解決辦法:B C都採用虛繼承,只有一個虛繼承也無法解決該問題。
虛繼承:
厲害了,virtual不僅可以聲明虛函數,還可以設定繼承方式:虛繼承
被採用虛繼承的基類 A,稱爲虛基類
此時派生類的大小擴大:會有虛基類指針B,C都會產生一個虛基類指針 vbptr(virtual base ptr)。並且如果基類有虛函數,所以派生類還會有虛函數指針vfptr。
重點:
正常繼承,派生類對象的內存先放: 基類對象,再放自己的成員變量。 並且這個是派生類包含着基類內容
而虛繼承,派生類對象的內存先放 : 一個虛基類指針vbptr、和自己的成員變量,之後放基類對象。 這兩個卻是並列存放的。所以A的am就是D自己的了,需要D自己構造
將B,C相同繼承下來的A,放到最後面,在原來的地方會留下虛基類指針。b一個 c一個,還有個虛基類表(第一行 存放的是虛基類指針自己的偏移地址,一般是0. 第二行存放的是,虛基類指針到A成員變量的偏移地址),存放的是A成員變量的偏移量,用這個指針可以找到A的成員變量。
舉個栗子:
class A
{
public:
A(int data) :ma(data){ cout << "A()" << endl; }
~A(){ cout << "~A()" << endl; }
protected:
int ma;
};
////////////////////////////////////////////////////
class B : virtual public A 就在這!!!!!!!!!!!
{
public:
B(int data) :A(data), mb(data){ cout << "B()" << endl; }
~B(){ cout << "~B()" << endl; }
protected:
int mb;
};
class C : virtual public A 就在這!!!!!!!!!!!
{
public:
C(int data) :A(data), mc(data){ cout << "C()" << endl; }
~C(){ cout << "~C()" << endl; }
protected:
int mc;
};
////////////////////////////////////////////////////
class D : public B, public C
{
public:
D(int data) :md(data),A(data), B(data), C(data)
{
cout << "D()" << endl;
}
~D(){ cout << "~D()" << endl; }
void show(){ cout << ma << endl; }
protected:
int md;
};
int main()
{
cout << sizeof(D) << endl;
D d(10);
d.show();
int *p = (int*)&d; 將B的vbptr地址給了p
int *q = (int*)*p; 將vbptr解引用也就是B的vbtable。
q += 1; +1後就指向了偏移量
int offset = *q; 記錄姐引用後的偏移量
char *p1 = (char*)&d;換成char纔可以
p1 += offset; 加上偏移量,之後p1就指向ma
*(int*)p1 = 30; 更改ma的值
d.show();
return 0;
}
附一篇博客從內存佈局看虛基類,挺詳細的:https://blog.csdn.net/xiejingfa/article/details/48028491