1. 析構(gòu)函數(shù)和虛析構(gòu)函數(shù)
如果基類的析構(gòu)函數(shù)是虛的丽惶,那么它的派生類的析構(gòu)函數(shù)都是虛的
這將導致:當派生類析構(gòu)的時候音羞,它的所有的基類的析構(gòu)函數(shù)都將得到調(diào)用
否則仓犬,只調(diào)用派生類的析構(gòu)函數(shù)(這可能導致基類的某些對象沒有得到釋放)
所以CObject類的析構(gòu)函數(shù)是虛的搀继,所有由它派生的類析構(gòu)的時候一級一級的進行,不會造成內(nèi)存泄漏财边。
??? 無論基類的析構(gòu)函數(shù)是否為虛析構(gòu)函數(shù). 基類的析構(gòu)函數(shù)總是會被自動調(diào)用的;但是, 如果用基類指針去操作一個了派生類對象,如果不為虛就不能保證派生類的析構(gòu)函數(shù)被調(diào)用酣难。
總結(jié)一下虛析構(gòu)函數(shù)的作用:
(1)如果父類的析構(gòu)函數(shù)不加virtual關鍵字
當父類的析構(gòu)函數(shù)不聲明成虛析構(gòu)函數(shù)的時候憨募,當子類繼承父類袁辈,父類的指針指向子類時晚缩,delete掉父類的指針(也就是析構(gòu)函數(shù)里面的 deletel baseptr)荞彼,只調(diào)動父類的析構(gòu)函數(shù),而不調(diào)動子類的析構(gòu)函數(shù)(因為沒辦法去訪問派生類的成員函數(shù)莺债,只能訪問成員數(shù)據(jù)變量)。
(2)如果父類的析構(gòu)函數(shù)加virtual關鍵字
當父類的析構(gòu)函數(shù)聲明成虛析構(gòu)函數(shù)的時候椎侠,當子類繼承父類我纪,父類的指針指向子類時丐吓,delete掉父類的指針(也就是析構(gòu)函數(shù)里面的 deletel baseptr)券犁,先調(diào)動子類的析構(gòu)函數(shù)粘衬,再調(diào)動父類的析構(gòu)函數(shù)。 (構(gòu)造函數(shù)先調(diào)用父類再調(diào)用子類勘伺,析構(gòu)函數(shù)先調(diào)用子類再調(diào)用父類飞醉,即先構(gòu)造的后刪除)
2. 純虛析構(gòu)函數(shù)
??? 析構(gòu)函數(shù)的純虛性唯一效果就是保證抽象類的實例化缅帘。
?? 《Effective C++》中第14條條款的一部分股毫,既是對虛析構(gòu)函數(shù)的徹底理解召衔,亦是對純虛析構(gòu)函數(shù)作用的解釋苍凛。
??? 在某些類里聲明純虛析構(gòu)函數(shù)很方便。純虛函數(shù)將產(chǎn)生抽象類——不能實例化的類(即不能創(chuàng)建此類型的對象)宣肚。有些時候霉涨,你想使一個類成為抽象類,但剛好又沒有任何純虛函數(shù)笙瑟。怎么辦?因為抽象類是準備被用做基類的框产,基類必須要有一個虛析構(gòu)函數(shù)秉宿,純虛函數(shù)會產(chǎn)生抽象類描睦,所以方法很簡單:在想要成為抽象類的類里聲明一個純虛析構(gòu)函數(shù)酌摇。
這里是一個例子:
class awov {
public:
virtual ~awov() = 0;????? // 聲明一個純虛析構(gòu)函數(shù)
};
????? 這個類有一個純虛函數(shù)嗡载,所以它是抽象的洼滚,而且它有一個虛析構(gòu)函數(shù)遥巴,所以不會產(chǎn)生析構(gòu)函數(shù)問題铲掐。但這里還有一件事:必須提供純虛析構(gòu)函數(shù)的定義:
awov::~awov() {}?????????? // 純虛析構(gòu)函數(shù)的定義
這個定義是必需的摆霉,因為虛析構(gòu)函數(shù)工作的方式是:最底層的派生類的析構(gòu)函數(shù)最先被調(diào)用携栋,然后各個基類的析構(gòu)函數(shù)被調(diào)用婉支。這就是說向挖,即使是抽象類,編譯器也要產(chǎn)生對~awov的調(diào)用跟畅,所以要保證為它提供函數(shù)體碍彭。如果不這么做庇忌,鏈接器就會檢測出來舰褪,最后還是得回去把它添上略就。
3. 虛函數(shù)
【1】在基類用virtual聲明成員函數(shù)為虛函數(shù)。這樣就可以在派生類中重新定義此函數(shù)表牢,為它賦予新的功能崔兴,并能方便地被調(diào)用。
【2】在派生類中重新定義此函數(shù)山析,要求函數(shù)名笋轨、函數(shù)(返回)類型、函數(shù)參數(shù)個數(shù)和類型與基函數(shù)的虛函數(shù)相同鸟款。如果在派生類中沒有對基類的虛函數(shù)重定義何什,則派生類簡單地繼承直接基類的虛函數(shù)处渣。
【3】C++規(guī)定罐栈,當一個成員函數(shù)被聲明為虛函數(shù)后,其派生類中的同名函數(shù)(符合2中定義的函數(shù))都自動成為虛函數(shù)荠诬。
【4】定義一個指向基類對象的指針變量琅翻,并使其指向同一類族中的某個對象锨天。通過該指針變量調(diào)用此函數(shù)杂伟,此時調(diào)用的就是指針變量指向的對象的同名函數(shù)。
【5】只能用virtual聲明類的成員函數(shù)拯钻,使它成為虛函數(shù)钧嘶,而不能將類外的普通函數(shù)聲明為虛函數(shù)棠众。
【6】一個成員函數(shù)被聲明為虛函數(shù)后,在同一類族中的類就不能再定義一個非virtual的但與該虛函數(shù)具有相同參數(shù)(個數(shù)與類型)和函數(shù)返回值類型的同名函數(shù)有决。
【7】靜態(tài)成員函數(shù)不能是虛函數(shù)闸拿,因為靜態(tài)成員函數(shù)不受限于某個對象。
【8】inline函數(shù)不能是虛函數(shù)书幕,因為inline函數(shù)是不能在運行中動態(tài)確定其位置的。即使虛函數(shù)在類的內(nèi)部定義,編譯時奔缠,仍將其視為非inline的。
【5】使用虛函數(shù),系統(tǒng)要有一定的空間開銷。當一個類帶有虛函數(shù)時测蘑,編譯器會為該類構(gòu)造一個虛函數(shù)表(virtual function tanle,vtable)沫勿,它是一個指針數(shù)組,存放每個虛函數(shù)的入口地址时甚。
4. 純虛函數(shù)
一個函數(shù)聲明為純虛后,純虛函數(shù)的意思是:我是一個抽象類!不要把我實例化!純虛函數(shù)用來規(guī)范派生類的行為又碌,實際上就是所謂的“接口”。它告訴使用者,我的派生類都會有這個函數(shù)。
virtual void show()=0;//純虛函數(shù)
這里將show()聲明為純虛函數(shù)(pure virtual function)墅拭。純虛函數(shù)是在聲明虛函數(shù)時被“初始化”為0的虛函數(shù)穗熬。
聲明純虛函數(shù)的一般形式為妓柜,
virtual 函數(shù)類型 函數(shù)名(參數(shù)列表)=0;
純虛函數(shù)沒有函數(shù)體作煌;最后的“=0”并不代表函數(shù)返回值為0鹰服,它只起形式上的作用笼踩,告訴編譯器“這是純虛函數(shù)”;這個一個聲明語句斑胜,最后有分號涧狮。
聲明純虛函數(shù)是告訴編譯器譬嚣,“在這里聲明了一個虛函數(shù),留待派生類中定義”。在派生類中對此函數(shù)提供了定義后泵督,它才能具備函數(shù)的功能,可以被調(diào)用。
純虛函數(shù)的作用是在基類中為其派生類保留了一個函數(shù)的名字,以便派生類根據(jù)需要對它進行定義。
如果在一個類中聲明了純虛函數(shù)租漂,而在其派生類中沒有對該函數(shù)定義,則該函數(shù)在派生類中仍為純虛函數(shù)。
1. 虛函數(shù)和純虛函數(shù)可以定義在同一個類(class)中骑脱,含有純虛函數(shù)的類被稱為抽象類(abstract class)苍糠,而只含有虛函數(shù)的類(class)不能被稱為抽象類(abstract class)叁丧。
2. 虛函數(shù)可以被直接使用,也可以被子類(sub class)重載以后以多態(tài)的形式調(diào)用岳瞭,而純虛函數(shù)必須在子類(sub class)中實現(xiàn)該函數(shù)才可以使用拥娄,因為純虛函數(shù)在基類(base class)
只有聲明而沒有定義。
?? 3. 虛函數(shù)和純虛函數(shù)都可以在子類(sub class)中被重載瞳筏,以多態(tài)的形式被調(diào)用稚瘾。
?? 4. 虛函數(shù)和純虛函數(shù)通常存在于抽象基類(abstract base class -ABC)之中,被繼承的子類重載乏矾,目的是提供一個統(tǒng)一的接口孟抗。
5. 虛函數(shù)的定義形式:virtual??? {method body}
純虛函數(shù)的定義形式:virtual??? { } = 0;
在虛函數(shù)和純虛函數(shù)的定義中不能有static標識符,原因很簡單钻心,被static修飾的函數(shù)在編譯時候要求前期bind,然而虛函數(shù)卻是動態(tài)綁定(run-time bind)凄硼,而且被兩者修飾的函數(shù)生命周期(life recycle)也不一樣。
6. 如果一個類中含有純虛函數(shù)捷沸,那么任何試圖對該類進行實例化的語句都將導致錯誤的產(chǎn)生摊沉,因為抽象基類(ABC)是不能被直接調(diào)用的。必須被子類繼承重載以后痒给,根據(jù)要求調(diào)用其子類的方法说墨。
5? 純抽象類
?? 從C++的 角度來看,一個抽象類和一個接口之間沒有任何區(qū)別苍柏。有時尼斧,我們習慣使用“純抽象類”這個詞來表示某個類僅僅只含有純虛函數(shù)(不包含任何數(shù)據(jù)成員),它是抽象類的最常見的形式试吁。
?? 使用純抽象類有什么好處棺棵?最明顯的例子就是“多接口、單實現(xiàn)”熄捍,這是一種很常見的情況烛恤。
?? 在C++中加入了 純虛函數(shù)的概念,一個純虛函數(shù)必須被其派生類重寫余耽。借助此概念缚柏,你可以在一個C++類中通過將其成員函數(shù) 聲明為純虛函數(shù)的方法表明該類是一個純接口類。從那以后碟贾,我就一直強調(diào)在C++中,有一種主要的使用類的方法就是讓該類不包含任何狀態(tài)粱锐, 而僅僅作為一個接口。
6? 抽象類
將不用來定義對象而只作為一種基本類型用作繼承的類恶座,稱為抽象類(abstract class),由于它常用作基類桂敛,通常稱為抽象基類术唬。凡是包含純虛函數(shù)的類都是抽象類粗仓。
如果在派生類中沒有對所有的純虛函數(shù)進行定義萝招,則此派生類仍然是抽象類橡淆,不能用來定義對象。
可以定義指向抽象類數(shù)據(jù)的指針變量师倔。當派生類成為具體類后凶朗,就可以用這個指針指向派生類對象搓萧,然后通過該指針調(diào)用虛函數(shù)。
??? 帶有純虛函數(shù)的類稱為抽象類。抽象類是一種特殊的類石蔗,它是為了抽象和設計的目的而建立的养距,它處于繼承層次結(jié)構(gòu)的較上層。抽象類是不能定義對象的,在實際中為了強調(diào)一個類是抽象類揣炕,可將該類的構(gòu)造函數(shù)說明為保護的訪問控制權(quán)限丁恭。抽象類的主要作用是將有關的組織在一個繼承層次結(jié)構(gòu)中,由它來為它們提供一個公共的根恶守,相關的子類是從這個根派生出來的第献。抽象類刻畫了一組子類的操作接口的通用語義,這些語義也傳給子類兔港。一般而言庸毫,抽象類只描述這組子類共同的操作接口,而完整的實現(xiàn)留給子類衫樊。抽象類只能作為基類來使用飒赃,其純虛函數(shù)的實現(xiàn)由派生類給出。如果派生類沒有重新定義純虛函數(shù)科侈,而派生類只是繼承基類的純虛函數(shù)盒揉,則這個派生類仍然還是一個抽象類。如果派生類中給出了基類純虛函數(shù)的實現(xiàn)兑徘,則該派生類就不再是抽象類了刚盈,它是一個可以建立對象的具體類了。
7 java 中的接口
不需要聲明接口為抽象或虛擬(本來就是)
接口不允許有構(gòu)造函數(shù)(純抽象了挂脑,根本不需要構(gòu)造)
接口不允許有析構(gòu)函數(shù)(本來無構(gòu)造藕漱,何需有析構(gòu))
接口的所有成員都是抽象的(純抽象類嘛)
接口只可以從接口繼承(因為只有接口可以保證使純虛的欲侮,如果從抽象類繼承,不能保證抽象類中可能存在非抽象的成員)
接口成員不允許有任何修飾(默認就是public的肋联,也只有是public的)
一個類或結(jié)構(gòu)可以實現(xiàn)多個接口