C++学习之抽象基类

抽象基类和纯虚函数

至此,已经介绍了简单继承和较为复杂的多态继承。接下来介绍抽象基类(Abstract base class, ABC)

有些时候,继承关系并不是is-a那样简单。例如在开发图形程序时,可能会涉及到椭圆和圆,会有Ellipse类和Circle类。因为所有的圆都是特殊的椭圆,所以考虑从Ellipse类派生出Circle类。但涉及到细节时,会有很多问题。椭圆Ellipse类包含的内容有:椭圆中心的横纵坐标(x,y)、长半轴长(a)、短半轴长(b)、方向角(angle)。方法包括:移动椭圆、求椭圆面积、旋转椭圆(Rotate)、缩短或放长长短半轴。

虽然圆是一种椭圆,但如果直接用Ellipse类派生Circle类是笨拙的。首先,圆只需要一个值——半径——就可以描述大小和形状,不需要长半轴长和短半轴长,也不需要方向角angle和方法Rotate(),因为这些东西对圆来说完全没有意义。所以与其派生,不如直接定义Circle类更简单。但是这种解决方式也不是最好的,忽略了CircleEllipse实际上有很多共同点的事实。分别定义忽略了这一事实。

因此,还有一种解决方法:从EllipseCircle类中抽象出它们的共性,将这些特效放到一个抽象基类,Abstract base class, ABC里头,再由ABC派生出Ellipse和Circle类。这样就可以实现用基类指针数组同时管理Ellipse类和Circle类。C++通过纯虚函数(pure virtual function)来提供未实现的函数。

1
2
3
4
5
6
7
8
9
10
11
12
class BaseEllipse
{
private:
double x;
double y;

public:
BaseEllipse(double x0 = 0, double y0 = 0):x(x0),y(y0) {}
virtual ~BaseEllipse() {}
void Move(int nx, int ny) { x = nx; y = ny; }
virtual double Area()const = 0;
};

注意Area方法。在函数原型的末尾加上=0使虚函数成为纯虚函数。这里的Area方法没有定义。这里的概念是:包含纯虚函数的类只用作基类。当类声明中包含纯虚函数时,不能创建该类对象

现在,可以用BaseEllipse类派生Ellipse类和Circle类。使用这些类的程序能够创建Ellipse类对象和Circle类对象,但不能创建BaseEllipse对象。由于Ellipse类和Circle类有共同的基类,所以可以用BaseEllipse指针数组同时管理这两种对象。所以说:ABC描述的是至少使用一个纯虚函数的接口。

在处理继承的问题上,抽象基类的方法更具有系统性和规范性。可以将抽象基类设计人员能够制定“接口约定”,确保从抽象基类派生的所有组件都至少支持抽象基类所制定的功能。