c++中的 虛擬函式 純虛擬函式 虛基類
虛擬函式 ,純虛擬函式, 虛基類,它們都和virtual有關,這三個帶有虛字的定義容易使人混淆,下面先從作用上來解釋這三個定義的區別:
1.虛擬函式是用於多型中virtual修飾父類函式,確保父類指標呼叫子類物件時,執行子類函式的。
2.純虛擬函式是用來定義介面的,也就是基類中定義一個純虛擬函式,基類不用實現,讓子類來實現。
3.虛基類是用來在多繼承中,如果父類繼承自同一個父類,就只例項化一個父類(說的有點繞,就是隻例項化一個爺爺的意思=。=)。
如果上面的沒看懂沒關係,下面來慢慢解釋清楚。
一.虛擬函式
這個和多型有關,多型的定義不清楚的話到其他地方先了解一下,多型的三個必要條件:1.繼承 2.過載 3.父類指標指向子類物件。
下面看程式:
第一個是沒有使用virtual的,沒有使用多型的:
- class A
- {
- public:
- void printf(){
- cout<<"printf A"<<endl;
- }
- };
- class B : public A
- {
- public:
- void printf(){
- cout<<"printf B"<<endl;
- }
- };
- int main(int argc, constchar * argv[])
-
{
- A *a = new A();
- a->printf();
- B *b = new B();
- b->printf();
- return 0;
- }
結果:
printf A
printf B
這是最基本的用法,沒有多型,只有繼承,下面是使用了多型但是沒有引用virtual關鍵字的情況,多型的作用請參考其他地方的文章:
- int main(int argc, constchar * argv[])
- {
- A *a = new B();
- a->printf();
-
return
- }
結果:
printf A
因為類的定義都一樣,所以就沒再寫出來了,當父類指標指向子類物件的時候,如果不使用virtual,父類呼叫方法的時候還是呼叫了父類自己的方法,沒有呼叫子類重寫的方法,所以就沒有實現到多型的作用,我們再來在父類中試試加入virtual關鍵字看看:
- class A
- {
- public:
- virtualvoid printf(){
- cout<<"printf A"<<endl;
- }
- };
- class B : public A
- {
- public:
- void printf(){
- cout<<"printf B"<<endl;
- }
- };
- int main(int argc, constchar * argv[])
- {
- A *a = new B();
- a->printf();
- return 0;
- }
結果:
printf B
virtual是加入到父類中的,子類的程式碼沒改變,main函式還是父類指標指向子類物件,結果終於可以列印到子類重寫的方法了,所以證實了虛擬函式是用於多型中virtual修飾父類該重寫的函式,確保父類指標呼叫子類物件時執行子類函式的。
二.純虛擬函式
簡單點說,純虛擬函式就像java的介面,使用了純虛擬函式的類不能被例項化,定義了純虛擬函式的類不用寫純虛擬函式的實現,由子類實現,下面看程式碼:
- class A
- {
- public:
- virtualvoid printf() =0;
- };
- void A::printf()//純虛擬函式可以不寫實現
- {
- cout<<"printf A"<<endl;
- }
- class B : public A
- {
- public:
- void printf(){
- cout<<"printf B"<<endl;
- }
- };
- int main(int argc, constchar * argv[])
- {
- A *a =new A();//編譯出錯,純虛擬函式的類不能例項化
- a->printf();
- return 0;
- }
- int main(int argc, constchar * argv[])
- {
- A *a =newB();//這裡使用了多型
- a->printf();
- return 0;
- }
結果:
printf B
我把main函式的a指向了子類的物件,結果可以正確打印出子類的方法。由此說明了純虛擬函式也是為多型服務的,它的作用是定義一個介面,讓子類去實現。
三.虛基類
虛基類是c++獨有的東西,因為c++中有多繼承,也是關鍵字virtual相關的定義。
先來說說多繼承,如果爺爺類(暫把父類的父類暫定為爺爺類= = ),父類繼承自爺爺類。如果孫類繼承自多個父類(聽起來有點怪異),那麼如果不使用虛基類,就會例項化多個爺爺類物件(越說越離奇),編譯器會報錯,說有歧義性。如果父類繼承自虛基類,則可以解決多個父類不會例項化多個爺爺的問題,就是隻有一個爺爺。
下面看程式碼:
- class Grandfather{
- public:
- int flag;
- Grandfather(){
- flag = 1;
- }
- };
- class Father1:publicGrandfather{
- public:
- Father1(){
- flag = 2;
- }
- };
- class Father2:publicGrandfather{
- public:
- Father2(){
- flag = 3;
- }
- };
- class Son:public Father1,publicFather2{
- };
- int main(int argc, constchar * argv[])
- {
- Son *son = new Son();
- cout<<son->flag<<endl;//這裡編譯錯誤,沒法指定flag是指定那一個,歧義
- return 0;
- }
如果沒有使用虛基類,多個父類繼承自同一個爺爺類,就會產生歧義,到底是不是同一個爺爺?如果父類繼承虛基類就不同了:
- class Grandfather{
- public:
- int flag;
- Grandfather(){
- flag = 1;
- cout<<"Grandfather flag = "<<flag <<endl;
- }
- };
- class Father1:virtualpublicGrandfather{
- public:
- Father1(){
- flag = 2;
- cout<<"Father1 flag = "<<flag<<endl;
- }
- };
- class Father2:virtualpublicGrandfather{
- public:
- Father2(){
- flag = 3;
- cout<<"Father2 flag = "<<flag<<endl;
- }
- };
- class Son:public Father1,publicFather2{
- };
- int main(int argc, constchar * argv[])
- {
- Son *son = new Son();
- cout<<son->flag<<endl;
- return 0;
- }
結果:
Grandfather flag = 1
Father1 flag = 2
Father2 flag = 3
3
現在,可以運行了,class Father2:virtualpublicGrandfather,就是繼承虛基類的寫法,爺爺物件只有一個,爺爺類的變數也只例項化了一次,那為什麼最後打印出來的是3呢?看建構函式的順序就可以看出來了,現在構造了爺爺類,再構造第一個繼承的父類,最後繼承第二個繼承的父類,因此flag最後保持在第二個父類的修改值裡了。
以上就是虛擬函式 ,純虛擬函式, 虛基類知識點,虛擬函式 ,純虛擬函式是為了多型服務,虛基類是為了只例項化一次基類存在的,如有說的不正確的地方,也請各位可以指正出來,共同學習。