關(guān)于C++的類(lèi)對(duì)象,內(nèi)存分別問(wèn)題_第1頁(yè)
關(guān)于C++的類(lèi)對(duì)象,內(nèi)存分別問(wèn)題_第2頁(yè)
關(guān)于C++的類(lèi)對(duì)象,內(nèi)存分別問(wèn)題_第3頁(yè)
關(guān)于C++的類(lèi)對(duì)象,內(nèi)存分別問(wèn)題_第4頁(yè)
關(guān)于C++的類(lèi)對(duì)象,內(nèi)存分別問(wèn)題_第5頁(yè)
全文預(yù)覽已結(jié)束

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、C/C+ 關(guān)于C+的類(lèi)對(duì)象,內(nèi)存分布問(wèn)題-答王富濤同學(xué)原文出處C+對(duì)象內(nèi)存布局C+虛函數(shù)表解釋C+內(nèi)存布局(上)C+內(nèi)存布局(下)深度探索 C+對(duì)象模型原問(wèn)題如下:#include <iostream>using namespace std;class basepublic:virtual void fun1();void fun2();int main()base s;cout<<sizeof(s)<<endl;return 0;代碼的結(jié)果為4。這個(gè)我覺(jué)得是因?yàn)槟莻€(gè)虛函數(shù)表里的一個(gè)指針占了4個(gè)字節(jié)但是如果我去掉virtual 代碼的結(jié)果為1類(lèi)中的普通成員函

2、數(shù)占對(duì)象的空間嗎?數(shù)據(jù)成員所占空間的計(jì)算是否也是和結(jié)構(gòu)體類(lèi)似呢?這個(gè)就不明白了,請(qǐng)各位指點(diǎn)。我的回答如下:這個(gè)涉及到類(lèi)和結(jié)構(gòu)體,在C+內(nèi)部的排列方式。我也不是很了解,只能就自己了解的一點(diǎn)知識(shí)做點(diǎn)回答,歡迎大家指正。編譯器在編譯每個(gè)類(lèi)時(shí),不管這個(gè)類(lèi)以后會(huì)實(shí)例化幾個(gè)對(duì)象,首先,它會(huì)提取這些類(lèi)的共性,放到一起,做成一個(gè)表。比如類(lèi)里面的非虛函數(shù),這類(lèi)函數(shù),所有的對(duì)象共享一段函數(shù)代碼,自然沒(méi)有必要每個(gè)對(duì)象內(nèi)部都設(shè)置一個(gè)函數(shù)指針,這太浪費(fèi)內(nèi)存了。因此,一個(gè)類(lèi),所有的非虛函數(shù),會(huì)被編譯器排成一個(gè)符號(hào)表,放置在特定的編譯期基礎(chǔ)變量區(qū)。這實(shí)際表現(xiàn)看,是放在exe文件里面的,在調(diào)用一個(gè)程序時(shí),是直接從文件中讀出,

3、并經(jīng)過(guò)地址修訂,準(zhǔn)備使用,這部分連基棧都算不上,算是常量區(qū)了,所有的常量也是放在這個(gè)區(qū)。嗯,函數(shù)內(nèi)部的靜態(tài)變量,類(lèi)中的靜態(tài)變量,靜態(tài)函數(shù),都是這個(gè)區(qū)。那,除掉這些,類(lèi)里面還有什么呢?還有虛函數(shù),我們知道,虛函數(shù)表示可能繼承,事實(shí)上,多次(不是多重)繼承后,一個(gè)類(lèi)的虛函數(shù)內(nèi)部會(huì)有一個(gè)棧,每個(gè)虛函數(shù)都有一個(gè)棧,每次調(diào)用該函數(shù),會(huì)從棧頂開(kāi)始call,當(dāng)然,如果程序員愿意,也可以在繼承的虛函數(shù)內(nèi)部,通過(guò)調(diào)用父類(lèi)的同名虛函數(shù),逐級(jí)向上call,直至call完所有的虛函數(shù)為止。這就說(shuō)明,虛函數(shù)和普通成員函數(shù)不同,每個(gè)對(duì)象都有可能變化,因此,編譯器就不敢把這個(gè)函數(shù)的指針,放在常量區(qū),必須跟著對(duì)象走,注意,不

