Effective C++条款33:避免遮掩继承而来的名称(Avoid hiding inherited names)

news2025/6/15 14:48:54

Effective C++条款33:避免遮掩继承而来的名称(Avoid hiding inherited names)

  • 条款33:避免遮掩继承而来的名称
    • 1、同名全局变量在局部作用域中被隐藏
    • 2、继承中的隐藏
    • 3、进一步论证——继承中的函数的隐藏
    • 4、如何将隐藏的行为进行覆盖
      • 4.1 通过using声明增加对基类成员函数的使用
      • 4.2 使用forwarding函数
    • 5、牢记
  • 总结


《Effective C++》是一本轻薄短小的高密度的“专家经验积累”。本系列就是对Effective C++进行通读:

第6章:继承与面向对象设计

在这里插入图片描述


条款33:避免遮掩继承而来的名称

1、同名全局变量在局部作用域中被隐藏

  名称其实和继承无关,而是和作用域(scopes)有关。如下面这段代码:

int x; //全局变量
 
void someFunc()
{
    double x; //局部变量
    std::cin >> x; //局部变量赋值
}

  当全局和局部存在相同的变量时,在局部作用域中,全局作用域的变量名会被隐藏,优先使用局部的变量。
在这里插入图片描述

  C++的名称遮掩规则所做的唯一事情是:遮掩名称。至于名称是否是同一类型,并不重要。

2、继承中的隐藏

  现在进入继承。我们知道当我们处在一个派生类成员函数内部时,并且指向了一些基类的东西(例如,一个成员函数,一个typedef或者一个数据成员),编译器能够找到我们所指向的东西,因为派生类继承了声明在基类中的这些东西。实际的工作方式是派生类的作用域被嵌套在基类作用域内部。举个例子:

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf2();
    void mf3();
    ...
};
 
class Derived :public Base
{
public:
    virtual void mf1(); //重写(覆盖)
    void mf4();
    ...
};

在这里插入图片描述
  这个类混合了public和private名称,以及一组成员变量和成员函数的名称。成员函数包括纯虚函数,普通虚函数(非纯虚函数)以及非虚函数。在这次的讨论中我们唯一关心的是他们是名称。至于他们是什么样的类型无关紧要。这个例子使用的是单继承,但是一旦你明白了在单继承下会发生什么,多继承下的C++行为很容易就能够预推测出来。

假设派生类中的mf4定义如下:

void Derived::mf4()
{
	...
	mf2();
	...
}

  当编译器看到这里使用了名字mf2,它们必须理解mf2指向的是什么。它们会在作用域中寻找名字为mf2的一个声明。

  在Derived的fm4()函数中调用了fm2()函数,对于fm2()函数的查找顺序如下:

  • ① 先在fm4()函数中查找,如果没有进行②
  • ② 然后在Derived类中查找,如果没有进行③
  • ③ 然后在基类Base中查找(查找到了就调用基类中的Base)
  • ④ 假设在Base中还没有查找到,那么就在Base所在的namespace中查找;如果还有没继续在全局作用域查找

3、进一步论证——继承中的函数的隐藏

  考虑前面的例子,这次我们除了要重载mf1和mf3之外,还在Derived中添加一个mf3版本。(正如条款36中解释的,Derived中mf3的声明——一个继承而来的非virtual函数——会让这个设计看起来很可疑,但是为了理解继承下的名字可见性,我们忽略这个问题。)

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);
    ...
};
 
class Derived :public Base
{
public:
    virtual void mf1(); //基类中的所有mf1()都被隐藏
    void mf3();         //基类中的所有fm3()都被隐藏
    void mf4();
    ...
};

现在使用下面代码进行调用:

Derived d;
int x;
... 
d.mf1();  //正确
d.mf1(x); //错误,被隐藏了
d.mf2();  //正确
d.mf3();  //正确
d.mf3(x); //错误,被隐藏了

  可以看到,对于有相同名字的基类和派生类中的函数,即使参数类型不同,上面的隐藏规则也同样适用,并且它和函数的虚与非虚没有关系。在这个条款开始也是同样的方式,函数someFunc中的double x隐藏了全局作用域的int x,在这里Derived中的函数mf3隐藏了基类中名字为mf3的函数,即使参数类型不一样。

4、如何将隐藏的行为进行覆盖

4.1 通过using声明增加对基类成员函数的使用

  有时隐藏可能会违反基类与派生类之间的is-a关系。因此我们可以使用using声明表达式取消这种隐藏,在派生类中导入基类的函数行为。如下面例子:

class Base
{
private:
    int x;
public:
    virtual void mf1() = 0;
    virtual void mf1(int);
    virtual void mf2();
    void mf3();
    void mf3(double);
    ...
};
 
class Derived :public Base
{
public:
    using Base::mf1; //Base所有版本的mf1函数在派生类作用域都可见
    using Base::mf3; //Base所有版本的mf3函数在派生类作用域都可见
 
    virtual void mf1(); //重写mf1()函数
    void mf3();         //隐藏了mf1(),但是mf3(double)没有隐藏
    void mf4();
    ...
};

