1. 程式人生 > >c++中的 虛擬函式 純虛擬函式 虛基類

c++中的 虛擬函式 純虛擬函式 虛基類

虛擬函式 ,純虛擬函式, 虛基類,它們都和virtual有關,這三個帶有虛字的定義容易使人混淆,下面先從作用上來解釋這三個定義的區別:

1.虛擬函式是用於多型中virtual修飾父類函式,確保父類指標呼叫子類物件時,執行子類函式的。

2.純虛擬函式是用來定義介面的,也就是基類中定義一個純虛擬函式,基類不用實現,讓子類來實現。

3.虛基類是用來在多繼承中,如果父類繼承自同一個父類,就只例項化一個父類(說的有點繞,就是隻例項化一個爺爺的意思=。=)。

如果上面的沒看懂沒關係,下面來慢慢解釋清楚。

一.虛擬函式

這個和多型有關,多型的定義不清楚的話到其他地方先了解一下,多型的三個必要條件:1.繼承 2.過載 3.父類指標指向子類物件。

下面看程式:

第一個是沒有使用virtual的,沒有使用多型的:

  1. class A  
  2. {  
  3. public:  
  4.     void printf(){  
  5.         cout<<"printf A"<<endl;  
  6.     }  
  7. };  
  8. class B : public A  
  9. {  
  10. public:  
  11.     void printf(){  
  12.         cout<<"printf B"<<endl;  
  13.     }  
  14. };  
  15. int main(int argc, constchar * argv[])  
  16. {  
  17.     A *a = new A();  
  18.     a->printf();  
  19.     B *b = new B();  
  20.     b->printf();  
  21.     return 0;  
  22. }  

結果:

printf A

printf B


這是最基本的用法,沒有多型,只有繼承,下面是使用了多型但是沒有引用virtual關鍵字的情況,多型的作用請參考其他地方的文章:

  1. int main(int argc, constchar * argv[])  
  2. {  
  3.     A *a = new B();  
  4.     a->printf();  
  5.     return
     0;  
  6. }  

結果:

printf A

因為類的定義都一樣,所以就沒再寫出來了,當父類指標指向子類物件的時候,如果不使用virtual,父類呼叫方法的時候還是呼叫了父類自己的方法,沒有呼叫子類重寫的方法,所以就沒有實現到多型的作用,我們再來在父類中試試加入virtual關鍵字看看:

  1. class A  
  2. {  
  3. public:  
  4. virtualvoid printf(){  
  5.         cout<<"printf A"<<endl;  
  6.     }  
  7. };  
  8. class B : public A  
  9. {  
  10. public:  
  11.     void printf(){  
  12.         cout<<"printf B"<<endl;  
  13.     }  
  14. };  
  15. int main(int argc, constchar * argv[])  
  16. {  
  17.     A *a = new B();  
  18.     a->printf();  
  19.     return 0;  
  20. }  

結果:

printf B

virtual是加入到父類中的,子類的程式碼沒改變,main函式還是父類指標指向子類物件,結果終於可以列印到子類重寫的方法了,所以證實了虛擬函式是用於多型中virtual修飾父類該重寫的函式,確保父類指標呼叫子類物件時執行子類函式的。

二.純虛擬函式

簡單點說,純虛擬函式就像java的介面,使用了純虛擬函式的類不能被例項化,定義了純虛擬函式的類不用寫純虛擬函式的實現,由子類實現,下面看程式碼:

  1. class A  
  2. {  
  3. public:  
  4.     virtualvoid printf() =0;  
  5. };  
  6. void A::printf()//純虛擬函式可以不寫實現
  7. {  
  8.     cout<<"printf A"<<endl;  
  9. }  
  10. class B : public A  
  11. {  
  12. public:  
  13.     void printf(){  
  14.         cout<<"printf B"<<endl;  
  15.     }  
  16. };  
  17. int main(int argc, constchar * argv[])  
  18. {  
  19.     A *a =new A();//編譯出錯,純虛擬函式的類不能例項化
  20.     a->printf();  
  21.     return 0;  
  22. }  
