- 在成員函數中調用虛函數
#include <iostream>
using namespace std;
class CBase
{
public:void func1(){func2();}virtual void func2() {cout << "CBase::func2()" << endl;}
};
class CDerived:public CBase
{
public:virtual void func2() { cout << "CDerived:func2()" << endl; }
};
int main()
{CDerived d;d.func1();return 0;
}
輸出結果:
CDerived:func2()
分析:
- 第 20 行調用 func1 成員函數。進入 func1 成員函數,執行到第 8 行,調用 func2 函數。看起來調用的應該是 CBase 類的 func2 成員函數,但輸出結果證明實際上調用的是 CDerived 類的 func2 成員函數。
- 這是因為,在 func1 函數中,func2();等價于this -> func2();,而 this 指針顯然是 CBase* 類型的,即是一個基類指針,那么this -> func2();就是在通過基類指針調用虛函數,因此這條函數調用語句就是多態的。
當本程序執行到第 8 行時,this 指針指向的是一個 CDerived 類的對象,即 d,因此被調用的就是 CDerived 類的 func2 成員函數。
- 在構造函數和析構函數中調用虛函數
#include <iostream>
using namespace std;
class A
{
public:virtual void hello() { cout << "A::hello" << endl; }virtual void bye() { cout << "A::bye" << endl; }
};
class B : public A
{
public:virtual void hello() { cout << "B::hello" << endl; }B() { hello(); }~B() { bye(); }
};
class C : public B
{
public:virtual void hello() { cout << "C::hello" << endl; }
};
int main()
{C obj;return 0;
}
輸出結果:
B::hello
A::bye
- 類 A 派生出類 B,類 B 派生出類 C。
- 第 23 行,obj 對象生成時會調用類 B 的構造函數,在類 B 的構造函數中調用 hello 成員函數。由于在構造函數中調用虛函數不是多態,所以此時不會調用類 C 的 hello 成員函數,而是調用類 B 自己的 hello 成員函數。
- obj 對象消亡時,會引發類 B 析構函數的調用,在類 B 的析構函數中調用了 bye 函數。類B沒有自己的 bye 函數,只有從基類 A 繼承的 bye 函數,因此執行的就是類 A 的 bye 函數。
- 將在構造函數中調用虛函數實現為多態是不合適的。以上面的程序為例,obj 對象生成時,要先調用基類構造函數初始化其中的基類部分。在基類構造函數的執行過程中,派生類部分還未完成初始化。此時,在基類 B 的構造函數中調用派生類 C 的 hello 成員函數,很可能是不安全的。
- 為什么基類中的析構函數要聲明為虛析構函數?
直接的講,C++中基類采用virtual虛析構函數是為了防止內存泄漏。具體地說,如果派生類中申請了內存空間,并在其析構函數中對這些內存空間進行釋放。假設基類中采用的是非虛析構函數,當刪除基類指針指向的派生類對象時就不會觸發動態綁定,因而只會調用基類的析構函數,而不會調用派生類的析構函數。那么在這種情況下,派生類中申請的空間就得不到釋放從而產生內存泄漏。所以,為了防止這種情況的發生,C++中基類的析構函數應采用virtual虛析構函數。
測試代碼:
#include <iostream>
#include <memory>
using namespace std;class Base {
public:Base() { cout << "Base Constructor" << endl; }~Base() { cout << "Base Destructor" << endl; }
};class Derived : public Base {
public:Derived() { cout << "Derived Constructor" << endl; }~Derived() { cout << "Derived Destructor" << endl; }
};int main()
{Base* p = new Derived();delete p;return 0;
}
輸出結果:
Base Constructor
Derived Constructor
Base Destructor
測試代碼
#include <iostream>
#include <memory>
using namespace std;class Base {
public:Base() { cout << "Base Constructor" << endl; }virtual ~Base() { cout << "Base Destructor" << endl; }
};class Derived : public Base {
public:Derived() { cout << "Derived Constructor" << endl; }virtual ~Derived() { cout << "Derived Destructor" << endl; }
};int main()
{Base* p = new Derived();delete p;return 0;
}
輸出結果:
參考資料
- C++調用虛函數的注意事項