国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

【C++】淺析C++中的對象模型

2019-11-11 03:39:11
字體:
來源:轉載
供稿:網友

以下代碼運行環境:windows8.1 32位 VS2015

(一)不含有虛函數的單一繼承模型:測試代碼:
//單一繼承,無虛函數class A{public:       A(int a = 0, char c = 0)              :_a(a)              , _c(c)       {}       int GetA()       {              return _a;       }       static int staticFun()       {              return _val;       }       static int _val;PRivate:       int _a;       char _c;};class B :public A{public:       B(char b = 0)              :_b(b)       {}       char GetB()       {              return _b;       }private:       char _b;};int A::_val = 100;void test1(){       A a(10,'a');       B b('b');}現象以及分析:關于類的靜態成員,我們需要知道以下幾點:(1)類的靜態成員是屬于類而不屬于對象,所以他不是類的單個對象所有。(2)靜態成員只存在一個,不像普通的成員,每創建一個對象,就會創建一組普通的成員。(3)靜態成員的初始化不能在類中,肯定是不能在構造函數中,只能在類外并且在main函數之前,按照這樣的格式進行初始化:int A::_val = 100。并且是先初始化再使用。(4)在靜態成員函數中不可以使用非靜態成員。因為非靜態成員是屬于對象,而類的靜態成員函數是屬于類的,在類的對象實例化之前就已經完成了初始化。如果在靜態成員函數用引用非靜態成員,就好比是使用一個并沒有完成初始化的變量。(5)類的非靜態成員函數中可以引用類的靜態成員。反之不可以。(6)靜態成員函數沒有this指針。了解了靜態成員的這些特性(當然本文主要是來分析對象模型的),我們在分析對象模型的時候,可以順便來看看靜態成員到底存儲在哪里?首先看一下上邊代碼的內存分布:在程序調試中,是這樣表現出來的:這里,我們只是看到的A類的普通數據成員繼承給子類(上述圖中,子類自稱自父類的成員默認是0,是因為我們的父類的默認構造函數給出的默認值是0),下邊我們來看一下A類的靜態成員是存儲在哪里?是否會繼承給子類?從這里我們可以看出,父類的靜態成員繼承給子類,并且兩者是存儲在一個地址上的。這里就驗證了這樣的一句話:父類中定義了靜態成員,則整個繼承體系中只有一個這樣的成員,無論派生出多少個子類。靜態成員是存儲在全局區的。(二)含有虛函數的單一繼承模型:測試代碼:
class A{public:       virtual void foo()       {              cout << "A::foo()" << endl;       }       virtual void funA()       {              cout << "A::funA()" << endl;       }private:       int _a;       char _c;};class B :public A{public:       virtual void foo()       {              cout << "B::foo()" << endl;       }       virtual void funB()       {              cout << "B::funB()" << endl;       }private:       char _b;};void test2(){       A a;       B b;}內存分布:結合程序的調試進行分析:結合程序的調試信息進行分析:下邊我們來寫一個函數來打印出我們的虛表,看看與我們分析的是否一樣~~
void PrintTable(int* vTable){       typedef void(*pFun)();       cout << "虛表地址為:" << vTable << endl;       for (int i = 0; vTable[i] != NULL; ++i)       {              printf("第%d個虛表地址為:%p->", i, vTable[i]);              pFun f = (pFun)vTable[i];              f();       }       cout << endl;}void test2(){       A a;       B b;       int* vTable1 = (int*)(*(int*)&a);       int* vTable2 = (int*)(*(int*)&b);       PrintTable(vTable1);       PrintTable(vTable2);}運行結果:【總結】:(1)一個類只要有一個虛函數,它就會被編譯器分配一個虛表指針,也就是__vfptr,用來存儲虛函數的地址;(2)子類的虛函數表是在父類的虛函數表上進行修改的,就像上邊的對象模型所示,B類的虛函數就是在A類的虛函數之后;(3)父類中的虛函數被子類改寫,也就是說,子類中含有與父類的虛函數 函數名相同,參數列表相同,返回值相同的函數(協變除外),這樣就構成了重寫。下邊再次區分幾個容易混淆的概念--重載、重寫(覆蓋)、重定義(隱藏)。重載--在同一個作用域內,函數名相同,參數列表不同,返回值可以相同可以不同的兩個函數可以構成重載,需要聲明的是,c++語言中支持函數的重載,而c語言中不支持函數的重載。原因是c語言和c++對函數的處理是不同的。具體可以點擊文末的鏈接。重寫(覆蓋)--在不同的作用域中(分別在父類和子類中),函數名相同,參數列表,返回值都相同(協變除外),并且父類的函數是虛函數,訪問限定符可同可不同的兩個函數就構成了重寫。重定義(隱藏)--在不同的作用域中(分別在父類和子類),函數名相同,只要不是構成重寫就是重定義。     協變:協變也是一種重寫,只是父類和子類中的函數的返回值不同,父類的函數返回父類的指針或者引用,子類函數返回子類的指針或者引用。(4)只有類的成員函數才可以被定義為虛函數,靜態成員函數不可以被定義為虛函數。(三)多繼承的對象模型測試代碼:
class A{public:       virtual void foo()       {              cout << "A::foo()" << endl;       }       virtual void funA()       {              cout << "A::funA()" << endl;       }private:       int _a;};class B{public:       virtual void foo()       {              cout << "B::foo()" << endl;       }       virtual void funB()       {              cout << "B::funB()" << endl;       }private:       int _b;};class C :public A, public B{public:       virtual void foo()       {              cout << "C::foo()" << endl;       }       virtual void funC()       {              cout << "C::funC()" << endl;       }private:       int _c;};void test3(){       C c;}下邊我們先通過調試信息,看看對象的內存分布:通過這個圖,我們就可以畫出多繼承的對象的內存分布:下邊我們仍然編寫一個函數打印出虛函數表:
void PrintTable(int* vTable){       typedef void(*pFun)();       cout << "虛表地址為:" << vTable << endl;       for (int i = 0; vTable[i] != NULL; ++i)       {              printf("第%d個虛函數地址為:0x%p->", i, vTable[i]);              pFun f = (pFun)vTable[i];              f();       }       cout << endl;}void test3(){       C c;       int* vTable = (int*)(*(int*)&c);       PrintTable(vTable);       vTable = (int *)(*((int*)&c + sizeof(A) / 4));       PrintTable(vTable);}程序運行結果:【總結】在多重繼承體系下,有n個含有虛函數的父類,派生類中就有n個虛函數表,最終子類的虛函數是在第一個父類的虛函數表中;(四)含有虛繼承的多重繼承模型(含有虛函數)測試代碼:
class A{public:	A()		:_a(1)	{}	virtual void foo()	{		cout << "A::foo()" << endl;	}	virtual void funA()	{		cout << "A::funA()" << endl;	}private:	int _a;};class B : public virtual A{public:	B()		:_b(2)	{}	virtual void foo()	{		cout << "B::foo()" << endl;	}	virtual void funB()	{		cout << "B::funB()" << endl;	}private:	int _b;};class C : public virtual A{public:	C()		:_c(3)	{}	virtual void foo()	{		cout << "C::foo()" << endl;	}	virtual void funC()	{		cout << "C::funC()" << endl;	}private:	int _c;};class D :public B, public C{public:	D()		:_d(4)	{}	virtual void foo()	{		cout << "D::foo()" << endl;	}	virtual void FunD()	{		cout << "D::funD()" << endl;	}private:	int _d;};通過調試信息看對象模型:B類對象的調試信息:C類對象的調試信息:D類對象的調試信息:下邊,根據調試信息畫出內存分布:文中涉及到的內容鏈接:內存對齊重載、重寫、重定義詳解重載參考文檔:對象模型(一)對象模型(二)


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

主站蜘蛛池模板: 灵丘县| 阿拉尔市| 外汇| 兰溪市| 安溪县| 宁远县| 镇原县| 诸暨市| 姜堰市| 左云县| 南康市| 新巴尔虎左旗| 南阳市| 当雄县| 虎林市| 南宁市| 商洛市| 扶余县| 吉林市| 沂南县| 宜都市| 赣州市| 盘锦市| 巴里| 阿巴嘎旗| 滨州市| 朔州市| 阿拉善左旗| 泰和县| 日喀则市| 娱乐| 三明市| 贵阳市| 门头沟区| 神农架林区| 五寨县| 石家庄市| 平泉县| 勐海县| 韶关市| 新建县|