之前在Effective C++看到有一个条款:决不要重新定义继承而来的非虚函数,今天在看代码的时候,发现有人重新定义继承而来的非虚函数,所以重新查看了这个条款。

虽然这样做(重新定义继承而来的非虚函数)不会出什么大错,只要注意声明的指针即可。但是我觉得还是不合理。

先看例子。

我把Effective C++例子补全如下:

#include "stdafx.h"#include "stdlib.h"#include <iostream>using namespace std;class B{public:void mf(){cout << "B::mf" << endl;}};class D : public B{public:void mf(){cout << "D::mf" << endl;}};void ShowPoint(int n){char szNum[33] = {0};_itoa_s(n, szNum, 10);cout << szNum << endl;}int _tmain(int argc, _TCHAR* argv[]){D x;           // x是类型D的一个对象B* pB = &x;          // 得到x的指针ShowPoint((int)pB);   // 显示指针值pB->mf();                  // 通过指针pB调用mfD* pD = &x;             // 得到x的指针ShowPoint((int)pD);    // 显示指针值pD->mf();                // 通过指针pD调用mfsystem("pause");return 0;}

输出的结果:

2422915

B::mf

2422915

D::mf

可以看到,都是对象x的指针去调用mf,从上面也可以看到输出的指针值是相同的,同一个对象调用同一个函数,产生了不同的行为。

当把基类B的成员函数改成虚函数时,即:

class B{public:virtual void mf(){cout << "D::mf" << endl;}};

这时输出:

1900272

D::mf

1900272

D::mf

非虚函数是静态绑定:

同一个对象调用同一个函数,产生了不同的行为产生的原因在于,非虚函数是静态绑定的。因为pB被声明为指向B的指针类型,通过pB调用非虚函数时将总是调用那些定义在类B中的函数(即使pB指向的是从B派生的类的对象)。

虚函数是动态绑定:

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

所以如果在实现类D时,对基类B非虚函数mf重写。当D的对象在调用mf时,行为有可能象B,也有可能象D,决定因素跟对象本身没有关系,反而是取决于指向它的指针所声明的类型。引用也会和指针一样表现出这样的异常行为。

下面摘自Effective C++
条款35解释了公有继承的含义是 “是一个”,条款36说明了为什么 “在一个类中声明一个非虚函数实际上为这个类建立了一种特殊性上的不变性,因为它表示的是不会改变的行为 —- 不管一个派生类有多特殊,声明非虚函数的目的在于,使派生类继承函数的接口和强制性实现”。如果将这些分析套用到类B、类D和非虚成员函数B::mf,那么,适用于B对象的一切也适用于D对象,因为每个D的对象 “是一个” B的对象。B的子类必须同时继承mf的接口和实现,因为mf在B中是非虚函数。那么,如果D重新定义了mf,设计中就会产生矛盾。如果D真的需要实现和B不同的mf,而且每个B的对象 —- 无论怎么特殊 —- 也真的要使用B实现的mf,那么,每个D将不 “是一个” B。这种情况下,D不能从B公有继承。相反,如果D真的必须从B公有继承,而且D真的需要和B不同的mf的实现,那么,mf就没有为B反映出特殊性上的不变性。这种情况下,mf应该是虚函数。最后,如果每个D真的 “是一个” B,并且如果mf真的为B建立了特殊性上的不变性,那么,D实际上就不需要重新定义mf,也就决不能这样做。

不管采用上面的哪一种论据都可以得出这样的结论:任何条件下都要禁止重新定义继承而来的非虚函数。

文章来源于:
http://www.cnblogs.com/fangyukuan/archive/2010/06/03/1751072.html

