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的同學發現這個有沒有一點想抽象類和接口呀。哈哈。。