1. 程式人生 > >【c++】深入剖析虛擬繼承與各種繼承關係中派生類內成員記憶體分佈情況及虛基類表的內容

【c++】深入剖析虛擬繼承與各種繼承關係中派生類內成員記憶體分佈情況及虛基類表的內容

概要

本文講述在VS2012環境下,採用程式碼和圖結合的方法,分析C++程式碼中不同繼承方式的物件模型,以及從彙編角度分析虛擬繼承編譯器生成的虛基類表裡的內容,不涉及虛擬函式。

繼承分類:

1.單繼承

一個子類只有一個直接父類

// 單繼承  工人類 繼承 人類
class Person
{
	//...
};
class Worker : public Person
{
	//...
};


2.多繼承

一個子類有兩個以上直接父類

// 多繼承 農民工類分別繼承 工人類 和 農民類
class Worker
{
	// ...
};
class Farmer
{
	// ...
};
class MigrantWorker : public Worker, public Farmer
{
	// ..
};


3.多重繼承

繼承關係大於兩層

// 多繼承 工人類繼承 Person類 農民工類繼承Worker類
class Person
{
	// ...
};
class Worker : Person
{
	// ...
};
class MigrantWorker : public Worker
{
	// ..
};


4.菱形繼承

兩個子類繼承同一個父類,而又有子類同時繼承這兩個子類。

// 菱形繼承也叫鑽石繼承 
// 工人類繼承人類, 農民繼承人類, 農民工類繼承工人類和農民類
class Person
{
	//...
};
class Worker : public Person
{
	//...
};
class Framer : public Person
{
	//...
};
class MigrantWorker : public Worker,public Framer
{
	//...
};



派生類記憶體分佈情況: 1.單繼承


2.多繼承


我們改變Worker類 和 Farmer類的繼承順序再觀察:


3.多重繼承


4.菱形繼承


在MigrantWorker的物件中有兩份Person的成員,導致菱形繼承存在二義性和資料冗餘的問題,而虛擬繼承可以很好地解決菱形繼承的二義性和資料冗餘問題。

虛擬繼承:

虛擬繼承與單繼承:

// 工人類虛繼承 Person類
class Person
{
public:
	int dataPerson;
};
class Worker : virtual public Person
{
public:
	int dataWorker;
};

int main()
{
	Worker w;
	w.dataPerson = 1;
	w.dataWorker = 2;
	return 0;
}
記憶體分佈圖情況:


我們可以看到在物件w的成員資料之前多了4個位元組,下面我們來分析一下這四個位元組是什麼.

先開啟看一看裡面存的是什麼:


裡面儲存了0和8這又是什麼呢?

首先進入派生類的建構函式:


進入建構函式內:


然後繼續執行下去直到ret返回後:


然後繼續執行:


然後下一步,mov dword ptr w[ecx],1 即在派生類物件首地址往後偏移ECX(8)個位元組然後存入1,而派生類則是利用ebp-XX 的方式棧幀賦值。至此我們已經搞清楚“表”裡的08儲存的是什麼,即:基類物件成員在派生類物件中與派生類物件首地址之間的偏移量。00到底是什麼請繼續看下面的分析。

虛擬繼承與多繼承:

(1)先繼承的類為虛基類時

記憶體分佈圖:


賦值情況:


(2)後繼承的類為虛基類時

記憶體分佈圖:


賦值情況:


(3)繼承的兩個類都為虛基類時

記憶體分佈:


賦值情況:


虛擬繼承與多重繼承:

(1)最上層類是虛基類時:

記憶體分佈圖:


對比多繼承中表的值我們可以發現,表中前四個位元組到底是什麼:派生類物件首地址與派生類中儲存的的表的指標之間的偏移量。

在這裡表的指標儲存在物件的前四個位元組,相對派生類物件地址偏移量為0,所以表中前四個位元組為0.

而在多重繼承中的前兩種情況中,表指標儲存在派生類物件向後偏移4個位元組出,也就是表的指標往前偏移4個位元組,即-4,在記憶體中-4儲存的是補碼也就是我們說看到的fc ff ff  ff。

至此我們已經弄明白了表中到底儲存的是什麼,也要給這個表(這個地址)一個名字,它叫虛基類表指標。在這裡不深究該表的來源,暫且理解為編譯器的功勞。

賦值情況:

經過前面的觀察和對比我們發現,賦值情況只有虛基類才會取偏移地址賦值,虛基類表中存放的內容也分析清楚,物件記憶體分佈也分析清楚,此後不再贅述,只放上成員記憶體分佈圖。

(2)中間層是虛基類時:

記憶體分佈圖:


菱形繼承

記憶體分佈:


分佈順序分析:


進Worker建構函式:

4

Framer建構函式:


賦值情況:


虛繼承雖然解決的菱形繼承裡子類物件包含對分父類物件的資料冗餘浪費空間的問題,單因為要給物件分配記憶體去儲存虛表,也帶來了效能上的損耗。

_________________________________________________________END______________________________________________________________________