C++ 重新定义继承而来的非虚函数相关推荐

  1. 避免在派生类中重新定义基类的非虚函数

    我们都知道,在基类中定义虚函数的目的是允许派生类拥有相同接口却可以有不同的实现,通过对象的指针或引用来访问虚函数可以实现运行时的多态.这么说来,在派生类中重定义(override)虚函数是没有任何问题 ...

  2. C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中

    C++对象内存布局--③测试多继承中派生类的虚函数在哪一张虚函数表中 测试2:证明派生类的虚函数的地址跟第一基类的虚函数地址保存在同一张虚函数表中. 派生类有多少个拥有虚函数的基类,派生类对象就有多少 ...

  3. c++继承 基类 派生类 虚函数

    继承   类和类的关系有组合.继承和代理.继承的本质就是代码复用.子类继承父类中的一些东西,父类也称为基类,子类也称为派生类.派生类继承了基类除构造函数以外的所有成员. 继承的方式   继承方式有pu ...

  4. 基类指针调用派生类函数_C++ 多态性:虚函数--基类与派生类类型转换(第7章 05)例子问题解析(学习笔记:第8章 05)...

    虚函数[1] 问题:还记得第7章的例子吗[2]? 例7-3 类型转换规则举例 #include <iostream> using namespace std; class Base1 { ...

  5. C++ 论公有继承时纯虚函数、虚函数、普通函数的行为表现及虚函数的重写(深度好文)

    文章目录 1 公有继承时三种类型的函数行为 1.1 纯虚函数 (pure virtual) 1.2 普通虚函数 **1.2.1 方法一** **1.2.2 方法二** 1.3 非虚函数 2 重写 (o ...

  6. 继承与派生、虚函数、多态

    文章目录 继承 单继承 多重继承 派生 派生类的声明方式 派生类的构成 派生类成员的访问属性 公用继承 私有继承 保护继承 普通派生类的构造函数 创建普通对象的构造函数 有子对象的派生类的构造函数 创 ...

  7. C++ 面向对象(二)多态 : 虚函数、多态原理、抽象类、虚函数表、继承与虚函数表

    目录 多态 多态的概念 多态的构成条件 虚函数 虚函数的重写 协变(返回值不同) 析构函数的重写(函数名不同) final和override final override 重载, 重写, 重定义对比 ...

  8. C++中的继承与虚函数各种概念

    虚继承与一般继承 虚继承和一般的继承不同,一般的继承,在目前大多数的C++编译器实现的对象模型中,派生类对象会直接包含基类对象的字段.而虚继承的情况,派生类对象不会直接包含基类对象的字段,而是通过一个 ...

  9. 虚函数、虚函数表、虚继承

    1.虚函数 虚函数的定义: 虚函数必须是类的 非静态成员函数(且非构造函数),其访问权限是public(可以定义为privateor proteceted, 但是对于多态来说,没有意义),在基类的类定 ...

  10. 「现代C++设计魅力」虚函数继承-thunk技术初探

    简介:工作中使用LLDB调试器调试这一段C++多继承程序的时候,发现通过lldb print(expression命令的别名) 命令获取的指针地址和实际理解的C++的内存模型的地址不一样.那么到底是什 ...

最新文章

  1. c语言用hash方式数组去重,js数组去重的hash方法
  2. 前端向后端发送请求,后端返回的一个值的请求的ajax.get();方法
  3. NumPy Cookbook 带注释源码 十一、NumPy 的底牌
  4. 海量数据挖掘MMDS week4: 推荐系统之隐语义模型latent semantic analysis
  5. springmvc mybatis 整合 框架源码 bootstrap html5 mysql oracle spring
  6. 企查查python爬虫实例
  7. stm32无源蜂鸣器定时器_STM32与无源蜂鸣器
  8. 开展网络口碑营销的前提
  9. UESTC 1639 云中谁寄锦书来?雁字回时,月满西楼。 Dijkstra拓展
  10. 备战双十一·尖货优品实时选
  11. 如何提升w ndows10系统网速,Win10更新后网速变慢怎么办?教你一招提升Win10网速...
  12. 基于Fortran的结构力学位移法编程求解
  13. ARM920T内核工作模式
  14. indesign如何画弧线_InDesign钢笔工具怎么使用
  15. 爬虫(一):爬虫的基础知识 ---通用爬虫和聚焦爬虫,http和https协议,常见的响应状态码
  16. 从UAP-Studio中导出项目并且部署到服务器上
  17. Json代码实战演练
  18. 今天去面试碰到到一个问题
  19. javascript 判断当前浏览器版本
  20. 【SAS】format和informat

热门文章

  1. 第二章 数据模型 概念数据模型E-R图 设计E-R图,过程,例子 逻辑数据模型,分类
  2. 王天官系古盐山县(今孟村县王帽圈)人
  3. 特斯拉Model 3进化不止 传统车企到底应该学什么?...
  4. java 另存为文本_Java代码复制所有文本,同时将大写字母从一个文本文件转换为另一个文本文件中的小写字母...
  5. JMeter-Ramp-up Period解释
  6. 无线路由器怎么改密码
  7. linux上wps能云同步吗,WPS For Linux 6634 再次更新发布-文档也要上云
  8. 医咖会免费SPSS免费教程学习笔记—灵敏度和特异度
  9. 内存分区0x00000000-0x0000FFFF共64K是null指针
  10. 联通一直显示无法连接服务器是怎么回事,联通宽带无法连接服务器1404