1.虛函數(shù)和多態(tài)
- 在類的定義中,前面有virtual關(guān)鍵字的成員函數(shù)就是虛函數(shù)败徊。
- virtual關(guān)鍵字只用在類定義里的函數(shù)說明中尿这,寫函數(shù)體時不用。
- 多態(tài)的表現(xiàn)形式一
?派生類的指針可以賦給基類指針蝙斜。
?通過基類指針調(diào)用基類和派生類中的同名虛函數(shù)時:
(1)若該指針指向一個基類的對象,那么被調(diào)用是
基類的虛函數(shù)澎胡;
(2)若該指針指向一個派生類的對象孕荠,那么被調(diào)用
的是派生類的虛函數(shù)绢片。
這種機(jī)制就叫做“多態(tài)”。
class CBase {
public:
virtual void SomeVirtualFunction() { }
};
class CDerived :public CBase {
public:
virtual void SomeVirtualFunction() { }
};
int main() {
CDerived ODerived;
CBase * p = &ODerived;
p->SomeVirtualFunction(); //調(diào)用哪個虛函數(shù)取決于p指向哪種類型的對象
return 0;
}
- 多態(tài)的表現(xiàn)形式二
?派生類的對象可以賦給基類引用
?通過基類引用調(diào)用基類和派生類中的同名虛函數(shù)時:
(1)若該引用引用的是一個基類的對象岛琼,那么被調(diào)
用是基類的虛函數(shù)底循;
(2)若該引用引用的是一個派生類的對象,那么被
調(diào)用的是派生類的虛函數(shù)槐瑞。
這種機(jī)制也叫做“多態(tài)”熙涤。
class CBase {
public:
virtual void SomeVirtualFunction() { }
};
class CDerived :public CBase {
public:
virtual void SomeVirtualFunction() { }
};
int main() {
CDerived ODerived;
CBase & r = ODerived;
r.SomeVirtualFunction(); //調(diào)用哪個虛函數(shù)取決于r引用哪種類型的對象
return 0;
}
- 多態(tài)的作用
在面向?qū)ο蟮某绦蛟O(shè)計中使用多態(tài),能夠增強(qiáng)程序的可擴(kuò)充性困檩,即程序需要修改或增加功能的時候祠挫,需要改動和增加的代碼較少。
用基類指針數(shù)組存放指向各種派生類對象的指針悼沿,然后遍歷該數(shù)組等舔,就能對各個派生類對象做各種操作,是很常用的做法
- 構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù)
在構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù)糟趾,不是多態(tài)慌植。編譯時即可確定,調(diào)用的函數(shù)是自己的類或基類中定義的函數(shù)义郑,不會等到運(yùn)行時才決定調(diào)用自己的還是派生類的函數(shù)蝶柿。
2.多態(tài)的實現(xiàn)原理
“多態(tài)” 的關(guān)鍵在于通過基類指針或引用調(diào)用
一個虛函數(shù)時,編譯時不確定到底調(diào)用的是基類還是派生類的函數(shù)非驮,運(yùn)行時才確定 ---- 這叫“動態(tài)聯(lián)編” 交汤。
-
多態(tài)實現(xiàn)的關(guān)鍵 --- 虛函數(shù)表
每一個有虛函數(shù)的類(或有虛函數(shù)的類的派生類)都有一個虛函數(shù)表,該類的任何對象中都放著虛函數(shù)表的指針劫笙。虛函數(shù)表中列出了該類的虛函數(shù)地址芙扎。 多出來的4個字節(jié)就是用來放虛函數(shù)表的地址的。
3.虛析構(gòu)函數(shù)
- 通過基類的指針刪除派生類對象時填大,通常情況下只調(diào)用基類的析構(gòu)函數(shù)
? 但是戒洼,刪除一個派生類的對象時,應(yīng)該先調(diào)用派生類的析構(gòu)函數(shù)栋盹,然后調(diào)用基類的析構(gòu)函數(shù)施逾。
?解決辦法:把基類的析構(gòu)函數(shù)聲明為virtual
? 派生類的析構(gòu)函數(shù)可以virtual不進(jìn)行聲明
? 通過基類的指針刪除派生類對象時敷矫,首先調(diào)用派生類的析構(gòu)函數(shù)例获,然后調(diào)用基類的析構(gòu)函數(shù)
? 一般來說,一個類如果定義了虛函數(shù)曹仗,則應(yīng)該將析構(gòu)函數(shù)也定義成虛函數(shù)榨汤。或者怎茫,一個類打算作為基類使用收壕,也應(yīng)該將析構(gòu)函數(shù)定義成虛函數(shù)妓灌。
注意:不允許以虛函數(shù)作為構(gòu)造函數(shù)
class son {
public:
~son() { cout << "bye from son" << endl; };
};
class grandson :public son {
public:
~grandson() { cout << "bye from grandson" << endl; };
};
int main() {
son *pson;
pson = new grandson();
delete pson;
return 0;
}
//輸出: bye from son 沒有執(zhí)行g(shù)randson::~grandson()!!!
class son {
public:
virtual ~son() { cout << "bye from son" << endl; };
};
class grandson :public son {
public:
~grandson() { cout << "bye from grandson" << endl; };
};
int main() {
son *pson;
pson = new grandson();
delete pson;
return 0;
}
//輸出: bye from grandson
//bye from son
//執(zhí)行g(shù)randson::~grandson(),引起執(zhí)行son::~son()C巯堋3婀 !
4.純虛函數(shù)和抽象類
? 純虛函數(shù): 沒有函數(shù)體的虛函數(shù)
class A {
private: int a;
public:
virtual void Print() = 0; //純虛函數(shù)
void fun() { cout << "fun"; }
};
- 包含純虛函數(shù)的類叫抽象類
? 抽象類只能作為基類來派生新類使用圃验,不能創(chuàng)建抽象類的對象
? 抽象類的指針和引用可以指向由抽象類派生出來的類的對象
A a ; // 錯掉伏, A 是抽象類,不能創(chuàng)建對象
A * pa ; // ok,可以定義抽象類的指針和引用
pa = new A ; //錯誤, A 是抽象類澳窑,不能創(chuàng)建對象
- 在抽象類的成員函數(shù)內(nèi)可以調(diào)用純虛函數(shù)斧散,但是在構(gòu)造函數(shù)或析構(gòu)函數(shù)內(nèi)部
不能調(diào)用純虛函數(shù)。 - 如果一個類從抽象類派生而來摊聋,那么當(dāng)且僅當(dāng)它實現(xiàn)了基類中的所有純虛函
數(shù)鸡捐,它才能成為非抽象類。
class A {
public:
virtual void f() = 0; //純虛函數(shù)
void g() {
this->f(); //ok
}
A() { //f( ); // 錯誤
}
};
class B :public A {
public:
void f() { cout << "B:f()" << endl; }
};
int main() {
B b;
b.g();
return 0;
}