virtualvoid printf() = 0;這是虛擬函式的寫法,我在下面寫了虛擬函式的實現 voidA::printf(),其實寫不寫都沒關係,寫了也起不了作用= 。= 。然後我才main函式中嘗試吧純虛擬函式的類例項化,結果直接報錯,說明純虛擬函式是不能例項化的。
  1. int main(int argc, constchar * argv[])  
  2. {  
  3.     A *a =newB();//這裡使用了多型
  4.     a->printf();  
  5.     return 0;  
  6. }  

結果:

printf B


我把main函式的a指向了子類的物件,結果可以正確打印出子類的方法。由此說明了純虛擬函式也是為多型服務的,它的作用是定義一個介面,讓子類去實現。

三.虛基類

虛基類是c++獨有的東西,因為c++中有多繼承,也是關鍵字virtual相關的定義。

先來說說多繼承,如果爺爺類(暫把父類的父類暫定為爺爺類= = ),父類繼承自爺爺類。如果孫類繼承自多個父類(聽起來有點怪異),那麼如果不使用虛基類,就會例項化多個爺爺類物件(越說越離奇),編譯器會報錯,說有歧義性。如果父類繼承自虛基類,則可以解決多個父類不會例項化多個爺爺的問題,就是隻有一個爺爺。

下面看程式碼:

  1. class Grandfather{  
  2. public:  
  3.     int flag;  
  4.     Grandfather(){  
  5.         flag = 1;  
  6.     }  
  7. };  
  8. class Father1:publicGrandfather{  
  9. public:  
  10.     Father1(){  
  11.         flag = 2;  
  12.     }  
  13. };  
  14. class Father2:publicGrandfather{  
  15. public:  
  16.     Father2(){  
  17.         flag = 3;  
  18.     }  
  19. };  
  20. class Son:public Father1,publicFather2{  
  21. };  
  22. int main(int argc, constchar * argv[])  
  23. {  
  24.     Son *son = new Son();  
  25.     cout<<son->flag<<endl;//這裡編譯錯誤,沒法指定flag是指定那一個,歧義
  26.     return 0;  
  27. }  

如果沒有使用虛基類,多個父類繼承自同一個爺爺類,就會產生歧義,到底是不是同一個爺爺?如果父類繼承虛基類就不同了:

  1. class Grandfather{  
  2. public:  
  3.     int flag;  
  4.     Grandfather(){  
  5.         flag = 1;  
  6.         cout<<"Grandfather flag = "<<flag <<endl;  
  7.     }  
  8. };  
  9. class Father1:virtualpublicGrandfather{  
  10. public:  
  11.     Father1(){  
  12.         flag = 2;  
  13.         cout<<"Father1 flag = "<<flag<<endl;  
  14.     }  
  15. };  
  16. class Father2:virtualpublicGrandfather{  
  17. public:  
  18.     Father2(){  
  19.         flag = 3;  
  20.         cout<<"Father2 flag = "<<flag<<endl;  
  21.     }  
  22. };  
  23. class Son:public Father1,publicFather2{  
  24. };  
  25. int main(int argc, constchar * argv[])  
  26. {  
  27.     Son *son = new Son();  
  28.     cout<<son->flag<<endl;  
  29.     return 0;  
  30. }  

結果:

Grandfather flag = 1

Father1 flag = 2

Father2 flag = 3

3

現在,可以運行了,class Father2:virtualpublicGrandfather,就是繼承虛基類的寫法,爺爺物件只有一個,爺爺類的變數也只例項化了一次,那為什麼最後打印出來的是3呢?看建構函式的順序就可以看出來了,現在構造了爺爺類,再構造第一個繼承的父類,最後繼承第二個繼承的父類,因此flag最後保持在第二個父類的修改值裡了。

以上就是虛擬函式 ,純虛擬函式, 虛基類知識點,虛擬函式 ,純虛擬函式是為了多型服務,虛基類是為了只例項化一次基類存在的,如有說的不正確的地方,也請各位可以指正出來,共同學習。