Effective C++:条款37:绝不重新定义继承而来的缺省参数值

由于重新定义继承而来的non-virtual函数是不正确的(见上一个条款),所以这个条款就将问题局限于:绝不重新定义继承一个带有缺省参数值的virtual函数。

(一)

virtual函数是动态绑定的,而缺省参数却是静态绑定。

对象的所谓静态类型,是它在程序中被声明时所采用的类型。

你可能会在“调用一个定义于derived
class 内的virtual函数”的同时,却使用了base
class为它所指定的缺省参数值。

(二)

为什么继承而来的virtual函数的缺省参数值不能被重新定义呢?

其实原因也挺简单:缺省参数是静态绑定,而virtual函数是动态绑定. 所谓对象的静态绑定也叫前期绑定,它是说该对象类型和行为在程序编译期间就可以确定,例如:

class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              virtual void draw(Color color = RED)const = 0;
              ...
     };
     class Circle : public Shape{
     public:
              //哦欧! 竟然改变缺省参数值
              virtual void draw(Color color = GREEN)const{ ... }
     };
     class Rectangle : public Shape{
     public:
             //没用指定参数类型,需要用户去明确的指定其值
            //静态绑定下不继承基类的缺省值,若以指针或引用调用则不需要指定缺省值,因为动态绑定
            //继承基类的参数缺省值
            virtual void draw(Color color)const{ ... }
     };

看一下下面几个指针:

 Shape* ps;
 Shape* pc = new Circle;
 Shape* pr = new Rectangle;

这里的ps,pc,pr不管它具体指向的是什么对象,他们的静态类型都是Shape*。而动态类型就是它们真正指向的对象的类型。故pc的动态类型为Circle*,而pr的动态类型为Rectangle*,ps由于没有指向任何对象,所以此时没有动态类型。

(三)看下面这个语句!

pc->draw();   //注意调用的是: Circle::draw(RED)

怎么会调用Circle::draw(RED)呢!?为什么不是Circle::draw(GREEN)?

原因:

(1)首先根据其调用语句用指针这一事实,我们就知道了其调用的版本应该是该指针的动态类型的函数版本,即Circle::draw,这个问题不大。

(2)下面我们来看它的传值参数,前面我们提到缺省参数值是静态绑定的,而pc的静态类型是Shape*,所以该参数的传入值是Shape的该函数版本的缺省值。

那为什么C++坚持以这种乖张的方式来运作呢?答案在于运行期效率,如果缺省值也是动态绑定的,那么编译期就必须要有办法在运行期为virtual函数决定适当的参数缺省值.如果这样做的话,就要比目前实现的"在编译期决定"的机制更慢而且更复杂,考虑到执行速度和实现上的简易性,C++放弃了这样的做法。

(四)解决方法!

现在,为了遵循本款约定却同时提供缺省参数值给你的基类和父类,,代码就这样了:

class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              virtual void draw(Color color = RED)const = 0;
              ...
     };
     class Circle:public Shape{
     public:
              virtual void draw(Color color = RED)const {...}
     };

明显的是代码重复嘛!何况你要是想改变缺省值的话,必须要同时改变基类和子类函数的缺省值,一不小心,就会出现漏改或写错的情况,导致意想不到的错误出现.有没用一种更方便的写法呢?当然,你还记得NVI手法吗?额..,(non-virtual
interface),要是忘记的话,回过头看看条款35,用这种手法的话,我们写下代码如下:

class Shape{
     public:
              enum Color{RED,GREEN,BLUE};
              void draw(Color color = RED) const{
                     ...
                     doDraw(color);
                     ...
              }
              ...
     private:
             virtual void doDraw(Color color) const = 0;
     };
     class Circle:public Shape{
                   ...
     private:
              virtual void doDraw(Color color){ ... }
     };

由于draw是non-virtual而non-virtual绝对不会被重新改写(条款36),所以color的缺省值总是为RED。

请记住:

(1)绝对不要重新定义一个继承而来的缺省参数值,因为缺省参数值都是静态绑定,而virtual函数-你唯一应该覆写的东西-却是动态绑定。

Effective C++:条款37:绝不重新定义继承而来的缺省参数值,布布扣,bubuko.com

时间: 2024-11-29 03:08:10

Effective C++:条款37:绝不重新定义继承而来的缺省参数值的相关文章

