P29-c++类继承-06抽象基类详细介绍

本文详细介绍了C++中的抽象基类(ABC),讨论了为何及如何使用ABC来处理特定的编程场景,如图形程序中的圆和椭圆类设计。通过示例展示了如何创建包含纯虚函数的ABC,并解释了如何利用ABC实现多态性和接口约定,以提高代码的组织性和可维护性。
摘要由CSDN通过智能技术生成

1. 抽象基类

至此,介绍了简单继承和较复杂的多态继承。接下来更为复杂的是抽象基类( abstract base class,ABC)。
我们来看一些可使用ABC的编程情况。
有时候,使用is-a规则并不是看上去的那样简单。例如,假设您正在开发一个图形程序,该程序会显示圆和椭圆等。
圆是椭圆的一个特殊情况一一长轴和短轴等长的椭圆。因此,所有的圆都是椭圆,可以从
Ellipse类派生出 Circle类。但涉及到细节时,将发现很多问题。
首先考虑 Ellipse类包含的内容。数据成员可以包括椭圆中心的坐标、半长轴(长轴的一半)、短半轴(短轴的一半)以及方向角(水平坐标轴与长轴之间的角度)。另外,还可以包括一些移动椭圆、返回椭圆面积、旋转椭圆以及缩放长半轴和短半轴的方法:

class Ellipse
{
   
private:
	double x; // x-coordinate of the ellipse"s center
	double y; //y-coordinate of the ellipses center
	double a; // semimajor axis半长轴
	double b; // semiminor axis半短轴
	double angle; // orientation angle in degrees
public:
	void Move(int nx, int ny) {
   x=nx; y=ny;}
	virtual double Area()const {
   return 3.14159 *a *b;}
	virtual void Rotate(double nang) {
   angle += nang;}
	virtual void Scale(double sa, double sb) {
   a *= sa; b *= sb;}
};

现在假设从 Ellipse类派生出一个 Circle类

class Circle : public Ellipse

虽然圆是一种椭圆,但是这种派生是笨拙的。例如,圆只需要一个值(半径)就可以描述大小和形状,并不需要有长半轴(a)和短半轴(b)。
Circle构造函数可以通过将同一个值赋给成员a和b来照顾这种情况,但将导致信息冗余。 angle参数和 Rotate()方法对圆来说没有实际意义:而 Scale()方法(顾名思义)会将两个轴作不同的缩放,将圆变成椭圆。可以使用一些技巧来修正这些问题,例如在 Circle类中的私有
部分包含重新定义的 Rotate()方法,使 Rotate()不能以公有方式用于圆。
但总的来说,不使用继承,直接定义 Circle类更简单:

class Circle
{
   
//no inheritance
private:
	double x; //x-coordinate of the circlets center
	double y; //y-coordinate of the circlets center
	double r; // radius
public:
	void Move(int nx, int ny)  {
   x =nx; y=ny;}
	double Area()const( return 3.14159*r+ ri
	void Scale(double sr) {
    r *= sr; }
}

现在,类只包含所需的成员。但这种解决方法的效率也不高。 Circle i和 Ellipse类有很多共同点,将它们分别定义则忽略了这一事实还有一种解决方法,即从 Ellipse和 Circle类中抽象出它们的共性,将这些特性放到一个ABC中。然后从该ABC派生出 Circle和 Ellipse类。这样,便可以使用基类指针数组同时管理 Circle和 Ellipse对象,即可以使用多态方法)。在这个例子中,这两个类的共同点是中心坐标、Move()方法(对于这两个类是相同的)和Area()方法(对于这两个类来说,是不同的)。
确实,甚至不能在ABC中实现Area()方法,因为它没有包含必要的数据成员。C++通过使用纯虚函数( pure virtual function)提供未实现的函数。

纯虚函数声明的结尾处为=0,参见Area()方法:

class BaseEllipse // abstract base class
{
   
private:
	double x; //x-coordinate of center
	double y; //y-coordinate of center
public:
	BaseEllipse(double x0 =0, double yo=0) : x(x0), y(y0) {
   }
	virtual ~BaseEllipse ()
	void Move(int nx, int ny) {
   x = xn, y = ny;}
	virtual double Area() const=0; // a pure virtual function
}

当类声明中包含纯虚函数时,则不能创建该类的对象。
这里的理念是,包含纯虚函数的类只用作基类。
要成为真正的ABC,必须至少包含一个纯虚函数。原型中的=0使虚函数成为纯虚函数。
这里的方法Area( )没有定义,但C++甚至允许纯虚函数有定义。例如,也许所有的基类方法都与Move()一样,可以在基类中进行定义,但您仍需要将这个类声明为抽象的。在这种情况下,可以将原型声明为虚的:

void Move(int nx, int ny) = 0;

这将使基类成为抽象的,但您仍可以在实现文件中提供方法的定义

void BaseEllipse::Move(int nx, ny) {
   x=nx; y=ny;}

总之,在原型中使用=0指出类是一个抽象基类,在类中可以不定义该函数。

现在,可以从 BaseEllipse 类派生出 Ellips类和 Circle类,添加所需的成员来完成每个类。需要注意的点是, Circle类总是表示圆,而 Ellipse类总是表示椭圆一也可以是圆。然而, Ellipse类圆可被重新缩放为非圆,而 Ciecle类圆必须始终为圆。
使用这些类的程序将能够创建 Ellipse对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值