1.抽象类概念
C++是一门面向对象的编程语言,而所有的对象都是通过类来描述的,如果一个类没有足够的信息来描述一个具体的对象,这样的类就是抽象类。换句话说,类是具有相同或相似结构、操作和约束规格的对象组成的结合。而对象是某一类的具体实例化。每一个类都是具有某些共同特征的对象的抽象。
例如,我们可以将所有的图形归为图形类,图形类即为一个抽象类(父类),但是对于具体的图形是三角形、四边形或者其他图形,无法准确描述。但是三角形、四边形等又都是继承于图形类的子类。具体是哪一类四边形又可分为正方形、平行四边形等。
实际开发中,并不需要将父类初始化为对象,需要的是子类对象,图形类(父类)不能抽象出任何一种图形,但子类却可以。
简单说,父类只是给出了一个模糊的概念,定义了方法的名称,以纯虚函数方式展示,却并没有给出方法的实现过程,而具体的实现过程,在继承于父类的子类中又大不相同。
1.1、抽象类如何使用
在使用抽象类之前,需要了解为什么要用它。如果是自己独自开发代码,得明白什么情况下、什么地方需要加抽象类;如果是在其他人开发的基础上进行二次开发,那也得知道别人为什么需要在此处加抽象类,不加行不行。
例如在工业控制中,有A、B两种运动控制轴卡可以选择,两种轴卡可以实现相同的功能,如果机台装的是A轴卡,那就启动A轴卡,如果装的是B轴卡,那就启用B轴卡。但是我的程序只有一套,即需要兼容效果,如果后面还需要采购C、D轴卡呢?这个时候就可以引用抽象类的概念,抽象类中定义基础运动控制方式名称,但是不实现,实现方式在不同轴卡中。在启用前,只需要判断是哪种轴卡,后续的代码实现运动控制过程调用的方法名称一样。避免了代码中大量修改。
1.2、抽象类规定
(1)抽象类只能用作其他类的基类,不能建立抽象类对象。
(2)抽象类不能用作参数类型、函数返回类型或显式转换的类型。
(3)可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。这一条很有用,用抽象类方法名称去兼容子类。
2.抽象类代码示例
搞清除抽象类概念后,以下通过一个简单例子加深对抽象类的理解,例子代码来源:C++的抽象类详解
2.1、抽象类
定义抽象类IShape,抽象类IShape作为基类:只有头文件,没有实现文件
#ifndef SHAPE_H
#define SHAPE_H
#include <string>
using std::string;
//interface
class IShape
{
public:
virtual float getArea() = 0; //纯虚函数,获得面积
virtual string getName() = 0; //纯虚函数,返回图形的名称
};
#endif
2.2、子类1
基于此抽象类的一个子类Circle
头文件:
#ifndef CIRCLE_H
#define CIRCLE_H
#include"IShape.h"
class CCircle : public IShape //公有继承自IShape类
{
public:
CCircle(float radius); //构造函数
public:
virtual float getArea(); //实现声明实现两个基类的函数,声明的时候需要加virtual关键字,实现的时候就不需要加virtual关键字了。
virtual string getName();
private:
float m_fRadius; //派生类可以拥有自己的成员
};
#endif
cpp实现文件
#include"Circle.h"
CCircle::CCircle(float radius)
:m_fRadius(radius) //使用构造函数的初始化列表初始化
{
}
string CCircle::getName()
{
return "CCircle";
}
float CCircle::getArea() //实现两个基类的函数
{
return 3.14* m_fRadius* m_fRadius;
}
2.3、子类2
基于抽象类的另一个子类:CRect
头文件
#ifndef RECT_H
#define RECT_H
#include"IShape.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;
};
#endif
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";
}
2.4、程序入口
#include"Rect.h"
#include"Circle.h"
using namespace std;
int main()
{
IShape* pShape = NULL; //定义了一个抽象类的指针,注意抽象类不能定义对象但是可以定义指针
pShape = new CCircle(20.2); //基类指针指向派生类的对象
cout << pShape->getName() << " " << pShape->getArea() << endl;
delete pShape; //释放了CCirle对象所占的内存,但是指针是没有消失的,它现在就是一个野指针,我们在使用之前必须对它赋值
pShape = new CRect(20, 10); //基类指针指向派生类的对象
cout << pShape->getName() << " " << pShape->getArea() << endl;
return 0;
}
上述例子中,定义了一个图形基类IShape,只有头文件,基类中定义两种方法,计算面积和返回图形名称,但并没有给出实现方式,也不知道是什么图形,无法实现。圆形类CCircle和矩形类CRect继承于基类IShape,分别实现了面积的计算与返回图形名称两种方法。
在程序入口,以指针pShape 的方式,指代基类IShape,而基类指针再指向子类。