Effective C++ Item 37 绝不重新定义继承而来的缺省参数值

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:绝对不要重新而来的缺省参数值,因为缺省参数值都是静态绑定,而 virtual 函数 -- 你唯一应该覆写的东西 -- 却是动态绑定 示例: class Shape{ public: enum ShapeColor {Red, Green, Blue}; virtual void draw(ShapeColor color = Red) const = 0; }; class Rect

Item 37:绝不重新定义继承而来的缺省参数值

Item 37:绝不重新定义继承而来的缺省参数值 Item 37:Never redefine a function's inherited default parameter value 本条款的讨论局限于:继承一个带有缺省参数值的virtual函数. 本条款成立的理由是:virtual函数是动态绑定(dynamically bound),而缺省参数却是静态绑定(statically bound). 静态绑定又名前期绑定,early binding:动态绑定又名后期绑定,late bindin

effective C++中条款37:绝不重新定义继承而来的缺省参数值

virtual 函数会动态绑定,而virtual函数的缺省参数值是静态绑定的.用一个base类型的指针p去指向一个derived类对象,通过p调用虚函数时,会动态绑定到实际所指对象中的函数:用一个derived类型的指针p2指向一个derived对象,由p2调用函数时,直接就是调用的derived中的函数,其参数值也是derived类中函数对应的参数值. #include <iostream> using namespace std; class A { public: enum Color

绝不重新定义继承而来的缺省参数值

考虑如下的代码: class Shape { public: enum ShapeColor{Red, Green, Blue}; virtual void draw(ShapeColor color = Red) const = 0; ... }; class Rectangle: public Shape { public: virtual void draw(ShapeColor color = Green) const; ... }; class Circle: public Shape

Effective C++ 条款36 绝不重新定义继承而来的non-virtual函数

1. public继承意味着is-a关系,即派生类是基类的一种,任何基类可以进行的操作派生类也应该可以进行,如果派生类对于某个函数的实现与基类不同,那么就应当这个函数设为virtual,如果基类中的某个函数是non-virtual的,那么也应该意味着派生类应该继承这个函数的实现,而不应该重定义它. 2. 由于non-virtual函数采用静态绑定,如果对其进行重定义,那么经由指向派生类对象的基类指针调用的non-virtual函数将会是基类版本,这与期望不同,因此要override就设为virt

Effective C++ Item 36 绝不重新定义继承而来的 non-virtual 函数

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:绝对不要重新定义继承而来的 non-virtual 函数 --> Item 7 "为多态基类声明 virtual 析构函数" 是本条款的特例 示例: class B{ public: void mf(); //... }; class D: public B{ public: void mf(); // 遮掩了B::mf,Item 33 名称遮掩规则 } D x; B

C++ 静态绑定与动态绑定------绝不重新定义继承而来的缺省参数

在了解静态绑定和动态绑定之前,先了解什么是对象的静态类型,什么是对象的动态类型. 对象的静态类型:对象在声明时采用的类型.是在编译器决定的. 对象的动态类型:目前所指对象的类型.是在运行期决定的. 动态类型可以更改,而静态类型不可更改.看一个示例 class Base { public: void setData(int i=10) { cout <<" virtual int Base::setData()"<<endl; } virtual int getD

条款38: 决不要重新定义继承而来的缺省参数值

虚函数是动态绑定而缺省参数值是静态绑定的,当基类和派生类对同一个虚函数设置缺省参数值时,只有基类的缺省参数值起作用. 对象的静态类型是指你声明的存在于程序代码文本中的类型,对象的动态类型是由它当前所指的对象的类型决定的.即,对象的动态类型表示它将执行何种行为. 虚函数是动态绑定的,意思是说,虚函数通过哪个对象被调用,具体被调用的函数就由那个对象的动态类型决定 将虚函数和缺省参数值结合起来分析就会产生问题,因为,如上所述,虚函数是动态绑定的,但缺省参数是静态绑定的.这意味着你最终可能调用的是一个定

条款37:绝不重新定义继承而来的缺省参数

在继承中,分为两类函数:virtual和non-virtual.而重新定义一个非虚函数是不好的(条款36),那么以下的讨论就是如何定义继承而来的虚函数. 强调:虚函数是动态绑定的,而缺省参数值是静态绑定的. 1 #include <iostream> 2 3 class Shape 4 { 5 public: 6 enum ShapeColor{ Red, Green, Blue }; 7 virtual void draw(ShapeColor color = Red) const = 0;