類中函數(shù)的深度探索
類中包含的函數(shù)主要有三種:static成員函數(shù)隆夯、nostatic成員函數(shù)、virtual成員函數(shù)。
C++類中數(shù)據(jù)成員和成員函數(shù)的命名機(jī)制:
數(shù)據(jù)成員的命名:在每個(gè)數(shù)據(jù)成員命名的時(shí)候編譯器將該成員所屬的類名也添加上猜绣,用來標(biāo)志這個(gè)成員的來源范圍秩贰。這樣霹俺,繼承類就可以與子類用相同的名字命名其成員,這樣就不會(huì)產(chǎn)生二義性和沖突毒费,但是對(duì)外界而言丙唧,通過繼承類對(duì)象訪問該名字,只會(huì)獲取繼承類的數(shù)據(jù)成員觅玻,因?yàn)楦鶕?jù)命名查找機(jī)制想际,兩個(gè)名字屬于不同的作用域,繼承類中的成員覆蓋了子類中的成員串塑,想要調(diào)用必須顯示調(diào)用子類成員或在繼承類作用域中使用using沼琉。(函數(shù)名相同)
成員函數(shù)的命名:在同一個(gè)類中允許函數(shù)重載,重載的機(jī)制就是對(duì)函數(shù)名+參數(shù)名編碼獲得唯一的編碼識(shí)別桩匪。
(1)static成員函數(shù)
首先打瘪,說明static成員函數(shù)的幾條限制:1)static成員函數(shù)只能使用static數(shù)據(jù)成員;2)static成員函數(shù)不能設(shè)為const傻昙、virtual闺骚、inline函數(shù),只能保持一份實(shí)例妆档;3)static成員函數(shù)不使用this指針僻爽,因此不必需要通過對(duì)象來訪問,雖然很多時(shí)候是這樣使用的贾惦。
static成員函數(shù)在內(nèi)存空間中只有一份實(shí)例胸梆,如果取函數(shù)的地址,得到的是真實(shí)的內(nèi)存地址须板,其函數(shù)指針是與普通函數(shù)相同的函數(shù)指針碰镜,而不是指向成員函數(shù)的指針,例如
static int foo(); int (*p)()=&A::foo; 使用方法與普通函數(shù)指針相同习瑰。
因此绪颖,對(duì)于static成員函數(shù)的調(diào)用與調(diào)用非成員函數(shù)的效率是相同的。
(2)nostatic成員函數(shù)
雖然nostatic成員函數(shù)也是存放在對(duì)象外的內(nèi)存空間中甜奄,且取其函數(shù)地址也是真實(shí)的內(nèi)存地址柠横,但是調(diào)用nostatic成員函數(shù)也需要通過對(duì)象,也就是說需要this指針课兄,因?yàn)閚ostatic成員函數(shù)中可以直接使用類數(shù)據(jù)成員牍氛。
編譯器將this指針作為參數(shù)傳進(jìn)nostatic成員函數(shù)中去,然后通過this指針訪問數(shù)據(jù)成員烟阐。例如:
int foo();A a;a.foo();編譯器將轉(zhuǎn)化為foo(&a);
編譯器將成員函數(shù)轉(zhuǎn)化為了非成員函數(shù)糜俗,是的對(duì)成員函數(shù)的調(diào)用不會(huì)有異于非成員函數(shù)。
成員函數(shù)的指針的使用也必須通過對(duì)象調(diào)用,例如
int (A::p)()=&A::foo;(a.p)();相當(dāng)于p(&a)調(diào)用悠抹,也可以由其繼承類對(duì)象調(diào)用。
(3)virtual函數(shù)
虛擬成員函數(shù)地址存放在虛表中扩淀,而獲得到虛表的訪問權(quán)仍然需要通過對(duì)象的地址楔敌。
指向虛擬成員函數(shù)的指針獲取到的虛擬成員函數(shù)的地址實(shí)際上是虛擬函數(shù)在虛表中slot編號(hào),通過該編號(hào)可以獲取到虛擬成員函數(shù)的地址驻谆,也正是因?yàn)楂@取得到的是編號(hào)而非真正的內(nèi)存地址卵凑,才能實(shí)現(xiàn)多態(tài)機(jī)制。
1)單一繼承
單一繼承情況下胜臊,子類會(huì)繼承父類的vptr指針和虛表勺卢,不會(huì)產(chǎn)生新的vptr,這時(shí)象对,子類中若存在不同與父類的虛擬函數(shù)黑忱,將會(huì)添加到虛表的尾部,子類若修改了父類中虛擬函數(shù)的定義勒魔,將會(huì)在虛表中相應(yīng)的位置進(jìn)行覆蓋指向新的函數(shù)地址甫煞,也就是說,子類的虛表是在父類虛表的基礎(chǔ)上擴(kuò)展修改得到的冠绢。
2)多重繼承
多重繼承中牽扯到this指針調(diào)整的問題抚吠,也就是第二個(gè)及以后的父類地址的問題。C++中將繼承類的虛函數(shù)寫到第一個(gè)父類的虛表中弟胀,而第一個(gè)父類的虛表也就成為主虛表楷力,其他的則成為次虛表。
在繼承類中修改的虛函數(shù)將會(huì)覆蓋所有的虛表中原虛函數(shù)孵户,<strong>凡是繼承類中存在的虛函數(shù)都會(huì)在主虛表中出現(xiàn)萧朝,</strong>對(duì)于沒有修改的虛函數(shù),其實(shí)際地址存放位置仍然在原父類的虛表中延届,調(diào)用的時(shí)候需要進(jìn)行this指針調(diào)整剪勿。
如圖,Derived a;a.mumble();的調(diào)用將會(huì)在主虛表中查到4號(hào)函數(shù)方庭,然后將this指針調(diào)整到Base2位置厕吉,同多Base2 子對(duì)象調(diào)用mumble()函數(shù)。
再例如械念,Base2 *p=new Derived; delete p;也會(huì)產(chǎn)生this指針的調(diào)整头朱,必須調(diào)用正確的析構(gòu)函數(shù),this指針必須回到真?zhèn)€Derived對(duì)象的地址首部龄减。
因此项钮,對(duì)于多重繼承主要是要考慮this指針的調(diào)整問題。
3)虛擬繼承
虛擬繼承的子類將會(huì)產(chǎn)生一個(gè)vptr和一個(gè)虛表來存儲(chǔ)子類中虛擬函數(shù)和其子類對(duì)象的地址。
虛擬繼承也需要進(jìn)行this指針的調(diào)整烁巫,如同多重繼承一樣署隘,但是this指針的調(diào)整更加復(fù)雜,建議不要在虛擬基類中定義nostatic成員亚隙,這樣會(huì)使虛擬子類對(duì)象的offset值的確定變得過于復(fù)雜磁餐。(在不同子類中,虛擬子對(duì)象的offset是不相同的)阿弃。
原文地址:http://www.cnblogs.com/tracylee/archive/2012/12/20/2825562.html