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

Effective C++ 条款36:绝不重新定义继承而来的 non-virtual 函数 深入解析 Effective C++ 条款36,理解为什么不应该重新定义继承而来的 non-virtual 函数,掌握静态绑定与动态绑定的本质区别,避免面向对象设计中的常见陷阱。 阅读详情

        假设我告诉你,class D系有class B以public形式派生而来,class B定义有一个public成员函数mf。由于mf的参数和返回值都不重要,所以我假设两者皆为void。换句话说我的意思是:

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

        你可能会相当惊讶。毕竟两者都通过对象x调用成员函数mf。由于两者所调用的函数都相同,凭借的对象也相同,所以行为也应该相同,是吗?

        是的,理应如此,但事实可能不是如此。更明确地说,如果mf是个non-virtual函数而D定义有自己的mf版本,那就不是如此:

class D: public B {
	public:
	void mf();    // 遮掩(hides)了B::mf;见条款33
	...
};
pB->mf();    // 调用B::mf
pD->mf();    // 调用D::mf

        造成此一两面行为的原因是,non-virtual函数如B::mf和D::mf都是静态绑定(statically bound,见条款37)。这意思是,由于pB被声明为一个pointer-to-B,通过pB调用的non-virtual函数永远是B所定义的版本,即使pB指向一个类型为“B派生之class”的对象,一如本例。

        但另一方面,virtual函数却是动态绑定(dynamically bound,见条款37),所以它们不受这个问题之苦。如果mf是个virtual函数,不论是通过pB或pD调用mf,都会导致调用D::mf,因为pB和pD真正指的都是一个类型为D的对象。

        如果你正在编写class D并重新定义继承自class B的non-virtual函数mf,D对象很可能展现出精神分裂的不一致行径。更明确地说,当mf被调用,任何一个D对象都可能表现出B或D的行为:决定因素不再对象自身,而在于“指向该对象之指针”当初的声明类型。References也会展现和指针一样难以理解的行径。

        条款32以及说过,所谓public继承意味is-a(是一种)的关系。条款34则描述为什么class内声明一个non-virtual函数会为该class建立起一个不变性(invariant),凌驾其特异性(specialization)。如果你将这两个观点施行于两个classesB和D以及non-virtual成员函数B::mf身上,那么:

  • 适用于B对象的每一件事,也适用于D对象,因为每个D对象都是一个B对象;
  • B的derived classes一定会继承mf的接口和实现,因为mf是B的一个non-virtual函数。

        不论哪一个观点,结论都相同:任何情况下都不该重新定义一个继承而来的non-virtual函数。

请记住

  • 绝对不要重新定义继承而来的non-virtual函数。
SRRC型号核准的模块豁免规则解析——如何利用完整型模块简化设备认证流程 本文详细解析了SRRC型号核准中模块豁免规则的应用,重点介绍了完整型模块的技术定义、豁免条件及实际应用场景。通过合理利用完整型模块,企业可显著简化设备认证流程,缩短上市周期并降低成本。文章还提供了模块选型指南和常见问题解决方案,帮助工程师高效应对SRRC认证挑战。 阅读详情

相关推荐

高速DAC时钟与配置实战:DAC5681Z硬件设计与寄存器编程详解

在高速数据转换系统中,时钟信号是决定系统性能的核心要素,其稳定性和纯净度直接影响数模转换器(DAC)的输出频谱纯度与动态范围。时钟电路的设计原理涉及信号完整性、阻抗匹配与直流偏置,而内部锁相环(PLL)和延迟锁相环(DLL)的配置则是实现精确时序控制的关键技术。这些技术的价值在于,它们共同保障了高速通信、雷达和任意波形生成等应用对信号实时性与精度的严苛要求。本文聚焦于德州仪器(TI)的DAC5681Z,深入解析其外部时钟输入电路的硬件设计要点,包括端接电阻的计算与选型,并详细拆解通过串行外设接口(SPI)对

weixin_30342363的博客 186

条款36:绝对不要重新定义继承而来的non-virtual函数

