標籤:c++ 多態 對象 virtual 虛函數
1.繼承和覆寫
子類繼承父類,子類可以覆寫基類的函數。當然,直接產生一個子類的對象,用子類的指標操作是沒有問題的。調用的函數是子類的函數,即覆寫後的。
// ConsoleApplication3.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>using namespace std;class Base{public:Base(void){cout<<"Base is created"<<endl;}virtual ~Base(void){cout<<"Base destroyed!"<<endl;}void func(){cout<<"Base function is called"<<endl;}};class Demo : public Base{public:Demo(void){cout<<"Demo is created"<<endl;}~Demo(void){cout<<"Demo destroyed!"<<endl;}void func(){cout<<"Demo function is called"<<endl;}};int _tmain(int argc, _TCHAR* argv[]){Demo* demo = new Demo();demo->func();int a;cin>>a;return 0;}
結果:
Base is created
Demo is created
Demo function is called
但是,如果要用父類的指標調用子類的對象呢?還是會調用覆寫後的函數嗎?
答案是不會。。。
// ConsoleApplication3.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>using namespace std;class Base{public:Base(void){cout<<"Base is created"<<endl;}virtual ~Base(void){cout<<"Base destroyed!"<<endl;}void func(){cout<<"Base function is called"<<endl;}};class Demo : public Base{public:Demo(void){cout<<"Demo is created"<<endl;}~Demo(void){cout<<"Demo destroyed!"<<endl;}void func(){cout<<"Demo function is called"<<endl;}};int _tmain(int argc, _TCHAR* argv[]){Base* base = new Demo();base->func();//Demo* demo = new Demo();//demo->func();//delete base;int a;cin>>a;return 0;}
Base is created
Demo is created
Base function is called
那麼要腫麼辦呢?
下面就是多態的方法啦。
2.關於多態
多態實際上就是用基類的指標來操作子類的對象。但是上面覆寫了之後,卻調用的還是父類的函數。要解決這個問題就要加一個關鍵字,virtual。
就是這一個關鍵字就改變了程式啟動並執行結果。
// ConsoleApplication3.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>using namespace std;class Base{public:Base(void){cout<<"Base is created"<<endl;}virtual ~Base(void){cout<<"Base destroyed!"<<endl;}void virtual func(){cout<<"Base function is called"<<endl;}};class Demo : public Base{public:Demo(void){cout<<"Demo is created"<<endl;}~Demo(void){cout<<"Demo destroyed!"<<endl;}void func(){cout<<"Demo function is called"<<endl;}};int _tmain(int argc, _TCHAR* argv[]){Base* base = new Demo();base->func();//Demo* demo = new Demo();//demo->func();//delete base;int a;cin>>a;return 0;}
在要被覆寫的基類函數前面加上virtual關鍵字就可以使父類指標調用子類對象的重寫的函數。
結果:
Base is created
Demo is created
Demo function is called
但是還有一種情況,就是即使覆寫了父類的函數,但仍然需要父類的函數的功能,這要腫麼辦呢?
最開始我的想法是從父類的Ctrl+c 然後Ctrl+v過來。後來一想,這個也忒麻煩了吧,而且不利於代碼的維護。還好,有這樣一個簡單的方法。
在子類覆寫的函數中,加上這句
Base::func();
即調用了父類的函數,然後再加上子類特有的功能即可。
// ConsoleApplication3.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>using namespace std;class Base{public:Base(void){cout<<"Base is created"<<endl;}virtual ~Base(void){cout<<"Base destroyed!"<<endl;}void virtual func(){cout<<"Base function is called"<<endl;}};class Demo : public Base{public:Demo(void){cout<<"Demo is created"<<endl;}~Demo(void){cout<<"Demo destroyed!"<<endl;}void func(){Base::func();cout<<"Demo function is called"<<endl;}};int _tmain(int argc, _TCHAR* argv[]){Base* base = new Demo();base->func();//Demo* demo = new Demo();//demo->func();//delete base;int a;cin>>a;return 0;}
Base is created
Demo is created
Base function is called
Demo function is called
這樣就能既使用子類的特有功能,又調用了父類的功能。
3..虛解構函式
如果是基類,沒有定義為虛解構函式的話,用基類指標操作子類,不會調用子類對象的解構函式,會導致只釋放了基類部分的資源,而定義在子類部分的資源沒被釋放,造成記憶體泄露。
// ConsoleApplication3.cpp : 定義控制台應用程式的進入點。//#include "stdafx.h"#include <iostream>using namespace std;class Base{public:Base(void){cout<<"Base is created"<<endl;}virtual ~Base(void){cout<<"Base destroyed!"<<endl;}};class Demo : public Base{public:Demo(void){cout<<"Demo is created"<<endl;}~Demo(void){cout<<"Demo destroyed!"<<endl;}};int _tmain(int argc, _TCHAR* argv[]){Base* base = new Demo();delete base;int a;cin>>a;return 0;}運行結果:
Base is created
Demo is created
Demo destroyed!
Base destroyed!
子類物件建構時,會先調用父類的建構函式,然後調用子類的建構函式,初始化子類的特有部分。析構時,順序相反,先調用子類的解構函式,再調用父類的解構函式。
建構函式和解構函式中都是預設調用父類的函數的。不用像上面那樣要額外加上調用父類函數的句子。
徹底搞懂C++多態虛函數等問題