第一种方法是由于圆是椭圆的特殊情况,可以从椭圆类派生圆类,如下:
class Ellipse{
private:
double x,y; //(x,y)表示椭圆中心坐标
double a,b; //a,b表示椭圆长轴短轴长度
......
public:
void move(int nx,int ny){x=nx;y=ny;}
virtual double scale(){return 3.14159*a*b;}
......
};
class Cycle:public Ellipse{
......
};
但是相较于椭圆,圆可以用一个变量表示半径来代替椭圆的长短轴,在方法上也有许多需要重新定义的地方,实质上和直接写一个圆类的工作量差别大,直接写一个圆类代码如下:
class Cycle{
private:
double x,y; //(x,y)为圆心坐标
double r; //圆的半径
public:
void move(int nx,int ny){x=nx;y=ny;}
double scale(){return 3.14159*r^2;}
};
但是这样又没有表现出椭圆和圆之间的关系。
所以,还有一种解决方法为先从两个类之间抽象出它们的共同特性,把这些特性封装到一个抽象基类(ABC Abstract Base Class)中,然后椭圆和圆类分别是该抽象基类的派生类。
具体的来说,椭圆和圆的共有特性是中心坐标和move()以及scale()方法,把中心坐标封装到抽象基类中不难,只要在抽象基类中定义两个变量来储存它们,move()方法也不难,因为抽象基类内有储存它要修改的中心坐标变量,而scale()方法就不能这么做了,因为Ellipse::scale()要求的长短轴和Cycle::scale()要求的半径在抽象基类中没有定义,必须要等继承后才能声明,所以这就要用到纯虚函数(pure virtual function),这是一种单纯的占位函数,连形参都没有,除了函数名字和返回类型之外一切都要等被继承之后才能得到扩充。其具体实现方式如下:
class BaseEllipse{
private:
double x,y; //中心坐标
public:
void move(int nx,int ny){x=nx;y=ny;}
virtual double scale()=0; //用virtual关键字声明,没有形参,末尾添加 =0 表明其是纯虚函数。它只是一个形式上的函数
};
这样通过抽象基类还能实现用指向基类的指针数组来储存两个类的对象。
关于纯虚函数,还有几个特性:
①当类中包含纯虚函数时,不能创建该类的对象。包含纯虚函数的类只能用作基类。即抽象基类不能创建对象。
②必须至少包含一个纯虚函数才能成为真正的抽象基类。