c++學習之多態(虛函數和純虛函數)

        c++是面嚮對象語言,面向對象有個重要特點,就是繼承和多態。繼承之前學過了,就是一種重用類的設計方式。原有的類叫父類,或者基類,繼承父類的類叫子類。在設計模式中,我們總是要避免繼承,推薦用組合。因爲繼承會導致類爆炸。類太多了,就太累了。。。哈哈。。。

        說說多態,多態,從我們語言的語法上說,就是通過父類的指針能直接調用子類的方法,在父類的層面,無需瞭解子類的實現。在我理解來看,其實多態,也是解耦的一種實現方式,因爲子類的實現和父類沒有關係了,父類總是可以調用到相關的函數。

        但是如果我們父類有完整的一個實現,子類也有完整的實現,腫麼辦?父類肯定會調用自己的實現方法呀,一會我會講解這個例子的。爲了實現多態,c++語言引用了虛函數的概念。就是說,如果父類的某個函數是虛函數,那麼好辦,儘管你父類實現了這個虛函數也沒關係,我通過指針調用的時候,我也會直接去找子類的相關實現,從而達到多態的效果。這個虛函數就有點像java裏面的抽象方法了。但是留意細節哦....c++的虛函數,在父類也是可以實現的。我們來看一個完整的例子。

        1 創建一個BaseClass.h的頭文件,對父類BaseClass進行聲明。

class BaseClass
{
protected:
	int x;
	int y;
public:
	BaseClass();
	BaseClass(int a,int b);
	void area();
};

        2 創建一個BaseClazz.cpp源文件,對父類BaseClass進行實現。

#include <iostream>
#include <string>
#include "BaseClass.h"
using namespace std;

BaseClass::BaseClass()
{
	x = 1;
	y = 2;
}
BaseClass::BaseClass(int a,int b)
{
	x = a;
	y = b;
}
void BaseClass::area()
{
	cout << "baseclass.area() call...." << endl;
	cout << x*y <<endl;
}

        3 創建一個DeriveClass1.h的頭文件,對子類DeriveClass1進行聲明。

#include "BaseClass.h"
class DeriveClass1:public BaseClass
{
private:
	int z;
public:
	void area();
	void setValue(int a);
	DeriveClass1();
};

        4 創建一個DeriveClass1.cpp源文件,對子類進行實現。

#include <iostream>
#include "DeriveClass1.h"

using namespace std;

void DeriveClass1::area()
{
	cout << "DeriveClass1.area() call...." << endl;
	cout << x*y*z << endl;
}

void DeriveClass1::setValue(int a)
{
	z = a;
}

DeriveClass1::DeriveClass1()
{
	z = 3;
}

        看到沒有,父類子類都有一個area()函數,父類有兩個成員變量x,y,子類有三個x,y,z。

        5 創建一個Main.cpp源文件,對以上的代碼進行測試。

#include <iostream>
#include "DeriveClass1.h"
using namespace std;

int main()
{

	BaseClass *dc = new DeriveClass1();
	dc->area();
	return 1;
}

        6 好了,這個時候,由於父類和子類都對area函數進行了實現,通過父類的指針來調用area函數,想都不用想,調用的是父類的area函數,儘管將子類的地址賦值給了父類的指針。看結果。

                


        7 這樣一來,多態的效果達不到了麼,爲了達到多態,腫麼辦?我們將BaseClass.h頭文件中的area函數聲明爲virtual即可,其他地方均不變。看代碼,

class BaseClass
{
protected:
	int x;
	int y;
public:
	BaseClass();
	BaseClass(int a,int b);
	virtual void area();
};

        8 這樣一來,我們再次運行第六步的測試代碼,截圖如下:

                

        這就是父類定義了虛函數的結果,儘管父類實現了該虛函數,但是父類的指針在指向子類的情況下,還是會調用子類對應的函數。

        9 這個時候又出現一種情況,如果父類真的是沒必要實現這個area函數腫麼辦??如果你就這樣直接不實現,在連接的時候會報錯,哈哈。BaseClass.cpp代碼如下:

#include <iostream>
#include <string>
#include "BaseClass.h"
using namespace std;

BaseClass::BaseClass()
{
	x = 1;
	y = 2;
}
BaseClass::BaseClass(int a,int b)
{
	x = a;
	y = b;
}
void BaseClass::area();

        連接時報錯如下:

--------------------Configuration: DeriveDemo - Win32 Debug--------------------
Compiling...
BaseClazz.cpp
Skipping... (no relevant changes detected)
DeriveClass1.cpp
Linking...
BaseClazz.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall BaseClass::area(void)" (?area@BaseClass@@UAEXXZ)
Debug/DeriveDemo.exe : fatal error LNK1120: 1 unresolved externals
執行 link.exe 時出錯.

DeriveDemo.exe - 1 error(s), 0 warning(s)


        10 這個時候,純虛函數就派上用場了,我們只需要將虛函數的定義後面賦值爲0,即可。看看BaseClass.h頭文件的定義。

class BaseClass
{
protected:
	int x;
	int y;
public:
	BaseClass();
	BaseClass(int a,int b);
	virtual void area()=0;
};

        11 然後編譯鏈接執行。。執行結果如下:



        12 純虛函數和虛函數有什麼區別,我們通過上面的定義可以看出:

                虛函數定義:virtvual void area();

                純虛函數的定義:virtual void area()=0;

                虛函數,在父類中是必須實現的。

                純虛函數,在父類中是可以不用實現的。

          熟悉java的同學發現這個有沒有一點想抽象類和接口呀。哈哈。。






發佈了59 篇原創文章 · 獲贊 10 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章