[Effective C++]条款36-37 两个绝不
本文初发于 “天目中云的小站”,同步转载于此。
条款36 : 绝不重新定义继承而来的non-virtual函数
本条款很容易理解, 援引以前的条款就可以说明为什么 :
-
条款34中就提到过 : non-virtual函数意味着接口 + 强制性实现继承, 它不应当被改变.
-
重新定义继承而来的non-virtual函数会触发条款33中所说的遮掩机制.
-
触发遮掩机制其实是对条款32中"public意味着is-a"这个定理的破坏.
如果你这么做了, 还可能会出现以下奇怪的效果 :
class B {
public:void mf();...
};
class D: public B { // D派生自B
public:void mf(); // 这个定义遮掩了B中的mf()
}; //--------------------------------------------//D x; // 创建一个D对象B *pB = &x;
pB->mf(); // 调用B::mf()D *pD = &x;
pD->mf(); // 调用D::mf()
我们可以看到通过同一个对象D调用的mf()居然效果不同, 这也很容易理解, 毕竟non-virtual并没动态绑定, 只是依据当前对象的静态类型来调用的.
请记住 :
- 任何情况下都不应重新定义一个继承来的non-virtual函数.
条款37 : 绝不重新定义继承而来的缺省参数值
先缩小本条款的范围, 通过条款36我们首先应该知道不应该修改继承来的non-virtual函数, 所以本条款的讨论范围将局限在"继承一个带有缺省参数的virtual函数".
首先明确本条款的核心知识 :
- virtual函数本身是动态绑定的, 但是缺省参数值是静态绑定的.
简单理解就是virtual函数会根据对象的当前类型进行动态绑定, 而缺省值无法改变, 只和一开始定义的静态类型相关.
我们来看代码来理解 :
class Shape {
public:enum ShapeColor { Red, Green, Blue }; virtual void draw(ShapeColor color = Red) const = 0; // Shape默认缺省值为Red...
};class Rectangle: public Shape {
public:// 这是一个糟糕的写法!virtual void draw(ShapeColor color = Green) const; // Shape默认缺省值为Green, 但是有用吗?...
};class Circle: public Shape {
public:virtual void draw(ShapeColor color) const; // 无缺省值...
};
这里设计了一个Shape基类, 派生出三角和圆, 我们来看调用时会发生什么 :
// 以下的静态类型都是基类
Shape* ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle; pc->draw();
pr->draw();
这样子调用看上去没有任何问题, 实际上也确实依靠动态绑定调用到了派生类对应的draw(), 但根据测试pc->draw()中color缺省值是Red而非Green, 这便是因为我们上述的理由, 缺省值看的是静态类型, 也就是Shape.
-
为什么C++会设计成会这样?
其实是因为动态绑定缺省值的花销实在过大, C++为了效率做了这般取舍.
由此引发出的另一个问题
- 当静态类型是派生类时, 将无法获取基类的缺省值!
// 这样可以使用基类的缺省值
Shape* pc = new Circle;
pc->draw();// 这样不行!
Circle* cc = new Circle;
cc.draw(); // 错误, 自己没有缺省值, 并且也调不到基类的缺省值
于是这样的机制似乎在逼迫我们要把所有含缺省值的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 = Red) const; // 都加上和基类相同的缺省值...
};class Circle: public Shape {
public:virtual void draw(ShapeColor color = Red) const; // 都加上和基类相同的缺省值...
};
只有这样你才可以确保在任何使用场景下你的缺省值都可以正常生效.
但是这样值得吗? 显然是不值得的, 这里包含了代码重复和代码相依性两大弊病, 只要你想修改基类中的缺省值, 那么其他所有的派生类就都要修改, 这是我们非常不希望看到的, 还好我们有一个现成的解决方法.
藉由NVI手法解决上述问题
没错, 就是我们条款35详细介绍的NVI手法(没看过的可以看我往期博客的对应部分, 这将很影响下文的理解), 我们让非虚函数接口有缺省值, 如果有缺省情况, 把这个缺省值传入具体实现的虚函数就可以了! 代码如下 :
class Shape {
public:enum ShapeColor { Red, Green, Blue };void draw(ShapeColor color = Red) const // 非虚函数接口(包含缺省值), 见条款35{doDraw(color); // 具体实现的虚函数接受color}...
private:virtual void doDraw(ShapeColor color) const = 0; // 见条款35
}; class Circle: public Shape {
public:...
private:virtual void doDraw(ShapeColor color) const; // 这样就不必再加缺省值了! ...
};//-----------------------//Circle* cc = new Circle;
cc.draw(); // 这样的调用也被允许了!
请记住 :
- 绝对不要重新定义一个继承而来的缺省参数值, 因为它是静态绑定的.
- 再想让virtual函数携带缺省值是, 不妨使用NVI手法, 让非虚接口替虚函数携带缺省值.
by 天目中云
相关文章:
[Effective C++]条款36-37 两个绝不
本文初发于 “天目中云的小站”,同步转载于此。 条款36 : 绝不重新定义继承而来的non-virtual函数 本条款很容易理解, 援引以前的条款就可以说明为什么 : 条款34中就提到过 : non-virtual函数意味着接口 强制性实现继承, 它不应当被改变. 重新定义继承而来的non-…...
各种网站(学习资源及其他)
欢迎围观笔者的个人博客~ 也欢迎通过RSS网址https://kangaroogao.github.io/atom.xml进行订阅~ 大学指南 上海交通大学生存手册中国科学技术大学人工智能与数据科学学院本科进阶指南USTC不完全入学指南大学生活质量指北科研论 信息搜集 AI信息搜集USTC飞跃网站计算机保研 技…...
docker怎么部署高斯数据库
部署高斯数据库(openGauss)到Docker的步骤如下: 安装Docker: 如果您的系统尚未安装Docker,需要先进行安装。以CentOS为例,可以使用以下命令安装Docker: yum install -y docker拉取镜像ÿ…...
VScode中配置ESlint+Prettier详细步骤(图文详情)
VScode中配置ESlintPrettier详细步骤(图文详情) 前置环境: node 18.19.0 vite 3.2.11 vue 3.2.47 本文将不在演示vue3基础工程创建,如果还没有vue3项目工程请参考文章: Vite创建Vue3工程并引入ElementPlus&#x…...
Leetcode打卡:考场就坐
执行结果:通过 题目: 855 考场就坐 在考场里,有 n 个座位排成一行,编号为 0 到 n - 1。 当学生进入考场后,他必须坐在离最近的人最远的座位上。如果有多个这样的座位,他会坐在编号最小的座位上。(另外&am…...
数据库压力测试详解
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 很多人提到 jmeter 时,只会说到 jmeter进行接口自动化或接口性能测试,其实jmeter还能对数据库进行自动化操作。个人常用的场景有以下&#…...
项目测试方案流程详解
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 作为一名软件测试工程师,为项目制作完成的测试方案并执行,是我们日常工作的重要部分,同时,也是一名合格的软件测试工…...
以二进制形式创建gitea仓库
1、官方文档: 数据库准备 | Gitea Documentation 使用二进制文件安装 | Gitea Documentation 2、具体操作 1)创建gitea数据库 2)检查是否安装 Git。要求 Git 版本 > 2.0。 如需升级git请参考以下链接:linux升级git版本-C…...
Spring(七)Spring Cloud----Feign、Zuul和Apollo
文章目录 一、服务调用Feign1.1 Feign的基本使用1.2 Feign的属性配置1.2.1 Ribbon配置1.2.2 Hystrix配置 二、网关服务Zuul2.1 Zuul的基本使用2.1.1 请求路由2.1.2 请求过滤 2.2 路由详解2.2.1 传统路由配置2.2.2 服务路由配置2.2.3 服务路由的默认规则2.2.4 自定义路由映射规则…...
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
选素数 小蓝有一个数 x,每次操作小蓝会选择一个小于 x 的素数 p,然后在 x 成为 p 的倍数前不断将 x 加 1,(如果 x 一开始就是 p 的倍数则 x 不变)。 小乔看到了小蓝进行了 2 次上述操作后得到的结果 n,他想…...
华为云环境下LVS/DR架构的故障诊断优化
本文作者:刘涛 文章目录 前言1.LVS/DR集群的问题2.华为云环境3.问题排查3.1 检查LVS/DR模式配置3.1.1 RS服务器3.1.2 DS服务器 3.2 继续分析抓包结果3.2.1 调整tcpdump抓包过滤条件3.2.2 client向集群VIP发包3.2.3 DS服务器arp消息 3.3 查看丢包3.3.1 监控DS和RS服…...
leetcode hot100除自身以外的数组的乘积
238. 除自身以外数组的乘积 已解答 中等 相关标签 相关企业 提示 给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在…...
SQL server学习09-数据库编程(上)
目录 一,了解T-SQL语言 1,常量(标量值) 2,变量 1)局部变量 2)全局变量 二,内置函数 1,字符串函数 2,数学函数 3,日期时间函数 4&#x…...
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
在刚刚过去的 FlutterInProduction 活动里,Flutter 官方除了介绍「历史进程」和「用户案例」之外,也着重提及了未来相关的 roadmap ,其中就有 3.27 里的 Swift Package Manager 、 Widget 实时预览 和 Dart 与 native 平台原生语言直接互操作…...
java全栈day19--Web后端实战(java操作数据库3)
一、MyBatis 1.1介绍 前提引入: controller(控制层)作用:接受请求,响应数据 service(业务层)作用:负责具体的逻辑处理 dao(持久层)作用:数据访问层 一般的访问流程:浏览器发起请求过来,先…...
【YashanDB知识库】Mybatis-Plus调用YashanDB怎么设置分页
本文内容来自YashanDB官网,原文内容请见 https://www.yashandb.com/newsinfo/7802958.html?templateId1718516 问题现象 Mybatis-Plus是Mybatis的增强工具,旨在简化开发者的CRUD操作,目前被广泛应用,Mybatis-Plus框架适配了多种…...
Ansible 批量管理华为 CE 交换机
注:本文为 “Ansible 管理华为 CE 交换机” 相关文章合辑。 使用 CloudEngine - Ansible 批量管理华为 CE 交换机 wsf535 IP 属地:贵州 2018.02.05 15:26:05 总体介绍 Ansible 是一个开源的自动化运维工具,AnsibleWorks 成立于 2012 年&a…...
基于自定义注解与 AOP 切面实现接口日志全面数据库存储
基于自定义注解与 AOP 切面实现接口日志全面数据库存储 一、引言 在当今复杂的软件系统开发与运维过程中,详细且精准地记录接口的各项信息对于系统性能监测、问题排查、安全审计以及业务分析都有着极为关键的意义。本文将深入讲解如何运用自定义注解与 AOP&#x…...
GraalVM完全指南:云原生时代下使用GraalVM将Spring Boot 3应用转换为高效Linux可执行文件
一、前言 在现代软件开发中,启动速度和资源利用率常常是衡量应用性能的关键指标。对于基于Spring Boot的应用来说,虽然它们易于开发和部署,但JVM的启动时间有时会成为一个瓶颈。本文介绍如何使用GraalVM将Spring Boot 3应用编译成原生Linux可执行文件,从而显著提高启动速度…...
单片机:实现驱动超声波(附带源码)
单片机实现驱动超声波模块 超声波模块(如HC-SR04)广泛应用于距离测量、避障系统、自动驾驶等嵌入式项目中。它能够通过发射超声波信号并接收反射波来计算物体的距离。本文将介绍如何使用单片机(如51系列单片机)驱动超声波模块&am…...
如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
spring Security对RBAC及其ABAC的支持使用
RBAC (基于角色的访问控制) RBAC (Role-Based Access Control) 是 Spring Security 中最常用的权限模型,它将权限分配给角色,再将角色分配给用户。 RBAC 核心实现 1. 数据库设计 users roles permissions ------- ------…...
6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
