在學(xué)習(xí)完類對(duì)象的構(gòu)造后蟹肘,下面就需要學(xué)習(xí)類數(shù)據(jù)成員和函數(shù)成員的存取词疼。
編譯器對(duì)于類對(duì)象的處理方式:(1)對(duì)于空類,編譯器為該類添加一個(gè)char類型的成員帘腹,用來(lái)唯一標(biāo)識(shí)該類在內(nèi)存的位置(2)使用對(duì)齊機(jī)制贰盗,當(dāng)一個(gè)類的內(nèi)存字節(jié)數(shù)不足4的倍數(shù)將自動(dòng)補(bǔ)充,目的是為了尋址的方便
有些編譯器對(duì)于空類的處理進(jìn)行了優(yōu)化處理阳欲,僅當(dāng)該空類被繼承的時(shí)候舵盈,空類對(duì)象在子類對(duì)象中不占用任何內(nèi)存陋率,單獨(dú)空類的大小仍是1個(gè)字節(jié),這樣可能會(huì)避免對(duì)齊機(jī)制书释,優(yōu)化了C++對(duì)象模型的內(nèi)存空間翘贮。
但是對(duì)于非空類,不處理和優(yōu)化處理對(duì)于類對(duì)象的內(nèi)存空間沒(méi)有任何改變爆惧。
<pre>
class A{};
class X:public virtual A{};
class Y:public virtual B{};
Class Z:public X,public Y{}
</pre>
兩種處理機(jī)制得到的各個(gè)類的大欣暌场:
未優(yōu)化???????優(yōu)化后
??1 ????????????????1
??8 ????????????????4
??8 ????????????????4
??12????????????????8
一、數(shù)據(jù)成員深度探索
1扯再、數(shù)據(jù)成員在類中聲明的位置盡量靠前芍耘,雖然C++標(biāo)準(zhǔn)沒(méi)有規(guī)定聲明順序,但是為了防范全局變量和局部成員變量產(chǎn)生二義性熄阻,所以盡量在聲明函數(shù)前聲明全部變量斋竞。
inline函數(shù)在未定義之前不去去決斷其中使用的變量,但若聲明之后立即定義秃殉,則會(huì)判斷函數(shù)前聲明的變量坝初。
2、nostatic數(shù)據(jù)成員是在對(duì)象存儲(chǔ)空間中存放的钾军,static數(shù)據(jù)成員存在在靜態(tài)存儲(chǔ)區(qū)中鳄袍,每個(gè)類只用一份實(shí)例(除了模板類)。nostatic數(shù)據(jù)成員在內(nèi)存中的布局要求是:在同一訪問(wèn)權(quán)限段(public private protected)中吏恭,晚聲明的成員的地址比早聲明的成員的地址高即可拗小,不要求相鄰。
3樱哼、編譯器為了實(shí)現(xiàn)某種機(jī)制為類對(duì)象添加的成員哀九,如vptr指針,它的存放位置C++標(biāo)準(zhǔn)并沒(méi)有限制搅幅,可以放在對(duì)象首部阅束,也可以放在尾部。放在首部方便了對(duì)虛函數(shù)調(diào)用盏筐,放在尾部可以與C語(yǔ)言中的結(jié)構(gòu)體相兼容围俘,各有好處,視編譯器而定琢融。
4、數(shù)據(jù)成員的存取
考慮通過(guò)對(duì)象存取成員和通過(guò)對(duì)象指針存取成員有什么區(qū)別簿寂?
(1)static數(shù)據(jù)成員的存取
由于static數(shù)據(jù)成員存儲(chǔ)在程序的靜態(tài)存儲(chǔ)區(qū)中漾抬,當(dāng)通過(guò)類對(duì)象、對(duì)象指針或者類::方式存取該成員是常遂,編譯器將內(nèi)部轉(zhuǎn)化為對(duì)靜態(tài)變量的存取纳令。因?yàn)閟tatic數(shù)據(jù)成員并不存儲(chǔ)在類對(duì)象空間中,所以對(duì)靜態(tài)成員的存取不需要經(jīng)過(guò)對(duì)象,因此通過(guò)對(duì)象和指針存取static變量沒(méi)有任何差異平绩。
<strong>若取static數(shù)據(jù)成員的地址圈匆,將會(huì)得到該成員在內(nèi)存中的實(shí)際地址,而且其指針類型和普通指針類型是相同的捏雌,而nostatic數(shù)據(jù)成員則有所區(qū)別跃赚。</strong>
static int a;為一個(gè)A類中的一個(gè)static成員性湿,聲明一個(gè)指向它的指針應(yīng)該這樣聲明:int *p=&A::a;指針的使用也和普通指針相同纬傲。
(2)nostatic數(shù)據(jù)成員的存取
nostatic數(shù)據(jù)成員的存取必須通過(guò)對(duì)象或指向?qū)ο蟮闹羔槪驗(yàn)閚ostatic數(shù)據(jù)成員的地址依賴于對(duì)象的存儲(chǔ)地址肤频,編譯器會(huì)在存取nostatic成員時(shí)加上this指針(指向?qū)ο蟮钠鹗嫉刂罚┨纠ǎㄟ^(guò)this指針和nostatic成員在對(duì)象中的offset值存取該成員。
換句話說(shuō)宵荒,nostatic數(shù)據(jù)成員的物理地址可表示為this+offset汁雷。
所以,取某個(gè)類中的nostatic數(shù)據(jù)成員地址得到的是該成員在對(duì)象中存儲(chǔ)offset报咳,即&A::b轉(zhuǎn)化成指針類型就是int A::p=&A::b;想要使用p還需要通過(guò)對(duì)象才能完成如a.p==a.b;
<strong>注意:&A::b的到的值在編譯器端將會(huì)自動(dòng)加1侠讯,也就是說(shuō)真實(shí)的offset=&A::b-1,這樣做的目的是為了區(qū)別空的指向數(shù)據(jù)成員指針(0)和非空指向數(shù)據(jù)成員指針。當(dāng)調(diào)用指針的時(shí)候首先將指針值減1少孝,如果是空的成員指針的話將不能調(diào)用继低。這樣就可以把空指針(0)和指向首部成員的指針(0)分開(kāi)。</strong>
例如int A::P1=0;int A::p2=&A::b;則p2的值是1(假設(shè)b放在對(duì)象首部稍走,vptr在尾部)袁翁,當(dāng)調(diào)用時(shí)想將p2-1+this獲取成員地址,空指針則是-1婿脸。
1)單一繼承(非虛擬繼承):子類總是把基類對(duì)象放在子類對(duì)象的首部粱胜,然后才放子類自己的成員。因此狐树,子類通過(guò)對(duì)象或者通過(guò)對(duì)象指針訪問(wèn)基類成員不會(huì)存在間接性焙压,基類成員在編譯期就可以確定其offset值(基類成員在基類中的offset值和在子類中的offset值是一樣的)。因?yàn)榛悓?duì)象在子類對(duì)象的首部抑钟,這樣當(dāng)基類指針被子類賦值時(shí)涯曲,基類指針仍然指向基類對(duì)象起始地址。
<strong>當(dāng)存在多態(tài)時(shí)在塔,編譯器會(huì)自動(dòng)根據(jù)vptr的位置修改數(shù)據(jù)成員的offset值幻件。</strong>
2)多重繼承(非虛擬繼承):此種情況較上述情況麻煩,需要編譯器進(jìn)行地址轉(zhuǎn)換蛔溃〈铝ぃ基類按照繼承生命的順序在繼承類中排列篱蝇,因此第一個(gè)基類的地址不需要轉(zhuǎn)化,直接復(fù)制即可徽曲。而處于中間的基類的地址就需要通過(guò)this+中間類的大小才能夠確定零截,這個(gè)工作由編譯器完成。地址轉(zhuǎn)化的時(shí)候首先判斷子類地址是否為零秃臣。
<strong>這種情況下涧衙,存取基類對(duì)象中的成員在編譯器是就確定了offset值,因此通過(guò)對(duì)象和指針訪問(wèn)基類對(duì)象不會(huì)存在差異甜刻。</strong>
3)虛擬繼承:虛擬繼承使得繼承類無(wú)論繼承多少個(gè)虛擬基類绍撞,都會(huì)只包含一個(gè)虛擬基類對(duì)象。那么得院,虛擬基類對(duì)象在繼承類對(duì)象內(nèi)存分布中就只存在一個(gè)基類對(duì)象實(shí)例∩迪常現(xiàn)在通用的是將虛擬基類對(duì)象放在繼承類對(duì)象的尾部,繼承類其他成員在虛擬基類對(duì)象上面祥绞。
那么非洲,<strong>虛擬基類對(duì)象在每個(gè)繼承類中的offset值是不同的,因此如果通過(guò)對(duì)象指針存取基類對(duì)象成員蜕径,不能在編譯期確定基類成員的offset值两踏。</strong>但是,通過(guò)對(duì)象存取基類成員時(shí)是在編譯期確定offset值兜喻。
比如:
Vertex3D v3d梦染;
Point3D *p=&v3d;
那么p->_x的步驟是:v3d的this指針指向Point3D子對(duì)象的地址傳遞給p,然后通過(guò)p查詢虛表找到虛擬基類對(duì)象的地址朴皆,再根據(jù)offset進(jìn)行相應(yīng)的修改即可存取_x帕识。
如果還按照之前的offset值,_X在Point3D中的offset是13遂铡,但是_x在Vertex3D中offset不是13肮疗,這樣就可能提取錯(cuò)誤的值,因此扒接,對(duì)虛擬基類對(duì)象成員的提取將通過(guò)虛表間接獲得基類對(duì)象的地址進(jìn)行轉(zhuǎn)換伪货。
<strong>在虛擬繼承時(shí),使用對(duì)象和對(duì)象指針存取基類對(duì)象成員會(huì)產(chǎn)生差異钾怔,使用對(duì)象指針存取基類對(duì)象成員時(shí)碱呼,必須等到運(yùn)行期進(jìn)行判斷指針指向的真正類型才能確定offset值。</strong>
原文地址:http://www.cnblogs.com/tracylee/archive/2012/12/19/2825397.html