4、是類(lèi),類(lèi)是沒(méi)有實(shí)體的,因此,不存在sizeof,只有對(duì)象存在大小。(sizeof只能計(jì)算棧內(nèi)存(局部變量)的大小,不能計(jì)算全局變量和動(dòng)態(tài)申請(qǐng)(malloc)的內(nèi)存大小,參考程序員面試寶典二P57和P252的筆記)還有就是普通成員變量,這些內(nèi)容,每個(gè)對(duì)象也是不一樣的,因此,每個(gè)對(duì)象必須自己建立一個(gè)表來(lái)管理,否則大家就混了。因此,我們知道了,每個(gè)類(lèi),實(shí)例化對(duì)象之后,其實(shí)對(duì)象的實(shí)體在內(nèi)存中的存儲(chǔ),就只包含虛函數(shù)和普通成員變量,這是C+編譯器為了節(jié)約內(nèi)存做得優(yōu)化。我們回到你的代碼看,你的代碼中,fun2是普通函數(shù),被編譯器放到常量區(qū)去了,因此,不占用對(duì)象空間,虛函數(shù)fun1,則需要占用,我們知道,32

5、位操作系統(tǒng),一個(gè)指針是4Bytes,函數(shù)指針也是指針,因此,你的結(jié)果是4Bytes。取消了virtual 之后,fun1也變成了普通函數(shù),因此和fun2等同處理,就不再占用對(duì)象空間,因此,對(duì)象空間為0了。類(lèi)的虛函數(shù),表現(xiàn)為一個(gè)函數(shù)指針棧,棧底,是基類(lèi)這個(gè)函數(shù)的指針,往上,實(shí)際上是繼承類(lèi),該虛函數(shù)的繼承函數(shù)的指針,一個(gè)類(lèi),被繼承幾次,比如3次,最后一次繼承,這個(gè)棧就有3層。有點(diǎn)繞。舉個(gè)例子吧class Avirtual void Func(void);class B : public Avirtual void Func(void);class C : public Bvirtual void

6、Func(void);這個(gè)A類(lèi),里面的Func指針就是它自己B就是一個(gè)棧,棧底是A:Func,棧頂是B:Func而C就是三層的棧了,在B的基礎(chǔ)上,棧頂有壓入了C:Func基本上就是這個(gè)管理關(guān)系。我的話的意思是,在任何一層繼承函數(shù),都可以去手動(dòng)去call父類(lèi)的對(duì)應(yīng)函數(shù),完成對(duì)整個(gè)棧鏈上所有函數(shù)的調(diào)用。因?yàn)槲覀冎溃粋€(gè)類(lèi)的虛函數(shù),一旦被繼承,原來(lái)的父類(lèi)函數(shù)指針就被壓倒棧下面去了,從棧頂看,只有最后一層的函數(shù)指針。比如C這個(gè)類(lèi)看,我們看它的Func,只要它繼承并實(shí)現(xiàn)了,那么,調(diào)用Func一定只能調(diào)用C:Func,B和A的由于看不到,因此是不會(huì)被調(diào)用的。當(dāng)然,如果C沒(méi)有實(shí)現(xiàn)這個(gè)虛函數(shù),則Func的棧

7、上,沒(méi)有C:Func,因此,直接Call會(huì)Call到B:Func,以此類(lèi)推,如果B沒(méi)有實(shí)現(xiàn)這個(gè)虛函數(shù),表示未繼承,則Call會(huì)Call到A:Func,這就是虛函數(shù)繼承中,后實(shí)現(xiàn)的覆蓋前實(shí)現(xiàn)的原理。當(dāng)然,如果A內(nèi)沒(méi)有實(shí)現(xiàn)Func的實(shí)體,做了一個(gè)純虛函數(shù),而B(niǎo)和C這些繼承類(lèi)也不實(shí)現(xiàn),那么,編譯器在構(gòu)造符號(hào)表的時(shí)候,就會(huì)找不到任何一個(gè)Func的實(shí)體,該虛函數(shù)棧為空,無(wú)法連接,因此會(huì)報(bào)連接失敗的錯(cuò)誤,編譯不能通過(guò)。這種棧式管理,有好有壞,好處是后面的繼承類(lèi),可以選擇實(shí)現(xiàn)虛函數(shù),也可以選擇不實(shí)現(xiàn),偷個(gè)懶。程序不會(huì)出錯(cuò),下次調(diào)用該函數(shù),會(huì)自動(dòng)沿著它的繼承關(guān)系,尋找父類(lèi)以及更往前的爺爺類(lèi)的函數(shù)實(shí)體,至少能找

