条款37: 决不要重新定义继承而来的非虚函数

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

class D: public B { ... };

甚至对B,D或mf一无所知,也可以定义一个类型D的对象x,

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

那么,如果发现这么做:

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

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

和下面这么做的执行行为不一样:

D *pD = &x;                   // 得到x的指针

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

你一定就会感到很惊奇。

如果mf是非虚函数而D又定义了自己的mf版本,行为就不会相同:

class D: public B {
public:
  void mf();                  // 隐藏了B::mf; 参见条款50

  ...

};

pB->mf();                     // 调用B::mf

pD->mf();                     // 调用D::mf

名字查找与继承:(函数调用步骤)

假设调用p->mem()

1.首先确定p的静态类型

2.在p的静态类型对应的类中查找,如果找不到,则依次在直接基类中不断查找直至到达继承链的顶端,如果找遍了该类及其基类仍然找不到,编译器将报错

3.一旦找到mem,就进行常规的类型检查,以确认对于当前找到的mem,本次调用是否合法

4.假设调用合法,则编译器根据调用的是否是虚函数而产生不同的代码:

如果mem是虚函数且我们是通过引用或指针进行的调用,则编译器产生的代码将在运行时确定到底运行该虚函数的哪个版本,依据是对象的动态类型;

反之,则编译器将产生一个常规函数调用,即静态绑定

条款37: 决不要重新定义继承而来的非虚函数

时间: 2024-10-13 01:04:06

条款37: 决不要重新定义继承而来的非虚函数的相关文章

条款36:绝不重新定义继承而来的非虚函数

1 #include <iostream> 2 3 using namespace std; 4 5 class Base 6 { 7 public: 8 void mf(); 9 }; 10 void Base::mf() 11 { 12 cout << "Base::mf" << endl; 13 } 14 15 class Derived : public Base 16 { 17 public: 18 void mf(); 19 }; 20

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

由于重新定义继承而来的non-virtual函数是不正确的(见上一个条款),所以这个条款就将问题局限于:绝不重新定义继承一个带有缺省参数值的virtual函数. (一) virtual函数是动态绑定的,而缺省参数却是静态绑定. 对象的所谓静态类型,是它在程序中被声明时所采用的类型. 你可能会在"调用一个定义于derived class 内的virtual函数"的同时,却使用了base class为它所指定的缺省参数值. (二) 为什么继承而来的virtual函数的缺省参数值不能被重新定

条款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;

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

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++:条款36:绝不重新定义继承而来的non-virtual函数

(一)首先有下面的继承体系: class B { public: void mf(); ... }; class D : public B {...}; D x; 以下行为: B* pB = &x; pB->mf(); 异于以下行为: D* pD = &x; pD->mf(); 上面两种行为产生的结果不一定相同.看下面这种情况: mf是个non-virtual函数而D定义有自己的mf版本: class D : public B { public: void mf(); ...

派生类的构造函数和析构函数和多继承中的二义性与虚函数

析构函数主要作用是对数据成员初始化. 1派生类的构造函数 派生类的构造函数定义的一般形式为: 派生类名::派生类名(基类所需形参,本类成员所需形参):基类1(基类1 参数表),基类2(基类2 参数表),···,基类n(基类n 参数表),对象成员1(对象1 参数表),对象成员2(对象2 参数表),···,对象成员m(对象m 参数表){ //本类基本类型数据成员初始化 } 如果使用基类无参构造函数,派生类构造函数形参表中不包含供给基类构造函数的参数.此时,系统会调用基类的默认构造函数.如果使用对象数

条款33:避免隐藏继承而来的名称

• 此例中混合了纯虚函数.虚函数.非虚函数等,只是为了强调隐藏的是继承而来的名字,至于名字代表的是什么并不重要,即使enum.nested class.typedef也不例外. 1 #include <iostream> 2 3 using namespace std; 4 5 class Base 6 { 7 public: 8 virtual void mf1() = 0; 9 virtual void mf1(int); 10 virtual void mf2(); 11 void mf