现在有下面的调用代码:

Derived d;
int x;
 
d.mf1();  //正确,调用Derived::mf1()
d.mf1(x); //正确,调用Base::mf1(int)
 
d.mf2();  //正确,调用Derived::mf2()
 
d.mf3();  //正确,调用Derived::mf3()
d.mf3(x); //正确,调用Base::mf3(double)

  如果你的继承基类并加上重载函数,你想对其中的一些函数进行重新定义或者覆盖,你需要为每个即将被隐藏掉的名字包含一个using声明,如果你不这样做,你想继承的一些名字就会被隐藏。

4.2 使用forwarding函数

  有时候你并不想继承基类的所有函数。在public继承下,这绝对不可能发生,因为它违反了基类和派生类之间public继承的”is-a”关系。(这也是为什么上面的using声明放在派生类的public部分:基类中的public名字在public继承的派生类中应该也是public的)。然而在private继承中(见条款39),它也是有意义的。举个例子,假设Derived私有继承自基类Base,Derived类想继承基类函数mf1的唯一版本是不带参数的版本。Using声明在这里就不工作了,因为一个using声明会使得所有继承而来的函数的名字在派生类中是可见的。这里可以使用不同的技术,也就是简单的forwarding函数:

class Base {
public:
	virtual void mf1() = 0;
	virtual void mf1(int);
	...
};
class Derived: private Base {
public:
	virtual void mf1() 
	{ Base::mf1(); } 
	...
}; 
...

用下面代码进行调用:

Derived d;
int x;
d.mf1(); // fine, calls Derived::mf1
d.mf1(x); // error! Base::mf1() is hidden

  inline转交函数的另一个用途是为那些不支持using声明式(注:这并非正确行为)的老旧编译器另开了一条新路,将继承而得的名称汇入派生类作用域内。

  当继承同模板结合起来的时候,一个完全不同的“继承而来的名字被隐藏”问题就会出现,详情见 条款43。

5、牢记

  • derived classes内的名称会隐藏base classes内的名称。在public继承下从来没有人希望如此。

  • 为了让被隐藏的名称再见天日,可使用using声明式或转交函数

总结

期待大家和我交流,留言或者私信,一起学习,一起进步!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/104576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vTESTstudio入门到精通 - 如何自动化控制Simulation节点_03

我们工作中经常会遇到需要仿真大量的CAN/CANFD报文的情况,通常我们只能通过人工去测试,因为很难实现仿真控制大量报文的发送和停止?那我们该如何去解决呢? 今天我们主要来解决这个问题,通过CAPL去控制simulation节点的仿真发送和停止,最大限度的在实验室仿真实车的报文数…

段错误产生原因

嵌入式C开发&#xff0c;或多或少都遇到段错误&#xff08;segmentation fault &#xff09;。 下面是一些典型的段错误产生的原因&#xff1a; 访问不存在的内存地址 访问只读的内存地址 栈溢出 内存越界 …… 实例 1. 访问不存在的内存地址 #include <stdio.h>in…

小学生C++编程基础 课程7(A)

