`

《认清C++语言》---继承而来的非虚函数和缺省参数

 
阅读更多

======================继承而来的非虚函数======================

假设类Derive公有继承自类Base,且类Base定义了一个公有非虚成员函数func

class Base

{

public:

void Func();

...

};

class Derive : public Base

{

...

}

Derive de; //定义一个派生类对象

Base *pb = &de; //得到一个指向deBase指针

pb->Func(); //通过指针调用Func()

Derive *pd = &de; //得到一个指向deDerive指针

pd->Func(); //通过指针调用Func()

这种情况下,如果Derive没有重新定义Func函数时,两个指针调用Func的行为是相同的;但是,如果Func是非虚函数且Derive又重新定义了自己的Func版本,那么两者的行为就不会相同了:

class Derive : public Base

{

public:

void Func(); //隐藏了Base::Func;

...

};

pb->Func(); //调用Base::Func()

pd->Func(); //调用Derive::Func()

这种行为的两面性原因在于:Base::Func()Derive::Func()这样的非虚函数是静态绑定的。因此,即使pb指向的是从Base派生的类的对象,但由于pb被声明为执行Base的指针类型,所以通过pb调用非虚函数时就总是调用那些定义在类Base中的函数。

与之相反,虚函数是动态绑定的,不会存在上述问题。通过pbpd调用Func()时都将导致调用Derive::Func(),因为pbpd实际上都是指向类型Derive的对象。

结论:如果写派生类Derive时重新定义从基类Base继承而来的非虚函数Func()Derive的对象就可能表现出精神分裂的症状。引用也会和指针一样表现出这种异常行为。

因此,任何情况下都要禁止重新定义继承而来的非虚函数。

======================继承而来的缺省参数值=====================

重定义缺省参数值的唯一方法是重定义一个继承而来的函数,而由上面分析可知,重定义继承而来的非虚函数是错误的,因此,我们这里主要讨论“继承一个有缺省参数值的虚函数”。

虚函数是动态绑定的而缺省参数值是静态绑定的。

对象的静态类型是指我们声明的存在于程序代码文本中的类型:

enum ASCEShapeColor{RED, GREEN, BLUE};

class ASCEShape //基类

{

public:

//所有形状都要提供这样一个函数绘制自身

virtual void draw(ASCEShapeColor color=RED) const = 0;

...

};

class ASCERectangle : public ASCEShape

{

public:

//定义了不同的缺省参数值错误!

virtual void draw(ASCEShapeColor color = GREEN) const;

...

};

class ASCECircle : public ASCEShape

{

public:

virtual void draw(ASCEShapeColor color) const;

...

};

ASCEShape *ps; //静态类型是ASCEShape*

ASCEShape *pr = new ASCEShapeRectangle; // 静态类型是ASCEShape*

ASCEShape *pc = new ASCEShapeCircle; //静态类型是ASCEShape*

上面三个指针的静态类型和它们实际所指向的对象的类型是没有关系的。

而对象的动态类型是由它当前所指的对象的类型决定的,即对象的动态类型表示它将执行何种行为。如上面的代码中,pr的动态类型是ASCERectangle*pc的动态类型是ASCECircle*,而ps实际上没有动态类型,因为它还没有指向任何对象。

动态类型可以在程序运行时改变,典型的方法是通过赋值:

ps = pr; //ps的动态类型现在是ASCERectangle*

ps = pc; //ps的动态类型现在是ASCECircle*

虚函数就是动态绑定的,即虚函数通过哪个对象被调用,具体被调用的函数就由那个对象的动态类型决定:

pr->draw(REG); //调用ASCERectangle::draw(REG)

pc->draw(REG); //调用ASCECircle::draw(REG)

虚函数机制的正确性是毋庸置疑的,但当将虚函数和缺省参数值结合起来分析就会产生问题:虚函数是动态绑定的,但缺省参数是静态绑定的,这意味着我们最终可能调用的是一个定义在派生类,但使用了基类中的缺省参数值的虚函数:

pr->draw(); //调用的是ASCERectangle::draw(REG)!!,而不是ASCERectangle::draw(GREEN)

由此,结论就是:禁止重新定义继承而来的缺省参数值!!

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics