c++|多態
- 1 多態的概念
- 2 多態的定義及其實現
- 2.1 滿足多態的條件
- 2.2 虛函數
- 2.3 虛函數的重寫
- 2.4 析構函數適合加virtural嗎
- 2.4 C++11 override 和 final
- 2.5 三個概念的對比
- 3 多態的原理
- 4 抽象類
- 4.1 概念
- 4.2 純虛函數
1 多態的概念
多態的概念:通俗來說,就是多種形態,具體點就是去完成某個行為,當不同的對象去完成時會產生出不同的狀態。
2 多態的定義及其實現
```cpp
class Person {
public:virtual void BuyTicket() { cout << "買票-全價" << endl; }
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "買票-半價" << endl; }/*注意:在重寫基類虛函數時,派生類的虛函數在不加virtual關鍵字時,雖然也可以構成重寫(因為繼承后基類的虛函數被繼承下來了在派生類依舊保持虛函數屬性),但是該種寫法不是很規范,不建議這樣使用*//*void BuyTicket() { cout << "買票-半價" << endl; }*/
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person ps;Student st;Func(ps);Func(st);return 0;
}
2.1 滿足多態的條件
那么在繼承中要構成多態還有兩個條件:
- 必須通過基類的指針或者引用調用虛函數
- 被調用的函數必須是虛函數,且派生類必須對基類的虛函數進行重寫
2.2 虛函數
即被virtural修飾的類成員函數
2.3 虛函數的重寫
虛函數的重寫(覆蓋):派生類中有一個跟基類完全相同的虛函數(即派生類虛函數與基類虛函數的
返回值類型、函數名字、參數列表完全相同),稱子類的虛函數重寫了基類的虛函數。
有兩個例外:
-
協變(基類與派生類虛函數返回值類型不同)
基類虛函數返回基類對象的指針或者引用,派生類虛函數返回派生類對象的指針或者引用時,稱為協變。(了解) -
如果基類的析構函數為虛函數,此時派生類析構函數只要定義,無論是否加virtual關鍵字,
都與基類的析構函數構成重寫,雖然基類與派生類析構函數名字不同。雖然函數名不相同,
看起來違背了重寫的規則,其實不然,這里可以理解為編譯器對析構函數的名稱做了特殊處理,編譯后析構函數的名稱統一處理成destructor
協變
class Person {
public:virtual A* f() { cout << "Person: " << endl;return new A; }
};
class Student : public Person {
public:virtual B* f() { cout << "Student:" << endl;;return new B; }
};
void Func(Person& p)
{p.f();
}
int main()
{Person p;Student s;Func(p);Func(s);return 0;
}
2.4 析構函數適合加virtural嗎
class Person {
public:~Person() {cout << "~Person" << endl;}
};class Student : public Person {int* ptr = new int[10];
public:~Student() {delete ptr;cout << "~Student" << endl;}
};int main()
{Person * p1 = new Person();Person* p2 = new Student();// 都調用 Persondelete p1;delete p2;return 0;
}
所以我們建議加上 vitural
2.4 C++11 override 和 final
final:修飾虛函數,表示該虛函數不能再被重寫
class Car
{
public:virtual void Drive() final {}
};
class Benz :public Car
{
public:virtual void Drive() {cout << "Benz-舒適" << endl;}
};
- override: 檢查派生類虛函數是否重寫了基類某個虛函數,如果沒有重寫編譯報錯。
class Car{
public:virtual void Drive(){}
};
class Benz :public Car {
public:virtual void Drive() override {cout << "Benz-舒適" << endl;}
};
2.5 三個概念的對比
3 多態的原理
class Person {
public:virtual void BuyTicket() { cout << "買票-全價" << endl; }int _i = 1;
};
class Student : public Person {
public:virtual void BuyTicket() { cout << "買票-半價" << endl; }int _j = 2;
};
void Func(Person& p)
{p.BuyTicket();
}
int main()
{Person Mike;Func(Mike);Student Johnson;Func(Johnson);return 0;
}
每個有虛函數的對象,都有個一個虛函數表指針,每個虛函數都進入了虛函數表中。
- 觀察下圖的紅色箭頭我們看到,p是指向mike對象時,p->BuyTicket在mike的虛表中找到虛
函數是Person::BuyTicket。 - 觀察下圖的藍色箭頭我們看到,p是指向johnson對象時,p->BuyTicket在johson的虛表中
找到虛函數是Student::BuyTicket。 - 這樣就實現出了不同對象去完成同一行為時,展現出不同的形態
滿足多態以后的函數調用,不是在編譯時確定的,是運行起來以后到對象的中取找的。不滿足多態的函數調用時編譯時確認好的。
驗證每個虛函數都進入了函數指針表中
class Base {
public:virtual void func1() { cout << "Base::func1" << endl; }virtual void func2() { cout << "Base::func2" << endl; }
private:int a;
};
class Derive :public Base {
public:virtual void func1() { cout << "Derive::func1" << endl; }virtual void func3() { cout << "Derive::func3" << endl; }virtual void func4() { cout << "Derive::func4" << endl; }
private:int b;
};
typedef void (*VFPTR) () ;
VFPTR p2[10];
void PrintVFT(VFPTR* arr)
{for (int i = 0; i < 4; i++) {printf("%p\n", arr[i]);VFPTR pf = arr[i];(*pf)();}}int main()
{Derive d;VFPTR* ptr = (VFPTR*)(*(int*)(&d));PrintVFT(ptr);return 0;
}
4 抽象類
4.1 概念
有純虛函數的類稱為抽象類,抽象類不能實例化。
派生類繼承后也不能實例化出對象,只有重寫純虛函數,派生類才能實例化出對象。
4.2 純虛函數
在虛函數的后面寫上 =0 ,則這個函數為純虛函數。
class Car
{
public:
virtual void Drive() = 0;
};
class Benz :public Car
{
public:virtual void Drive(){cout << "Benz-舒適" << endl;}
};
class BMW :public Car
{
public:virtual void Drive(){cout << "BMW-操控" << endl;}
};
void Test()
{
Car* pBenz = new Benz;pBenz->Drive();Car* pBMW = new BMW;pBMW->Drive();
}