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

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

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

2019-11-11 04:51:26
字體:
供稿:網(wǎng)友

以下代碼運行環(huán)境:windows8.1 32位 VS2015

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


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

圖片精選

主站蜘蛛池模板: 五常市| 宁夏| 三门峡市| 布尔津县| 横山县| 松原市| 宜宾市| 星座| 萨嘎县| 阿克苏市| 彭州市| 兴义市| 新河县| 清水河县| 会理县| 东兰县| 达拉特旗| 松江区| 武宣县| 铜梁县| 靖西县| 夹江县| 福海县| 普格县| 柘荣县| 菏泽市| 讷河市| 鲁山县| 昆明市| 曲阳县| 抚顺市| 洪雅县| 尼木县| 苏尼特右旗| 通江县| 平利县| 灌阳县| 衡南县| 饶平县| 合阳县| 云南省|