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

要点:

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

 

 

virtual函数是动态绑定的,而缺省参数却是静态绑定,对象的所谓静态类型,是它在程序中被声明时所采用的类型。

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 { 
public: 
    virtual void draw(ShapeColor color) const; 
    //以上这么写当客户以对象调用此函数,一定要指定参数值 
    //因为静态绑定下这个函数并不从其base继承缺省参数值 
    //但若以指针(或reference)调用此函数,可以不指定参数值 
    //因为动态绑定下这个函数会从其base继承缺省参数值 
};

 

Shape* ps;                            //静态类型是Shape* 
Shape* pc = new Circle;               //静态类型是Shape* 
Shape* pr = new Rectangle;            //静态类型是Shape*

ps,pc,pr不论这些指针指向什么,它们的静态类型都是Shape*  对象的所谓动态类型则是指“目前所指对象的类型”。可以在程序执行过程中改变

ps = pr;
ps = pc;

 

virtual 函数系动态绑定而来,意思是调用一个virtual 函数时, 究竟调用哪一份函数实现代码,取决于发出调用的那个对象的动态类型:

pc->draw(Shape::Red) //调用Circle::draw
pr->draw(Shape::Red) //调用Rectangle::draw

考虑带有缺省参数的virtual函数时,因为virtual函数是动态绑定的,而缺省参数是静态绑定的。意思是你可能在“调用一个定义于derived class内的virtual函数”的同时,却使用base class 为他所指定的缺省参数值:

pr->draw(); //调用Rectangle::draw(Shape::Red)!

pr的动态类型是Rectangle*,所以调用的是Rectangle的virtual函数。Rectangle::draw函数的缺省参数应该是Green,但由于pr的静态类型是Shape*,所以调用的缺省参数值来自Shape class而非Rectangle class!

即使把指针换成reference,问题仍然存在。

为什么c++坚持以这种乖张的方式来运作呢?答案在于运行期效率。如果缺省参数是动态绑定的,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值。这比目前实行的“在编译期决定”的机制更慢并且复杂。为了程序的执行速度和编译器实现上的简易度,c++做了这样的取舍,其结果就是你如今享受的执行效率。

 

如果你遵守这条规则,并且同时提供缺省参数值给base和derived classes的用户,又会发生什么呢?

class Shape { 
public: 
    enum {Red, Green, Blue}; 
    virtual void draw(ShapeColor color = Red) const = 0; 
};

class Rectangle : public Shape { 
public: 
    virtual void draw(ShapeColor color = Red) const; 
};

代码重复。更糟的是,又带有相依性;如果Shape内的缺省参数值改变了,所有“重复给定缺省参数值”的那些derived classes也必须改变,否则它们最终导致“重复定义一个继承而来的缺省参数值”。

怎么办?

当你想令virtual函数表现你所想要的行为却遭遇麻烦,聪明的做法是考虑替代设计。条款35。其中之一就是NVI手法:

令base class内的一个public non-virtual函数调用一个private virtual函数,后者可被derived classes重新定义。这里我们可以让non-virtual函数指定缺省参数,而private virtual函数负责真正的工作:

class Shape { 
public: 
    enum {Red, Green, Blue}; 
    void draw(ShapeColor color = Red) const 
    { 
        doDraw(color); 
    } 
private: 
    virtual void doDraw(ShapeColor color) const = 0; 
};

class Rectangle : public Shape { 
public: 
private: 
    virtual void doDraw(ShapeColor color) const;//不需指定缺省参数 
};

由于non-virtual函数应该绝对不被derived classes覆写(条款36),这个设计很清楚的使draw函数的color缺省参数值总为Red。

STM32CubeMX和华为LiteOS Studio开发STM32 传统的STM32的开发通常使用Keil/IAR这类开发软件,随着物联网时代的到来,传统嵌入式的发展不断向物联网方向靠拢,而Keil/IAR这类开发软件无法满足IoT一站式的开发,华为LiteOS Studio结合STM32CubeMX可以快速,高效地进行物联网开发,本文提供一种开发思路及环境的配置方法。... 阅读详情

