- 虛函數
- 虛析構函數
- 純虛函數與抽象類
多態實現的條件:(1)公有繼承 (2)派生類重寫基類虛函數 (3)基類指針/引用指向派生類對象
虛函數不能是構造函數,不能是靜態函數,不能是友元函數,只能是普通的成員函數。
其中,綁定的含義是什么?
綁定分為兩類,一類是靜態綁定,一類是動態綁定
靜態綁定:在編譯階段,普通成員函數、全局函數、重載函數這些都是靜態綁定
動態綁定:在運行階段,程序才能確定函數調用對應的具體函數,只有virtual聲明的虛函數,并且用基類指針引用調用時,此案呢個發生動態綁定。
綁定就是確定函數調用到底會執行哪一個函數體的過程。
靜態綁定就是在編譯時綁定,動態綁定就是在運行時綁定。
綁定就是“函數調用和函數實現之間的對應關系是何時確定”的過程。
?基類聲明了虛函數,產生虛函數表,將虛函數放入。類多一個隱含的虛函數指針,指向虛函數表。派生類繼承時,會更新虛函數指針,指向自己的虛函數表,派生類重寫的虛函數會放入派生類的虛函數表中。基類指針/引用指向派生類對象的時候,通過指針/引用調用虛函數時,會先通過派生類對象的虛函數指針到虛函數表中查找,如果有就會調用派生類的虛函數。
通過對象名無法實現多態。
#include<iostream>
using namespace std;class A{
public:virtual void func() {cout << "A::func" << endl;}
};class B : public A{
public:void func() {cout << "B::func" << endl;}
};int main()
{A* pa = new B();pa->func(); //多態,基類用指針調用派生類的虛函數A a;B b;a = b;a.func(); //不是多態,對象名無法調用派生類的成員函數,C++語法規則return 0;
}
補充:override/final
因為基類中聲明了虛函數,這個函數在派生類中即使不加virtual,它在派生類中仍然是虛函數。所以在類的多層繼承的情況下,一個成員函數很難看出是不是虛函數,可以通過override確保這個函數在某個基類中聲明過虛函數,那么他就是虛函數。
class A{
public: virtual void func() {cout << "A::func()" << endl;}void show() {cout << "A::show" << endl;}
};class B : public A{
public:void func() override {cout << "B::func" << endl;}void show() override {cout << "B::show" << endl;}
};
final: 修飾類時表示類不能被繼承;修飾虛函數時表示虛函數不能被重寫。
虛析構函數
?????????構造函數不能聲明為虛函數,原因是構造函數在對象創建完之前調用的,此時還沒有虛函數表和虛函數指針。
????????析構函數在對象釋放時調用,此時對象已經創建完成了,所以它有條件聲明為虛函數。而且,應該聲明成虛函數。
????????如果析構函數不是虛函數的話,基類指針指向派生類對象,通過基類指針釋放對象空間時,只會調用基類析構函數,而不會調用派生類析構函數,這樣會導致派生類中的動態內存單元不被釋放。
?
#include<iostream>
using namespace std;
class Base { //基類Base
public:virtual ~Base(); //虛析構函數
};Base::~Base() {cout << "Base類析構函數" << endl;
}class Derive :public Base { //派生類Derive公有繼承Base類
public:~Derive(); //虛析構函數
};Derive::~Derive()
{cout << "Derive類析構函數" << endl;
}int main() {Base* pb = new Derive; //基類指針指向派生類對象delete pb;return 0;
}
純虛函數與抽象類
當基類非常抽象時,它的函數代碼不好實現,不能只寫出函數原型,而不寫代碼,定義這個函數的目的,是為了規范它的所有派生類都必須要有的函數,它的作用相當于接口。
純虛函數定義的語法:
virtual 函數返回值類型函數名(參數列表) = ?0
如果一個類中包含純虛函數,這樣的類稱為抽象類。
抽象類的作用就是為了定義接口,方便實現多態。
抽象類不能創建對象,只能作為基類派生。
抽象類中如果有多個純虛函數,派生類沒有全部實現,此時派生類仍然是抽象類。
?
#include<iostream>
using namespace std;class Animal{ //動物類Animal
public:virtual void speak() = 0; //純虛函數virtual void eat() = 0; //純虛函數virtual ~Animal(); //析構函數
};Animal :: ~Animal()
{cout << "調用Animal析構函數" << endl;
}class Cat : public Animal{
public:void speak();void eat();~Cat();
};void Cat :: speak() {cout << "小貓喵喵叫" << endl;}
void Cat :: eat() {cout << "小貓吃魚" << endl;}
Cat :: ~Cat() {cout << "調用Cat析構函數" << endl;}
class Rabbit :public Animal { //兔子類Rabbit公有繼承Animal類
public:void speak(); //聲明speak()函數void eat(); //聲明eat()函數~Rabbit(); //聲明析構函數
};
void Rabbit::speak() { cout << "小兔子咕咕叫" << endl; }
void Rabbit::eat() { cout << "小兔子吃白菜" << endl; }
Rabbit::~Rabbit() { cout << "調用Rabbit析構函數" << endl; }int main() {Animal* pa = new Cat; //定義基類指針pC指向Cat類對象pa->speak(); //通過pC指針調用speak()函數pa->eat(); //通過pC指針調用eat()函數delete pa; //釋放指針pC指向的空間pa = new Rabbit; //定義基類指針pR指向Rabbit類對象pa->speak(); //通過pR指針調用speak()函數pa->eat(); //通過pR指針調用eat()函數delete pa; //釋放pR指向的空間return 0;
}