1. 程式人生 > >C++虛繼承實現原理

C++虛繼承實現原理

首先給出以下繼承關係,以便描述虛繼承原理:

class AAA
{
public:
	int age;
};

class BBB:virtual public AAA//變為虛繼承,A變為虛基類
{
};

class CCC:virtual public AAA
{
};

class DDD:public BBB, public CCC
{
};

通過vs中的開發人員命令提示視窗得到類的記憶體佈局:class DDD中左邊的數字代表偏移量,vbtable(virtual base table)是虛基類表,類中儲存的父類指標指向了虛基類表中的表頭(指標),而對這個表頭+1進行定址操作即可得到虛基類表中所記錄的偏移量。

一.以下程式碼為通過BBB的虛基類指標得到此指標位置相對於成員變數age的偏移量,並根據偏移量計算出對應成員變數age的值。

DDD d;
d.age = 10;
cout << *((int *)*(int **)&d + 1) << endl;//8
cout << ((AAA *)((char *)&d + *((int *)*(int **)&d + 1)))->age << endl;//10
cout << *(int *)((char *)&d + *((int *)*(int **)&d + 1)) << endl;//10
  1. 目的:的到虛基類表中所記錄的vbptr指標相對於父類成員(age)的偏移量。
  2. 先取得最低派生類的地址,強轉為int**型別,即只取四個位元組的地址,得到的便是vbptr指標的地址。
  3. 再對這個指標定址*,得到的便是 在Sheep虛基類表中指向表頭的一級指標,此時將這個指標強轉為 int*型別再加+1,使其指向虛基類表中記錄的偏移量,再對這個指標進行定址,得到的就是偏移量的值了。
  4. 得到偏移量後,使用BBB的vbptr指標的起始位置加上這個偏移量,然後強轉為AAA*型別,然後再次進行指向操作,便可以得到age的值了。
  5. 當然還可以對找到的成員變數age的地址直接解引用得到age的值。

需要注意的是:這裡只能將地址轉為AAA*型別做指向操作,BBB、CCC、DDD均不可以,因為記憶體結構都不相同。只有AAA是在首地址位置就就代表了成員變數age,而BBB、CCC則是儲存的vbptr,DDD儲存的是兩個vbptr指標與一個age變數(當然這個變數也是它們的頂級父類AAA的)。

二.通過CCC的虛基類指標得到此指標位置相對於成員變數age的偏移量,並根據偏移量計算出對應成員變數age的值。

cout << *((int *)*(int **)((int **)&d + 1) + 1) << endl;//4
cout << (  (AAA *) ((char *)((int *)&d + 1) + *((int *)*(int **)((int **)&d + 1) + 1))  )->age << endl;//10
cout << *(int *)( ((char *)((int *)&d + 1) + *((int *)*(int **)((int **)&d + 1) + 1)) )<< endl;
  1. 目的:通過Tuo的虛基類指標得到此指標對於age的偏移量。
  2. 還是&d,然後再轉為int**型別,此時要先+1得到Tuo的vbptr指標,之後再轉為int**型別進行定址,得到Tuo虛基類表中指向表頭的一級指標,之後再轉為int型別,再次定址,得到偏移量4。
  3. 強轉為AAA*型別指向age。
  4. 將地址轉為int*型別解引用。