相关推荐

点云中概念:匹配、配准、融合

匹配,是寻找与一幅图相似的图像(不对寻找到的图像做矫正)。 配准,是寻找相似图像但是变形后的图像(需要做一些旋转之类的校正变换)。 融合,是多幅图像连接成一幅大图,视频集成中用的比较多。 1)匹配: 2)配准: 比如由于固定式激光雷达受测量方式的限制和被测物体集合形状的限制,固定的话一次只能扫描被测物体有限范围的点云数据(比如一个面??),这需要在多个视角下进行多次扫描(就是将激光雷达前后左右四个面都扫一下??对的),然而每个视角下得到点云数据都具有独立的坐标系,无法直接进行拼接(拼接就是将四个面

m0_37957160的博客 1万+

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

virtual 函数会动态绑定,而virtual函数的缺省参数值是静态绑定的。用一个base类型的指针p去指向一个derived类对象,通过p调用虚函数时,会动态绑定到实际所指对象中的函数;用一个derived类型的指针p2指向一个derived对象,由p2调用函数时,直接就是调用的derived中的函数,其参数值也是derived类中函数对应的参数值。 #include using name

yinqiaohua的专栏 839

基于C++ MFC RSA 实现加密程序

RSA 公开密钥密码体制是一种使用不同的加密密钥与解密密钥,“由已知加密密钥推导出解密密钥在计算上是不可行的”密码体制。在公开密钥密码体制中,加密密钥(即公开密钥)PK 是公开信息,而解密密钥(即秘密密钥)SK 是需要保密的。加密算法 E 和解密算法 D 也都是公开的。虽然解密密钥 SK 是由公开密钥 PK 决定的,但却不能根据 PK 计算出 SK。正是基于这种理论,1978 年出现了著名的 RSA 算法,它通常是先生成一对 RSA 密钥,其中之一是保密密钥,由用户保存;

神仙别闹的自留地 282

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

引子: 阿里的一道题: 1 #include <IOSTREAM> 2 using namespace std; 3 4 class A{ 5 public: 6 virtual void func(int val = 0) 7 { 8 cout<<"a~"<<val<&lt...

anjl1296的博客 146

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

考虑以下类: 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 colo...

qq_44800780的博客 238

Effective C++》读书笔记 条款37绝不重新定义继承而来的缺省参数值

我们都知道virtual函数是动态绑定的,那么为什么要求绝不重新定义继承而来的缺省参数值呢?先来看一个例子 class Shape { public: enum ShapeColor{Red,Green,Blue}; //纯虚函数,因为所有形状都必须提供一个函数,用来绘出自己,而且每个形状都不一样 virtual void draw(ShapeColor color = Red)const ...

junya_zhang的博客 347

Effective C++条款37绝不重新定义继承而来的缺省参数值(Never redefine a function‘s inherited default parameter value)

Effective C++条款37绝不重新定义继承而来的缺省参数值(Never redefine a function‘s inherited default parameter value)

CltCj的博客 562

Effective C++条款37继承与面向对象之(绝不重新定义继承而来的缺省参数值

待续

董哥的黑板报 779

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

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

u010470972的专栏 929

C++】列表初始化与右值引用及移动语义介绍

C++11 带来了很多重要的内容,也带走了我的头发,在实现各种容器的时候,我几乎没用到 C++11 的内容,因为 C++ 的发展历史太长了,概念多如牛毛,所以我想要根据历史系统的学习 C++ 就没用太使用现代C++的内容大概从这篇文章开始,我想要系统的补充比较现代的C++内容,本篇就先来介绍。

qeen87的博客 258

C++设计模式】工厂方法模式

工厂方法模式:提供一种通用的方法创建对象实例,把派生类对象的创建细节隐藏思路:调用一个专门的工厂函数,传入参数,由工厂函数负责生成对象。利用继承和多态实现这种模式用来解决:如果代码到处写new Plane(),new Boat(),当后续需要改动对象创建逻辑,就要修改项目中每一处new。优点缺点。

2401_88972077的博客 350

C++】线程库

本文系统介绍C++多线程编程:涵盖线程创建、join/detach、this_thread;深入讲解互斥锁(普通、递归、超时、自旋)及RAII管理,对比lock_guard与unique_lock;介绍atomic原子操作与CAS无锁编程;最后详解条件变量,包括生产者-消费者模型、核心API及注意事项,并给出交替打印的实现。

wefg1的博客 182

2026年8月31日-9月6日(UE肉鸽蓝图改c++和GIS视频为主)

GIS视频 [█░░░░░░░░░░░░░] 2% (2 / 85)肉鸽改造 第28节(一函数或类) ✅。当前执行位置:9.1(超前1天)📋 8.31 日计划(正常)根据百日计划,实际执行情况。OSG地球源码100行 ✅。今日所有计划任务已完成 ✅。✅ 当前状态(8.31)GIS视频 1-22 ✅。GIS视频 1-21 ✅。GIS代码 1-21 ✅。UEC++ 第26节 ✅。GIS代码 201 ✅。肉鸽视频 第28节 ✅。🔧 需要周末查漏补缺项。GIS主线进度 状态。Qt网络 第1课 ✅。

coder 161

C语言-流程控制语句 05

1。

2502_90352018的博客 219

C++多线程精讲:线程创建、join/detach、互斥锁、条件变量、死锁成因与规避、线程安全、并发工程实战

前面我们学习的所有代码都是单线程串行执行:代码从上到下依次运行,同一时刻只能处理一件事。在服务器、后端服务、高性能程序中,单线程效率极低:IO阻塞、任务排队、CPU资源浪费。因此多线程并发编程是后端开发的核心能力。C++11之前,线程依赖系统API(Windows CreateThread、Linux pthread),平台不兼容、代码无法跨平台。C++11 标准库统一封装多线程接口,提供全套并发组件,真正实现一次编写、跨平台编译运行。:线程创建、管理:互斥锁、智能锁:条件变量:原子类型。

2401_86195313的博客 245

通用GUI编程基础(C++造轮子)——把窗立起来——从一扇空白窗到 GWLP_USERDATA

仓库已经开源!喜欢的话点个⭐!仓库Win32和Win32图形栈的部分目前已完成教程,力争做一个完备的GUI教程!欢迎各位大佬前来参观:https://github.com/Charliechen114514/anatomy_gui开源系列文档站正在维护,点击阅读:https://charliechen114514.github.io/anatomy_gui/。麻烦在于 Win32 的窗口根本不是面向对象的东西——系统派消息时只认 HWND、只调 WndProc,它不知道也不关心我们的Window。

charlie114514191的博客 323

基于C++ WinPcap 的网络抓包软件

项目名称:网络抓包软件设计项目成员:项目描述:目前,有许多流行的网络抓包软件可以获取流经网卡的数据包(ethereal, sniffer),这个题目要求学生利用 winpcap 库设计并实现一个网络抓包的原型系统。项目目标:实现一个网络抓包系统项目功能搜索并显示所有的网卡设备根据所选网卡进行抓包操作,并显示数据包的基本信息,eg:类型、时间、IP 地址等信息对数据包进行详细分析,显示数据内容。

神仙别闹的自留地 193

搭建GraalVM开发环境,配置Spring Boot原生镜像

graalvm搭建

XinhuaShuDiao的博客 191

(全新整理)省级-高标准农田面板数据(2003-2023年)

省级-高标准农田面板数据(2003-2023年)

上一篇: test
下一篇: PHPUnit学习05---Mock使用进阶
weixin_34192993
博客等级 码龄11年 5343粉丝 175原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值