注意:本文中代碼均使用 Qt 開發(fā)編譯環(huán)境
在C++中不能聲明虛構(gòu)造函數(shù)爱沟,但是可以聲明虛析構(gòu)函數(shù)帅霜。析構(gòu)函數(shù)沒有類型,也沒有參數(shù)呼伸,和普通成員函數(shù)相比身冀,虛析構(gòu)函數(shù)相對簡單。
語法:
virtual ~className();
如果有可能通過基類指針調(diào)用對象的析構(gòu)函數(shù)(通過delete)括享,并且被析構(gòu)的對象是有重要的析構(gòu)函數(shù)的派生類的對象闽铐,就需要讓基類的析構(gòu)函數(shù)成為虛函數(shù)。
未使用虛析構(gòu)函數(shù)的情況舉例:
#include <QCoreApplication>
#include <QDebug>
class Base{
public:
~Base(){ qDebug() << "Basedestructor"; }
};
class Derived: public Base{
public:
Derived();
~Derived();
private:
int * i_pointer;
};
Derived::Derived(){
i_pointer = new int(0);
}
Derived::~Derived(){
qDebug() << "Derived destructor";
delete i_pointer;
}
void fun(Base *b){
delete b;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Base *b= new Derived();
fun(b);
return a.exec();
}
本例使用了Qt5.8的開發(fā)和編譯環(huán)境奶浦,因此有一些Qt中的輸出log的方法兄墅,上述測試代碼的輸出為:
Basedestructor
這說明,通過基類指針刪除派生類對象時調(diào)用的是基類的析構(gòu)函數(shù)澳叉,派生類的析構(gòu)函數(shù)沒有被執(zhí)行隙咸,因此派生類對象中動態(tài)分配的內(nèi)存空間沒有被釋放沐悦,造成了內(nèi)存泄漏。
避免上述情況的方法就是將基類的析構(gòu)函數(shù)聲明為虛函數(shù):
class Base{
public:
virtual ~Base(){ qDebug() << "Basedestructor"; }
};
運行結(jié)果為:
Derived destructor
Basedestructor
這說明派生類的析構(gòu)函數(shù)被調(diào)用了五督,派生類對象中動態(tài)申請的內(nèi)存空間被正確的釋放了藏否。這是由于使用了虛析構(gòu)函數(shù),實現(xiàn)了多態(tài)充包。
《抽象類》
抽象類是一種特殊的類副签,他為一個類族提供統(tǒng)一的操作界面。抽象類是為了抽象和設(shè)計的目的而建立的基矮,可以說淆储,建立抽象類,就是為了通過它多態(tài)地使用其中的成員函數(shù)家浇。抽象類處于類層次的上層本砰,一個抽象類自身無法實例化,也就是說我們無法定義一個抽象類的對象钢悲,只能通過繼承機制点额,生成抽象類的非抽象派生,然后再實例化莺琳。
<純虛函數(shù)>
純虛函數(shù)是一個在基類中聲明的虛函數(shù)还棱,它在該基類中沒有定義具體的操作內(nèi)容,要求派生類根據(jù)實際需要定義自己的版本惭等,聲明格式:
virtual returnType functionName(params) = 0;
實際上珍手,它與一般虛函數(shù)成員的原型在書寫格式上的不同就在于后面加了“=0”。聲明為純虛函數(shù)之后咕缎,基類中就不再給出函數(shù)的實現(xiàn)部分珠十。純虛函數(shù)的函數(shù)體由派生類給出料扰。
還有一種情況是函數(shù)體為空的虛函數(shù)凭豪,請注意它和純虛函數(shù)的區(qū)別。純虛函數(shù)根本就沒有函數(shù)體晒杈,而空的虛函數(shù)的函數(shù)體為空嫂伞,前者所在的類是抽象類,不能直接進行實例化拯钻,而后者所在的類是可以實例化的帖努。他們共同的特點是都可以派生出新的類,然后在新的類中給出虛函數(shù)的實現(xiàn)粪般,而且這種新的實現(xiàn)可以具有多態(tài)特征拼余。
<抽象類>
帶有純虛函數(shù)的類就是抽象類。抽象類的主要作用就是通過它為一個類族建立一個公共接口亩歹,使它們能夠更有效地發(fā)揮多態(tài)特性匙监。抽象類聲明了一族派生類的共同接口凡橱,而接口的完整實現(xiàn),即純虛函數(shù)的函數(shù)體亭姥,要由派生類自己定義稼钩。
抽象類派生出新的類之后,如果派生類給出所有純虛函數(shù)的函數(shù)實現(xiàn)达罗,這個派生類就可以定義自己的對象坝撑,因而不再是抽象類;反之粮揉,如果派生類沒有給出所有純虛函數(shù)的函數(shù)實現(xiàn)巡李,這時派生類仍然是一個抽象類。
抽象類不能實例化滔蝉,即不能定義一個抽象類的對象击儡,但是,我們可以聲明一個抽象類的指針和引用蝠引。通過指針或引用阳谍,我們就可以指向并訪問派生類對象,進而訪問派生類的成員螃概,這種訪問是具有多態(tài)特征的矫夯。
示例:
#include <QCoreApplication>
#include <QDebug>
class B {
public:
virtual void display() = 0;
};
class C: public B {
public:
void display() { qDebug() << "C::display()"; }
};
class D: public C {
public:
void display() { qDebug() << "D::display()"; }
};
void fun(B *ptr) {
ptr->display();
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
B *p;
C c;
D d;
p = &c; fun(p);
p = &d; fun(p);
return a.exec();
}
該測試示例的輸出結(jié)果是:
C::display()
D::display()
同時,程序中派生類的虛函數(shù)并沒有用關(guān)鍵字virtual顯示說明吊洼,因為它們與基類的純虛函數(shù)具有相同的名稱训貌、參數(shù)及返回值,由系統(tǒng)自動判斷確定其為虛函數(shù)冒窍。