【C++】多态(早期绑定、后期绑定)、抽象类(纯虚函数)、虚析构函数
我們都知道面向對象編程的三大特征是封裝、繼承、多態,今天我們就來說一下其中之一的多態。
概念:
多態:
多態字面意思就是多種形態,C++ 多態意味著調用成員函數時,會根據調用函數的對象的類型來執行不同的函數。(取自菜鳥教程)
多態分類:
多態分為編譯時多態(早期綁定)和運行時多態(后期綁定)
編譯時多態(早期綁定=靜態聯編)
概念:
? ? ? ? 通過函數重載和運算符重載來實現。
特點:
? ? ? ? 在編譯時我們就已經知道調用的是哪些個函數
看下面的代碼:
int Max(int a, int b) { return a > b ? a : b }
char Max(char a, char b) { return a > b ? a : b }
double Max(double a, double b) { return a > b ? a : b }int main()
{int x = Max(12, 23);char ch = Max('a', 'b');double de = Max(12.23, 34.45);return 0;
}
上述代碼我們通過重載函數Max來實現早期綁定,也就是在我們編譯的時候通過函數重載的機制就已經知道調用的是哪個函數,比如Max(12,23)參數為int整型類型的參數,所以我們就知道調用的是第一個int Max()這個函數。
運行時多態(動態綁定=動態聯編)
概念:
????????運行時的多態性是指在程序執行前,無法根據函數名和參數來確定該調用哪一個函數,必須在程序執行過程中,根據執行的具體情況來動態地確定。它是通過類繼承關系public和虛函數來實現的。
特點:
? ? ? ? 同一個方法在不同的對象身上體現出來不同的代碼形式,但是我們有統一的接口,例如下面代碼中的fun函數這個方法,在基類中和派生類中體現出來的方法不一樣!!!
? ? ? ? 就是說運行時多態是通過虛函數來實現的。虛函數在我上一篇說過:【C++】虛函數_神廚小福貴!的博客-CSDN博客虛函數是構成C++多態的重要一步,今天來說一下虛函數!虛函數:在基類(或父類)中,使用virtual關鍵字對函數進行聲明為并在一個或多個派生類(子類)中被重新定義的成員函數,通過指向派生類的基類指針或引用,訪問派生類中同名覆蓋成員函數。它的用法是這樣的:virtual + 函數返回類型 + 函數名 +(參數表) {函數體}首先我們要知道為什么要有虛函數這個東西?我來看下下面這兩段代碼的結果再來說結果:class A{public:void fun(){couthttps://blog.csdn.net/qq_45829112/article/details/123460960?spm=1001.2014.3001.5502
下面我那代碼來實現一下動態綁定:
class A
{
public:virtual void fun(){}
};class B :public A
{
public:virtual void fun(){cout << "class B::fun()" << endl;}
};class C :public A
{
public:virtual void fun(){cout << "class C::fun()" << endl;}
};void pun(A& a)
{a.fun();
}int main()
{B b;C c;pun(b);pun(c);return 0;
}
類B、C都公有繼承類A,然后創建bc對象,分別調用函數pun,當傳入參數為class B參數時 調用B中的婦女函數,傳參為class C時,調用類C中的fun函數!、
上述代碼解析如下圖所示:
運行結果:
這就是簡單的運行時的多態,相同的方法在不同的 不同對象下進行調用時,產生的結果也不盡相同,這也就是簡單的運行時的多態!!!
抽象類(純虛函數)
概念:
? ? ? ? 含有純虛函數的類稱為抽象類。純虛函數沒有實現部分,不能產生對象。
? ? ? ? 簡單來說就是抽象類(有純虛函數的類)不能定義對象!!!
????????virtual? 類型? 函數名(參數列表)= 0? 如下所示:
virtual void fun() { } = 0;
下面來說下純虛函數的特點:
? ? ? ??抽象類只能作為基類來使用,其純虛函數的實現由派生類給出。如果派生類沒有重新定義純虛函數,而派生類只是繼承基類的純虛函數,則這個派生類仍然還是一個抽象類。如果派生類中給出了基類純虛函數的實現,則該派生類就不再是抽象類了,它是一個可以建立對象的具體類了。
?
? ? ? ? 可能會有些抽象,舉個簡單例子就是說,你說你要去混社會去了,但是有社會這個實體這個對象嘛,顯然是沒有的,我們只能在社會上做某些事情,而沒有社會這個實體,大致就這么個意思!!!
那么純虛函數以及這個抽象類有什么作用呢???
class A
{
public:virtual void fun() {} = 0; //純虛函數
};class B :public A
{
public:virtual void fun() //必須要進行重寫,否則class B也是一個抽象類,不可以定義對象{cout << "class B::fun()" << endl;}
};class C :public A
{
public:virtual void fun() //必須要進行重寫,否則class C也是一個抽象類,不可以定義對象{cout << "class C::fun()" << endl;}
};
? ? ? ? 抽象類一般作為接口來使用,真正的實現都是在其派生類中進行實現,如上述代碼中所示,在基類A中的fun啥也不干,僅僅搞個純虛函數,真正的實現都是在其派生類class B和class C中來進行實現的。
析構函數為什么聲明為虛函數???
class A
{
public:A(int x = 10){cout << "A構造函數運行" << endl;}virtual void fun(){}~A(){cout << "~A運行" << endl;}
};class B :public A
{
public:B(int x = 10){cout << "B構造函數運行" << endl;}~B(){cout << "~B運行" << endl;}virtual void fun(){cout << "class B::fun()" << endl;}
};int main()
{A* op = new B(10);op->fun();delete op;op = nullptr;return 0;
}
觀察上述代碼,父類指針指向派生類對象,完成執行fun函數之后,我們對op指針所指向資源進行釋放以及置空,運行結果:
?為啥上面我們構造了兩次,最后釋放卻只釋放了一次呢?
? ? ? ? 原因是我們op是基類指針,指向派生類的對象,構造class B的無名對象的時候,先構造A的隱藏父對象,再構造B對象,但是我們釋放的時候因為是基類指針,所以只能找到基類的析構函數,所以我們派生類的對象就沒有進行釋放,可能會造成內存泄漏!!!
怎么解決這個問題呢?父類析構函數加虛聲明virtual:
class A
{
public:A(int x = 10){cout << "A構造函數運行" << endl;}virtual void fun(){}virtual ~A() //構造函數加virtual聲明{cout << "~A運行" << endl;}
};class B :public A
{
public:B(int x = 10){cout << "B構造函數運行" << endl;}~B(){cout << "~B運行" << endl;}virtual void fun(){cout << "class B::fun()" << endl;}
};int main()
{A* op = new B(10);op->fun();delete op;op = nullptr;return 0;
}
至于派生類的析構函數,我們可以加也可以不加,因為如果我們派生類還有派生類的話,就得加virtual聲明,下面我們來看運行結果:
我們在基類中將析構函數聲明為virtual虛函數,派生類相當于繼承了虛函數這個特性,所以析構的時候,就可以通過虛表指針來找到派生類中的虛函數來對派生類對象進行析構!!!
總結
以上是生活随笔為你收集整理的【C++】多态(早期绑定、后期绑定)、抽象类(纯虚函数)、虚析构函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 光遇兔子头饰要多少钱?
- 下一篇: 【C++】多线程互斥锁、条件变量