897.a到b (课程7&#xff09; 难度&#xff1a;1 登录 898.2位偶数 &#xff08;课程7&#xff09; 难度&#xff1a;1 登录 899.从0开始&#xff08;课程7&#xff09; 登录 900.前面数 &#xff08;课程7&#xff09; 登录 901.奇数 (课程7) 登录 902.7的倍数 (课程7) …

第二证券|新冠药销售占比不到1.5%,三连板医药龙头跌停!

今天早盘&#xff0c;A股商场延续调整态势&#xff0c;沪指震动失守3100点整数关口&#xff0c;深证成指、创业板指跌幅在1%左右。 虽然指数体现不佳&#xff0c;但个股层面不乏亮点。从涨跌份额来看&#xff0c;早盘A股商场有2695只股票上涨&#xff0c;2017只股票跌落&#x…

计算机毕设Python+Vue学生在线请假管理系统(程序+LW+部署)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

双向扩散模型

🍿*★,*:.☆欢迎您/$:*.★* 🍿 简单的描述是一个图先扩散为噪声而后 噪声扩散为另一个图 这样的思路类似如 当人类看到图之后 开始联想和脑补 不断的脑补 一步一步的 脑补出另一幅图 比如给定图的一部分 脑补出另一部分 与传统的扩散模型 不同的点在于 初始给定的是一个…

2022 UUCTF Web

目录 <1> Web (1) websign(禁用js绕过) (2) ez_rce(?>闭合 rce) (3) ez_unser(引用传递) (4) ez_upload(apache后缀解析漏洞) (5) ezsql(union注入) (6) funmd5(代码审计 %0a绕过preg_replace) (7) phonecode(伪随机数漏洞) (8) ezpop(反序列化字符串逃逸) …

Sharding-JDBC(三)按月分表

目录1.Maven 依赖2.创建表结构3.yml 配置4.TimeShardingAlgorithm.java 分片算法类5.ShardingAlgorithmTool.java 分片工具类6.ShardingTablesLoadRunner.java 初始化缓存类7.SpringUtil.java Spring工具类8.代码测试9.测试结果背景&#xff1a; 项目用户数据库表量太大&#x…

20221220英语学习

今日新词&#xff1a; hurt adj.&#xff08;身体上&#xff09;受伤的, &#xff08;感情上&#xff09;受伤的 blood n.血, 血统, 有…类型的血的, 家世 sister adj.姐妹的, 同类(型)的 humour n.&#xff08; humor&#xff09;幽默, 心情, 情绪, 脾气 holiness n.神圣…

UX设计师是做什么的,现在怎么样

对设计领域略知一点的学生都知道UX设计已经流行了很长一段时间&#xff0c;几年前它的平均工资甚至超过了程序员&#xff0c;那么它的未来会一如既往地保持它的热度吗&#xff1f;未来稳定增长的高薪会下降吗&#xff1f;UX可以称之为科技领域的新秀。根据去年的数据统计&#…

AnQICMS 安装步骤教程

AnQICMS 安装步骤教程 支持的系统 支持 Windows 7、Windows 8、Windows 10、Windows 11、Windows server 各个版本。 Windows XP 未测试 支持 Ubuntu、Centos、Red Hat、Debian 等 基于 X86 的 Linux 版本。 支持 MacOS。 Linux服务器上部署AnQiCMS 先从 https://www.anqic…

力扣(LeetCode)169. 多数元素(C++)

抵消法 多数元素的数量比其他所有元素的总数还多。那么从前往后遍历&#xff0c;遇到相同元素&#xff0c;计数 111 &#xff0c;遇到不同元素&#xff0c;计数 −1-1−1 &#xff0c;考虑边界&#xff0c;当旧数的出现次数减到 000 &#xff0c;那么新数就可以替换旧数&#…

携手华为,瑞金医院病理科为健康数字化保驾护航

作者 | 曾响铃 文 | 响铃说 人生在世&#xff0c;沧桑流转&#xff0c;到了晚年&#xff0c;各种疾病袭来&#xff0c;总是无法避免&#xff0c;总要坦然接受。 只是&#xff0c;这个时候&#xff0c;能够知道自己究竟得的是什么病&#xff0c;才好去积极面对、笑对苦难。 …

前端简单案例——扩展卡

效果展示 色块可以替换成图片&#xff0c;改变background-color为background-image即可。 html代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&quo…

图片加载引入的内存溢出问题分析

Android ImageView进行图片加载时&#xff0c;经常会遇到内存溢出的问题&#xff0c;本文针对于这一问题出现的定义、原理、过程、解决方案做统一总结。 1.一些定义 在分析具体问题之前&#xff0c;我们先了解一些基本概念&#xff0c;这样可以帮助理解后面的原理部分。当然了…

实时通讯技术Ajax,WebSocket,SSE

实时通讯技术是一项基于web开发的重要技术&#xff0c;网站是需要前后端通讯的&#xff0c;因此数据刷新的时间就是获取信息的时间&#xff0c;为了能准确而有快速的获取信息需要尽可能的提高信息的刷新效率。 常见的实时通讯技术&#xff1a; 通讯方式AjaxCometWebSocketSSE…

从0到1学会开发前端脚手架

【课程简介】 在前端开发中经常会用到create-vue, create-react-app这类脚手架&#xff0c;它可以帮助我们快速生成一个配置化的项目&#xff0c;提高开发效率。现在很多大厂都有自己研发的脚手架&#xff0c;掌握脚手架的使用&#xff0c;并且自己能开发脚手架&#xff0c;能…

涵盖全场景构建方方面面!魅族2023-2025年产品矩阵曝光

在万物互联的时代大背景下&#xff0c;一众以智能手机闻名的科技厂商们开始了全场景概念上的推进构建&#xff0c;形如早前作为国产智能手机「领头羊」的老牌手机厂商魅族&#xff0c;就在近日公布了2023-2025年全场景多终端沉浸式的全方位产品矩阵。 从中可以看到&#xff0c…

解读最佳实践:倚天 710 ARM 芯片的 Python+AI 算力优化 | 龙蜥技术

编者按&#xff1a;在刚刚结束的 PyCon China 2022 大会上&#xff0c;龙蜥社区开发者朱宏林分享了主题为《ARM 芯片的 PythonAI 算力优化》的技术演讲。本次演讲&#xff0c;作者将向大家介绍他们在倚天 710 ARM 芯片上开展的 PythonAI 优化工作&#xff0c;以及在 ARM 云平台…

SCI 论文插图格式一般要求

插图是反映 SCI 文章品质的核心指标之一&#xff01;&#xff01;&#xff01; 图片格式要求:图片一般可以保存为TIFF、JPEG、EPS这三种常见格式,并存为独立文件。 二、图片色彩要求:一般要求为CMYK或RGB色彩。 1.尺寸符合杂志社的要求(宽度8.3~17.6厘米,高度一般不超过20厘米…