class b {public: void mf(); ...};class d: public b {public: void mf(); // hides b::mf; see item33 ...};       如果你在编写 class d 而且你重定义了一个你从 class b 继承到的非虚拟函数mf,d的objects将很可能表现

蜜蜂 475

栅格数据空间叠置分析题目步骤讲解

题目: 准备工作: ①新建数据库,将实验数据导入(利于后期矢量转栅格操作,未建数据库此步骤会一直出错); ②在工具条自定义窗口勾选spacial analyst模块,在菜单栏→扩展模块,勾选spacial analyst;确保实验后面的操作可以成功。 详细操作步骤: 1.对三个方案的规划路线(line.shp)建立50m缓冲区;打开ArcMap,在ArcToolBox→分析工具→邻域分析→缓冲区,打开缓冲区工具窗口,界面设置如图:线性单位填写50米,其余默认,单击“确定”。 ...

m0_57139844的博客 8893

【GPA】C++面向对象程序设计_笔试详解

C++期末笔试的完全解析。

Explorer Dong 的技术博客 1668

基于Plecs的LLC谐振变换器闭环设计:从PI参数整定到动态响应优化

本文详细介绍了在Plecs仿真环境中,如何为半桥LLC谐振变换器设计并优化闭环控制系统。从开环模型搭建与特性分析入手,重点阐述了基于频域法的PI控制器参数整定流程,并通过负载阶跃、输入电压突变等测试优化动态响应。文章还分享了从仿真模型过渡到实际硬件时的关键注意事项,帮助工程师实现稳定、高效的电源设计。

e4f5g6h的博客 623

固定翼飞行器建模(一)——六自由度动力学

前两期文章讲完了气动力和气动力矩的模型,本期将来讲解一下六自由度部分的动力学与运动学方程。

2302_80148937的博客 1万+

C++ 虚函数和类作用域

15.5.4. Virtual Functions and Scope Consider the following (artificial) collection of classes: class Base { public: virtualint fcn(); }; classD1 : public Base { public: //hides fcn in the base

梦幻DUO的游戏开发之路 650

基于UC3842的反激电源的全面深入解析与MATLAB仿真

内置电流检测,可实现电流模式控制,提高系统的动态响应性。多种保护功能,如欠压锁定、过温保护、短路保护等,确保系统的安全可靠运行。宽工作电压范围,适应不同的电源设计需求。简单的外部元件配置,易于实现。UC3842的典型应用包括开关电源、DC-DC转换器和反激式电源等,其稳定性和可靠性在实际应用中得到了广泛验证。

qq_38334677的博客 1086

C++箴言:绝不重定义继承的非虚拟函数

zzy5177的专栏 385

JAX/JAXlib GPU 加速版本安装教程(CUDA)

本文介绍了在Windows WSL2+Ubuntu22.04环境下配置JAX/JAXlib GPU加速版本的详细步骤。使用RTX 4060显卡,安装CUDA 12.4和cuDNN 8.9.7驱动,通过Miniconda创建Python 3.9虚拟环境。具体步骤包括:安装Miniconda、配置CUDA环境变量、安装cuDNN驱动、下载并安装JAX/JAXlib。最后通过测试代码验证GPU加速功能,成功检测到GPU并完成矩阵运算。整个过程配有详细命令和截图说明,适合需要搭建JAX GPU开发环境的用户参考。

猿小白的博客 624

STM32 IIC温湿度传感器(STH45)实验

精度 ΔRH = ±1.0 %RH, ΔT = ±0.1 °C• 平均电流:0.4 μA,空闲电流:80 nA• I2C FM、CRC 校验和、乘法。I2C 地址。• 专利保护选项 [1]、PTFE 膜和可拆卸保护盖• 工作范围:0 …100 %相对湿度,−40…125 摄氏度• 在冷凝环境中功能齐全• 动力加热器,真正的NIST可追溯性• JEDEC JESD47 认证 • 符合 ISO 17025 : 2017 的传感器专用校准证书,3 点温度校准。

baidu_34971492的博客 3067

non-virtual函数的调用

先看下面一段代码,派生类没有重新实现non-virtual函数print函数:    #include   using namespace std;  class Base  {  public:      void print()      {          cout " invoked from Base" endl;      }  };    class Derived: pub

keep_moving_cqu的专栏 1359

详解Xilinx FPGA高速串行收发器GTX/GTP(6)--Pattern Generator/Pattern Checker

详解Xilinx FPGA高速串行收发器GTX/GTP(6)--Pattern Generator/Pattern Checker

孤独的单刀 1624

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

假设class D是由class B以public形式派生而来,class B定义有一个public成员函数mf,由于mf的参数和返回值都不重要,所以定义两者都为void。

依然爱猪的博客 641

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

条款36:绝不重新定义继承而来的non-virtual函数——208

文章目录非虚拟函数是静态绑定的——208虚拟函数是动态绑定的——209总结——210 非虚拟函数是静态绑定的——208 结合条款33:如果基类一个非虚拟函数,派生类也有这个函数的定义,派生类中的此函数会遮掩基类的函数。因为非虚拟函数是静态绑定的。 讨论以下定义: class B{ public: void mf(); ... }; class D:public B{ public...

缇珞的博客 271

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

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

non-virtual 函数的定义和行为non-virtual 函数是在基类中定义的普通成员函数,不带有virtual关键字。在子类中定义同名同参数的函数时,实际上是隐藏(隐藏是指基类函数名被隐藏,无法在子类作用域中直接访问,但可以通过作用域解析操作符调用)了基类的函数,而不是覆盖它。这意味着通过基类指针或引用调用该函数时,调用的是基类版本,而不是子类版本。静态绑定:编译时确定函数调用,与对象的实际类型无关,适用于非虚函数。动态绑定:运行时确定函数调用,根据对象的实际类型选择函数版本,适用于虚函数。

qq_31638535的博客 652

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

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

如果D重新定义了mf,那么设计中就出现了矛盾。如果D确实需要以不同于B的方式实现mf,并且如果每个B对象——无论多么特化——确实都必须使用B的mf实现,那么“每个D都是B”就不成立了。另一方面,如果D确实必须public继承自B,并且如果D确实需要以不同于B的方式实现mf,那么mf反映B的特化过程中不变式这一说法就不成立了。这意味着,由于 pB 被声明为指向 B 的指针类型,通过 pB 调用的非虚函数将永远是 B 类中定义的版本,即使 pB 实际指向的是从 B 派生的类的对象(本例中正是如此)。

tus00000的博客 261

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

条款 36 :绝不重新定义继承而来的non-virtual函数 Never redefine an inherited non-virtualfunction class B{ public: void f(); ... }; class D:public B{...}; D x;//x是一个类型为D的对象 //1 B* pB=&x;//获得一个指针指向x pB->f();//经由该指针调用f //2 D* pD=&x;//获得一个指针指向x pD->

weixin_45730130的博客 334

Effective C++条款36:继承与面向对象之(绝不重新定义继承而来的non-virtual函数)

一、看一个隐藏non-virtual函数的例子 假设class D以public的方式继承于class B,代码如下: class B { public: void mf(); }; class D :public B {}; int main() { D x; B *pB = &x; pB->mf(); //调用B::mf() ...

董哥的黑板报 697

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

//绝不重新定义继承而来的non-virtual函数 #include using namespace std; class B{ public: void mf() {cout << "调用基类B成员函数" << endl;} //...... }; class D:public B{ public: void mf(){cout << "this is D running" <<

天涯明月的专栏 850

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

知识点: 多态时:基类(或子类)指针指向子类对象,利用该指针调用成员时关键点看子类对象 非多态时:基类(或子类)指针指向子类对象时,利用该指针调用成员时关键点看指针 对于基类B和子类D:class B{ public: void mf(); ... }; class D:public B{...};  我们知道对于non-virtual函数,子类是既继承接口又继承实现的,在这种情况下

cape的博客 527

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

## 一. 内容 1. 考虑以下示例,一个 D class 继承自 B class 的体系中,两者均定义了同样的 non-virtual 函数: ```cpp class B { public: std::string ToString() const{ return "B"; } }; class D : public B { public: std::string ToString() const{ return "D"

MoOtA 302

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

我们来看看这样一个简单的程序: #include #include using namespace std; class A { public: void fun() { cout << "A" << endl; } }; class B : public A { public: void fun() { cout << "B" << endl; } }; int

认知 行动 坚持 7261

条款36-37绝不重新定义继承而来的non-virtual函数与缺省参数

条款36:绝对重新定义继承而来的non-virtual函数 条款37:绝不重新定义继承而来的缺省参数 这两个条款说的绝不没有说不可以,意思是最好是这么做。个人觉得这两个条款没有太多可说的。 这里讲下缺省参数的问题。 在C++面向对象的技术中,virtual函数的类型是动态的,但是参数是静态的。具体可以参考《C++ primer》 以下的代码是糟糕的: class Sharp {

622
上一篇: 考虑virtual函数以外的其他选择——条款35
下一篇: 绝不重新定义继承而来的缺省参数值——条款37
OODeveloper
博客等级 码龄12年 43粉丝 137原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值