C++ virtual虛函數


 1 #include<iostream>
 2 using namespace std;
 3 class Base{
 4 public:
 5     void m()
 6     {
 7         cout << "it's Base's m() " << endl;
 8     }
 9     virtual void f()
10     {
11         cout << "it's Base's f()" << endl;
12     }
13 };
14 class Sub: public Base 
15 {
16     void m()
17     {
18         cout << "it's Sub's m()" << endl;
19     }
20     void f()
21     {
22         cout << "it's Sub's f()" << endl;
23     }
24 };
25 void main()
26 {
27     Base* base = new Sub;
28     base->f();
29     base->m();
30 }

上述是以Base為基類,並且Sub派生了Base,同時復寫了兩個函數f(),m()

當在主函數main里用Base類型的指針指向Sub類型對象,此時利用Base指針調用f()和m(),那么這個時候問題就來了。

在默認情況下Base類的指針會調用當前類型的方法,也就是Base::f(),Base::m(),那么應該輸出的都是Base方法中的語句

然而結果輸出的是

 

為什么會產生這種原因呢?

 

因為父類的f()是虛函數,那么當基類指針調用它的派生類對象時,會默認調用多態性對象的相應方法,於是就解釋了為什么Base* base->f()調用了派生類Sub的f()的方法,

 

由此我們可以知道虛函數Virtual主要是為了解決多態性問題,因為一個基類方法定義后,它的派生類對象會針對此方法進行不同的覆蓋實現,即多態性,若你此時利用基類的指針來指向基類的多態性子類,那么就需要利用virtual修飾基類方法,從而可以來調用你所需要的派生類的方法而避免了調用基類的此方法。

 

Virtual是C++ OO機制中很重要的一個關鍵字。在類Base中加了Virtual關鍵字的函數就是虛擬函數(例如函數print),於是在Base的派生類Derived中就可以通過重寫虛擬函數來實現對基類虛擬函數的覆蓋。當基類Base的指針point指向派生類Derived的對象時,對point的print函數的調用實際上是調用了Derived的print函數而不是Base的print函數。這是面向對象中的多態性的體現。

 

還有虛函數析構函數,應用在這樣一個問題:在基類為抽象類,並且基類指針指向派生類,想要刪除這個基類指針指向的內存時

先看第一種情況

 1 de<iostream>
 2 using namespace std;
 3 class Base{
 4 public:
 5     Base()
 6     {
 7     }
 8     ~Base()
 9     {
10         cout << "Base has deleted" << endl;
11     }
12 };
13 class Sub: public Base 
14 {
15 public:
16     Sub()
17     {
18     }
19     ~Sub()
20     {
21         cout << "Sub has delete" << endl;
22     }
23 };
24 void main()
25 {
26     Base* base = new Sub;
27     delete base;
28     base = NULL;
29 }

看運行結果

此時是用基類Base指針去操作繼承類Sub,在delete Base*指針后,我們可以從結果得到只調用了基類的析構函數,但是派生類的析構函數並沒有調用,這相當於內存只是刪除了一一半,還有一半內存為刪除,導致了內存泄漏,此時則需要用到虛析構函數,來達到調用派生類的析構函數來刪除內存空間的目的,也就是下面這種情況:

 1 #include<iostream>
 2 using namespace std;
 3 class Base{
 4 public:
 5     Base()
 6     {
 7     }
 8     virtual ~Base()
 9     {
10         cout << "Base has deleted" << endl;
11     }
12 };
13 class Sub: public Base 
14 {
15 public:
16     Sub()
17     {
18     }
19     ~Sub()
20     {
21         cout << "Sub has delete" << endl;
22     }
23 };
24 void main()
25 {
26     Base* base = new Sub;
27     delete base;
28     base = NULL;
29 }

運行結果如下圖所示

此時我們可以看到在基類析構函數加了Virtual變成虛函數后,成功地調用了基類和派生類的析構函數,從而實現了基類指針指向內存的完全釋放。

虛析構函數工作的方式是:最底層的派生類的析構函數最先被調用,然后各個基類的析構函數被調用。

 

但是有一點要注意,當某個類沒有虛函數時,那么此類不會被當作基類,此時定義虛析構函數只會增加內存開銷,因為需要開啟一個虛函數列表來儲存。

 

以上只是個人的小總結,若有錯誤,歡迎前來糾正!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM