在介紹抽象類之前,我們先介紹一下純虛函數。
1.純虛函數
在基類中僅僅給出聲明,不對虛函數實現定義,而是在派生類中實現。這個虛函數稱爲純虛函數。普通函數如果僅僅給出它的聲明而沒有實現它的函數體,這是編譯不過的。純虛函數沒有函數體。
純虛函數需要在聲明之後加個=0;
class <基類名>
{
virtual <類型><函數名>(<參數表>)=0; ......
};
2.抽象類
含有純虛函數的類被稱爲抽象類。抽象類只能作爲派生類的基類,不能定義對象,但可以定義指針。在派生類實現該純虛函數後,定義抽象類對象的指針,並指向或引用子類對象。
1)在定義純虛函數時,不能定義虛函數的實現部分;
2)在沒有重新定義這種純虛函數之前,是不能調用這種函數的。
抽象類的唯一用途是爲派生類提供基類,純虛函數的作用是作爲派生類中的成員函數的基礎,並實現動態多態性。繼承於抽象類的派生類如果不能實現基類中所有的純虛函數,那麼這個派生類也就成了抽象類。因爲它繼承了基類的抽象函數,只要含有純虛函數的類就是抽象類。純虛函數已經在抽象類中定義了這個方法的聲明,其它類中只能按照這個接口去實現。
3.接口和抽象類的區別
1)C++中我們一般說的接口,表示對外提供的方法,提供給外部調用。是溝通外部跟內部的橋樑。也是以類的形式提供的,但一般該類只具有成員函數,不具有數據成員;
2)抽象類可以既包含數據成員又包含方法。
抽象類的實例
1.抽象類IShape作爲基類:只有頭文件,沒有實現文件
#ifndef SHAPE_H
#define SHAPE_H
#include
using std::string;
//interface
class IShape
{
public:
virtual float getArea()=0; //純虛函數,獲得面積
virtual string getName()=0; //純虛函數,返回圖形的名稱
};
#endif
2.派生類Circle類繼承自抽象類IShape:
Circle.h頭文件:
#ifndef CIRCLE_H
#define CIRCLE_H
#include"Shape.h"
class CCircle : public IShape //公有繼承自IShape類
{
public:
CCircle(float radius); //構造函數
public:
virtual float getArea(); //實現聲明實現兩個基類的函數,聲明的時候需要加virtual關鍵字,實現的時候就不需要加virtual關鍵字了。
virtual string getName();
private:
float m_fRadius; //派生類可以擁有自己的成員
};
#endif
Circle.cpp實現文件:
#include"Circle.h"
CCircle::CCircle(float radius)
:m_fRadius(radius) //使用構造函數的初始化列表初始化
{
}
float CCircle::getArea() / /實現實現兩個基類的函數
virtual string getName();
{
return 3.14 * m_fRadius * m_fRadius;
}
string CCircle::getName()
{
return "CCircle";
}
3. 派生類Rect類繼承自抽象類IShape:
Rect.h頭文件:
#ifndef RECT_H
#define RECT_H
#include"shape.h"
class CRect : public IShape
{
public:
CRect(float nWidth, float nHeight);
public:
virtual float getArea();
virtual string getName();
private:
float m_fWidth; //矩形類具有自己的兩個屬性,寬和高
float m_fHeight;
};
Rect.cpp實現文件:
#include"Rect.h"
CRect::CRect(float fWidth, float fHeight)
:m_fWidth(fWidth), m_fHeight(fHeight)
{
}
float CRect::getArea()
{
return m_fWidth * m_fHeight;
}
string CRect::getName()
{
return "CRect";
}
4.測試文件main.cpp:
#include
#include"Rect.h"
#include"Circle.h"
using namespace std;
int main()
{
IShape* pShape = NULL; //定義了一個抽象類的指針,注意抽象類不能定義對象但是可以定義指針
pShape = new CCircle(20.2); //基類指針指向派生類的對象
cout<getName()<<" "<getArea()<<endl;
delete pShape; //釋放了CCirle對象所佔的內存,但是指針是沒有消失的,它現在就是一個野指針,我們在使用之前必須對它賦值
pShape = new CRect(20, 10); //基類指針指向派生類的對象
cout<getName()<<" "<getArea()<<endl;
return 0;
}
運行結果如下:可以看到,我們使用父類的指針調用同一個函數,分別調用了這兩個派生類的對應函數,它根據指針指向的類型的不同來決定調用的方法。即使我們以後需要新增加幾個類,我們還是這種調用方法,這就是多態的巨大魅力。