8、到一個(gè)執(zhí)行其功能,簡(jiǎn)化開(kāi)發(fā)。但是,也有一個(gè)壞處,就是一個(gè)虛函數(shù),一旦被繼承類(lèi)實(shí)現(xiàn)了,則父類(lèi)的必然被覆蓋,如果父類(lèi)有什么內(nèi)置的功能,就沒(méi)有辦法執(zhí)行了,這很麻煩,由于面向?qū)ο蟮睦^承關(guān)系,我們總是希望,繼承類(lèi)的對(duì)應(yīng)函數(shù),只要完成它相對(duì)于父類(lèi)增加的那部分功能就夠了,父類(lèi)的功能,還能繼續(xù)執(zhí)行,免得寫(xiě)重復(fù)的代碼。問(wèn)題1:  當(dāng)我聲明兩個(gè)全是虛函數(shù)時(shí) 結(jié)果也是 4答:對(duì)象里存的是“表”的地址。而非表。所以和幾個(gè)虛函數(shù)無(wú)關(guān),就4個(gè)字節(jié),如下例#include <iostream>using namespace std;class A public: virtual void f

9、un1() cout<<"fun1() called!"<<endl; virtual void fun3() cout<<"fun3() called!"<<endl; void fun2() cout<<"fun2() called!"<<endl; ;class B:public virtual A;class C:public virtual B;int main() cout<<sizeof(A)<<endl; cout<&

10、lt;sizeof(B)<<endl; cout<<sizeof(C)<<endl; return 0;輸入4 8 12,雖然A中有2個(gè)虛函數(shù),由于只需保存虛表地址,所以認(rèn)為4#include <iostream>using namespace std;class basepublic:virtual void fun1()cout << "fun1 called !" << endl;void fun2()cout << "fun2 called !" <<

11、 endl;double    d;static int  a;int main()    base s;    cout << sizeof(s) << endl;    return 0;疑問(wèn) 1:  double 是8個(gè)字節(jié) 這個(gè)程序運(yùn)行是16個(gè)字節(jié) 除了虛函數(shù)的4個(gè) 那多余處理的4個(gè)呢?疑問(wèn) 2:  static int  a;我將static int  換成double是24個(gè)沒(méi)錯(cuò),可我將st

12、atic 去掉,那么個(gè)int應(yīng)該是4個(gè)字節(jié) 共 4+8+4=16 可程序運(yùn)行是24個(gè) why?疑問(wèn) 2:將static去掉后,然后int a和double d調(diào)換后還是24個(gè)字節(jié),好像不符合內(nèi)存對(duì)齊???答1:由于sizeof不能計(jì)算全局變量(static)和動(dòng)態(tài)申請(qǐng)內(nèi)存(malloc)的大小,只能計(jì)算局部變量(棧內(nèi)存),所以static int  a是不會(huì)計(jì)算字節(jié)大小的,由于內(nèi)存對(duì)齊,double d占8個(gè),虛表地址占8個(gè),就共16個(gè)。參考程序員面試寶典二P57和P252的筆記答2:在類(lèi)定義時(shí),分配內(nèi)存空間存在“字節(jié)對(duì)齊”的問(wèn)題。編譯器首先根據(jù)類(lèi)成員變量中所占空間的最大值(

13、如double為8個(gè)字節(jié)),每次都一次性分配這么大的空間,然后填充,因此可能會(huì)有剩余。去掉static的情況,共為24個(gè)字節(jié),分析如下:1.類(lèi)數(shù)據(jù)成員所需最大的空間為sizeof(double)=8字節(jié)2.編譯器首先分配8個(gè)字節(jié)給虛表指針3.編譯器接著分配8個(gè)字節(jié)給double d,雖然前面虛表指針只占4個(gè)字節(jié),還剩下4個(gè)字節(jié),但是不夠double所需的空間,因此需要重新分配8個(gè)字節(jié)4.編譯器最后分配8個(gè)字節(jié)給int a。編譯器進(jìn)行”字節(jié)對(duì)齊“的一個(gè)重要的原因是便于類(lèi)成員通過(guò)地址進(jìn)行訪問(wèn)答3:調(diào)換后不能節(jié)省8個(gè)字節(jié)空間。因?yàn)檫€有一條原則:虛表指針和其他的類(lèi)成員不能共享一份內(nèi)存空間,因此還是24

14、個(gè)字節(jié)。下面再舉一個(gè)例子:Class  X1   virtual int disp();   int var1;   double a;sizeof(X1)=24字節(jié)Class  X2   virtual int disp();   int var1;   int var2;   double a;sizeof(X2)=24字節(jié)X1和X2類(lèi)的大?。╯izeof)相同,是因?yàn)樘摫碇羔樁肌蔼?dú)享”了一份內(nèi)存空間(sizeof(double),而X2中var1和var2“共享”了一份內(nèi)存空間。還有那個(gè)空類(lèi)的大小為1,我感覺(jué)是分配一個(gè)字節(jié)給空類(lèi),在內(nèi)存中給這個(gè)空類(lèi)做一個(gè)標(biāo)識(shí),同時(shí)使這個(gè)空類(lèi)的對(duì)象均指向同一個(gè)地址。我

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論