虛函數(shù)
類的某些函數(shù),基類希望它的派生類各自定義這些函數(shù)各自的版本。
除了構(gòu)造函數(shù),任何函數(shù)都可以是虛函數(shù)。
讓派生類繼承借口和缺省定義(默認(rèn)定義)。
定義&聲明
class A
{
virtual int get(){};
};
class B:public A
{
int get() override
}
如果是普通的虛函數(shù)(不是純虛函數(shù)),那么基類也是需要提供虛函數(shù)的定義的。
因?yàn)椴皇羌兲摵瘮?shù),那么該類就可以實(shí)例化,那就必須需要定義。
派生類必須在其內(nèi)部重新定義的虛函數(shù)進(jìn)行聲明。
在基類中定義虛函數(shù)的時(shí)候需要在最開頭加virtual。
虛函數(shù)可以有默認(rèn)實(shí)參,且派生類也可以定義不同的默認(rèn)實(shí)參,但是最好相同。
重寫
在子類函數(shù)重寫的時(shí)候,可以選擇加也可以選擇不加virtual。同時(shí),如果可以在最結(jié)尾的時(shí)候加override確認(rèn)是重寫。
在派生類中覆蓋了某個(gè)虛函數(shù)時(shí),可以再一次使用virtual指出該函數(shù)的性質(zhì),也可以不加。但一個(gè)函數(shù)被聲明為虛函數(shù)時(shí),在他的所有派生類中,都是虛函數(shù)。
重寫時(shí),虛函數(shù)的返回值,形參必須完全相同。
但是,當(dāng)基類返回的是自己類型的指針時(shí)。那么氣派生類可以修改虛函數(shù)的返回值,但必須是返回派生類自身類型的指針。
另外,在分離定義的時(shí)候,不能加virtual.
關(guān)鍵字
override
使用override關(guān)鍵字,用來確認(rèn)定義的函數(shù)確實(shí)是覆蓋了基類的虛函數(shù),如果沒有覆蓋,那么編譯時(shí)候會(huì)報(bào)錯(cuò)。
使用
類的函數(shù)成員分類
- 普通函數(shù)成員:基類希望其派生類直接繼承,而不要改變的函數(shù)
- 虛函數(shù):基類希望其派生類進(jìn)行覆蓋的函數(shù)。
普通虛函數(shù),可以派生類可以不用重寫。
純虛函數(shù),必須重寫。
普通成員函數(shù),其解析過程發(fā)生在編譯時(shí)。
虛函數(shù)的解析可能在編譯時(shí),也可能是在運(yùn)行時(shí)。
當(dāng)通過指針或引用調(diào)用對(duì)象的虛函數(shù)時(shí)。那么函數(shù)會(huì)在運(yùn)行時(shí)才會(huì)被解析。
這里解析的含義是,當(dāng)對(duì)象調(diào)用自己的成員函數(shù)的時(shí)候,是執(zhí)行自己的成員函數(shù),還是執(zhí)行其子類的成員函數(shù)。
當(dāng)將一個(gè)派生類對(duì)象的指針或引用賦值給基類對(duì)象指針時(shí)(此時(shí)會(huì)發(fā)生派生類向基類的轉(zhuǎn)化),在運(yùn)行時(shí)解析。執(zhí)行的是派生類的成員函數(shù)。
如果我們想回避虛函數(shù)的動(dòng)態(tài)綁定機(jī)制,而直接運(yùn)行派生類基類的成員函數(shù)。(調(diào)用的函數(shù)仍然是一個(gè)虛函數(shù),只是我們希望調(diào)用基類的)。
pa->A::set();
顯式的指定。那么函數(shù)將在編譯時(shí)完成解析。
抽象基類
接口類
純虛函數(shù)
純虛函數(shù)的目的是為了讓派生類只繼承借口,不繼承定義。所以必須要定義。
定義
class A
{
virtual int get()=0;
};
=0只能在類內(nèi)出現(xiàn)。
同時(shí)仍然可以在類外提供這個(gè)函數(shù)的定義。唯一的調(diào)用方式是顯式調(diào)用。
出純虛函數(shù)可以不提供定義,因?yàn)檫@個(gè)類不能夠?qū)嵗?。但是可以定義指向這個(gè)類的指針。
同時(shí)這個(gè)類里面還是可以出現(xiàn)正常的虛函數(shù),成員變量。
構(gòu)造函數(shù)
派生類構(gòu)造函數(shù)只初始化他的直接基類。也就是說派生類在構(gòu)造函數(shù)中只能調(diào)用直接基類的構(gòu)造函數(shù),不能使用其間接基類的構(gòu)造函數(shù)。
class C:public B
{
public:
C():B(),A()
{
};
}
這里報(bào)錯(cuò)
type 'A' is not a direct base of 'C'
C():B(),A(){};
虛析構(gòu)函數(shù)
任何函數(shù),只要帶有一個(gè)虛函數(shù),那么他的析構(gòu)函數(shù)必將是虛函數(shù)。
如果一個(gè)類的析構(gòu)函數(shù)不是虛函數(shù),那么不能將派生類向基類轉(zhuǎn)化
如果say()函數(shù)不是虛函數(shù)
//忽略大部分
A *pa;
B *pb;
pa=pb;
pa->say();
執(zhí)行的是類A的成員函數(shù)。
如果say()函數(shù)是虛函數(shù),那么執(zhí)行的是 類B的函數(shù)成員。
所以,如果基類的析構(gòu)函數(shù)不是虛函數(shù),那么我們使用上述的轉(zhuǎn)化的時(shí)候,析構(gòu)的將是類A的析構(gòu)函數(shù)。
那么其派生類部分的成員將無法析構(gòu)。
所以,析構(gòu)函數(shù)必須是虛函數(shù)。
析構(gòu)時(shí),從最外層向基層依次執(zhí)行析構(gòu)函數(shù).
構(gòu)造函數(shù)和析構(gòu)函數(shù)中不能使用虛函數(shù)
構(gòu)造函數(shù)中,構(gòu)造不完全,調(diào)用的是基類的函數(shù).
同樣析構(gòu)時(shí)也一樣。
對(duì)象構(gòu)造期間,虛函數(shù)不是虛函數(shù)。所以調(diào)用不會(huì)出錯(cuò),只是不知道會(huì)發(fā)生什么。
也就是說,派生類構(gòu)造時(shí),如果正處于基類構(gòu)造函數(shù)執(zhí)行期間,那么類型是基類類型,也就是說誰的構(gòu)造函數(shù)在執(zhí)行,那類型就是什么。
因?yàn)?,如果將基類函?shù)定義為虛函數(shù)。那么,無法初始化。編譯會(huì)報(bào)錯(cuò)。