類中聲明函數成員的時候,在函數的前面加上virtual
關鍵字,則該成員為虛函數
虛函數的特點
- 如果在類中定義的虛函數,那么系統會為這個類維護一個虛函數表
- 類中會多出4個字節的指針去指向這個虛函數表,在虛函數表中保存了虛函數的首地址,在調用虛函數的時候就會先到表中查找虛函數,然后調用,就比普通函數多了步操作
- 虛函數表不會被繼承,但是表中的項會被繼承(虛函數會被繼承)
虛函數的作用
通過類的繼承及虛函數來實現多態
- 有兩個類,他們之間是父子關系
- 子類和父類有同名的虛函數,但是功能不同
- 父類指針或引用指向父類或子類對象,指向哪個對象就可以調用哪個對象的虛函數成員
示例:
#include<iostream>
using namespace std;class Animal {
public:virtual void speak() {cout << "動物在說話" << endl;}
};class Cat :public Animal {
public:virtual void speak() {//子類重寫父類的虛函數cout<<"貓在喵喵叫" << endl;}
};
int main() {//調用:父類指針指向子類對象Animal* p = new Cat;p->speak();//調用的是Cat的speak()delete p;
}
多態下釋放的注意事項
基類中有虛函數時,且通過父類指針去分配子類對象的時候,在釋放的時候只能通過父類指針去進行釋放,在delete父類指針的時候會調用父類的析構函數,而不會調用子類的析構函數;需要把父類的析構函數定義為虛析構,那么在釋放父類指針的時候也就會調用子類的析構函數了。
以上面示例為例:
#include<iostream>
using namespace std;class Animal {
public:virtual ~Animal() {cout << "調用了父類析構" << endl;}virtual void speak() {cout << "動物在說話" << endl;}
};class Cat :public Animal {
public:~Cat() {cout << "調用了子類析構" << endl;}virtual void speak() {//子類重寫父類的虛函數cout<<"貓在喵喵叫" << endl;}
};
int main() {//調用:父類指針指向子類對象Animal* p = new Cat;p->speak();//調用的是Cat的speak()delete p;
}
輸出:
貓在喵喵叫
調用了子類析構
調用了父類析構
注意:構造函數不能是虛函數
純虛函數
純虛函數是一種特殊的虛函數。在基類中不能為虛函數給出有意義的實現,就可以把它聲明為純虛函數,然后把它的實現留給派生類去完成。
定義:virtual 函數返回值類型 函數名(函數參數)=0;
在C++中沒有interface
關鍵字,“接口”的概念靠純虛函數實現
示例:
#include<iostream>
#include<string>class Printable {
public:virtual std::string GetID() = 0;void PrintID() {std::cout << this->GetID() << std::endl;}
};class Student :public Printable {
public:std::string StuID = "S123456";std::string GetID() override {return StuID;}
};class Teacher :public Printable{
public:std::string TeaID = "T654321";std::string GetID() override {return TeaID;}
};int main() {Student stu;stu.PrintID();Teacher tea;tea.PrintID();
}
Student類和Teacher類可以看作是實現了“Printable接口”
純虛析構
析構函數也可以聲明成純虛析構,類內寫聲明,不用實現;但要在類外實現。
virtual ~類名()=0;//純虛析構的類內聲明
類名::~類名(){};//純虛析構的類外實現
抽象類
- 在一個類中具有一個及以上純虛函數,那么這個類被稱之為抽象類
- 抽象類不能實例化對象,但是可以定義指針,只能作為基類為派生類服務
- 如果派生類中沒有完全實現基類中所有的純虛函數,那么該派生類也會變成抽象類,同樣不能實例化對象
final
- 可以阻止成員函數的重寫:
virtual void fun() final
- 可以阻止類的繼承:
class C1 final
注:一個抽象類被阻止繼承,那么這個類就沒有任何作用(一般不會這樣寫)