文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>公有继承的含义

公有继承的含义

时间:2010-09-21  来源:zyd_cu

公有继承体现“是一个”的含义,类D(Derived)从类B(Base)公有继承时,实际上是告诉编译器:类型D的每一个对象也是类型B的一个对象,但反之不成立。如Student类公有继承自Person类,说明每一个Student都是一个Person,但反之则不然,并不是每一个Person都是Student。

class Person { ... };
class Student: public Person { ... };

另外公有继承声明,对基类使用的任何东西也适应于派生类,如下例:正方形公有继承自矩形则不合适,对于矩形,长宽可以不等,但正方形长宽一定相等,makebigger对矩形适用,但对正方形不适用(长变了宽一定跟着变),故并不是对矩形适用的一切对正方形都使用,故这样的公有继承时不合理的。

class Rectangle {
public:
 virtual void setHeight(int newHeight);
 virtual void setWidth(int newWidth); virtual int height() const; // 返回当前值

 virtual int width() const;     // 返回当前值

 ...     
};     
void makeBigger(Rectangle& r)     // 增加r 面积的函数

{     
     int oldHeight = r.height(); r.setWidth(r.width() + 10);     // 对r 的宽度增加10
     assert(r.height() == oldHeight);      //     断言r 的高度未变
}         
    
class Square: public Rectangle { ... };

对于公有继承,接口总是被继承下来的,作为类的设计者,有时希望派生类只继承成员函数的接口(声明);有时希望派生类同时继承函数的接口和实现,但允许派生类改写实现;有时则希望同时继承接口和实现,并且不允许派生类改写任何东西。如下例:

class Shape {
public:
virtual void draw() const = 0;
virtual void error(const string& msg);
int objectID() const;
...
};
class Rectangle: public Shape { ... };
class Ellipse: public Shape { ... };

Shape类中声明了三个函数。第一个函数,draw,在某一画面上绘制当前对象。第二个函数,error,被其它成员函数调用,用于报告出错信息。第三个函数,objectID,返回当前对象的一个唯一整数标识符。每个函数以不同的方式声明:draw是一个纯虚函数;error是一个简单的虚函数;objectID是一个非虚函数。这些不同的声明各有什么含义呢?

对于纯虚函数draw,纯虚函数最显著的特征是:它们必须在继承了它们的任何具体类中重新声明,而且它们在抽象类中往往没有定义(但也可有定义),定义纯虚函数的目的在于,使派生类仅仅只是继承函数的接口。

这对Shape::draw函数来说非常有意义,因为,让所有Shape对象都可以被绘制是很合理,但Shape类无法为Shape::draw提供一个合理的缺省实现。例如,绘制椭园的算法就和绘制矩形的算法大不一样。打个比方来说,上面Shape::draw的声明就象是在告诉子类的设计者,你必须提供一个draw函数,但我不知道你会怎样实现它。

简单虚函数的情况和纯虚函数有点不一样。照例,派生类继承了函数的口,但简单虚函数一般还提供了实现,派生类可以选择改写它们或不改写它们思考片刻就可以认识到:声明简单虚函数的目的在于,使派生类继承函数的接口和缺省实现。

具体到Shape::error,这个接口是在说,每个类必须提供一个出错时可以被调用的函数,但每个类可以按它们认为合适的任何方式处理错误。如果某个类不想做什么特别的事,可以借助于Shape 类中提供的缺省出错处理函数。也就是说,Shape::error的声明是在告诉子类的设计者,你必须支持error 函数,但如果你不想写自己的版本,可以借助Shape类中的缺省版本。

对于非虚函数objectID。当一个成员函数为非虚函数时,它在派生类中的行为就不应该不同。实际上,非虚成员函数表明了一种特殊性上的不变性,因为它表示的是不会改变的行为,不管一个派生类有多特殊。所以,声明非虚函数的目的在于,使派生类继承函数的接口和强制性实现,并且决不要重新定义继承而来的非虚函数。如下例:

class B {
public:
void mf();
...
};
class D: public B { ... };

D x; // x 是类型D 的一个对象

B *pB = &x; // 得到x 的指针

pB->mf(); // 通过指针调用mf,调用B::mf

/* 上述的做法与下面的做法结果并不相同 */
D *pD = &x; // 得到x 的指针

pD->mf(); // 通过指针调用mf,调用D::mf

行为的两面性产生的原因在于,象B::mf 和D::mf这样的非虚函数是静态绑定的。这意味着,因为pB 被声明为指向B的指针类型,通过pB调用非虚函数时将总是调用那些定义在类B中的函数 ---- 即使pB指向的是从B派生的类的对象。

相反,虚函数是动态绑定的,因而不会产生这类问题。如果mf 是虚函数,通过pB或pD调用mf 时都将导致调用D::mf,因为pB和pD实际上指向的都是类型D的对象。

结论是,如果写类D时重新定义了从类B 继承而来的非虚函数mf,D的对象就可能表现出精神分裂症般的异常行为。即D的对象在mf被调用时,行为有可能象B,也有可能象D,决定因素和对象本身没有一点关系,而是取决于指向它的指针所声明的类型。引用也会和指针一样表现出这样的异常行为。故决不要重新定义继承而来的非虚函数

相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载