Effective C++——Item33: 避免隐藏继承的名字

一、从原理理解隐藏

  1. 从变量作用域看隐藏

全局变量x和局部变量x的类型是不同的,但C++的隐藏规则:只隐藏名字(hiding names)。

int x;                        // global variable
void someFunc()
{
double x; // local variable
std::cin >> x; // read a new value for local x
}
  1. 对于继承而言,隐藏的原理即为:派生类作用域内嵌在基类作用域中。

  2. 这是为了强调我们正在谈论名称。该示例还可以包含类型名称,例如枚举、嵌套类和 typedef。在这次讨论中唯一重要的是它们的名字。

  3. 从例子理解名字(names)的查找过程

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() {
//..
mf2();
//..
}
};

在mf4()中查找mf2()的过程:

  1. 局部作用域mf4()函数
  2. 包含作用域Derived类
  3. 下一个包含作用域Base类
  4. Base类的namespace中查找

二、从实例理解隐藏

  1. 基类函数被隐藏,即使参数不同
#include <iostream>
using namespace std; class Base { private:
int x; public:
virtual void mf1() = 0;
virtual void mf1(int) {
cout << "Base::mf1(int)" << endl;
}
virtual void mf2() {
cout << "Base::mf2()" << endl;
}
void mf3() {
cout << "Base::mf3()" << endl;
}
void mf3(double) {
cout << "Base::mf3(double)" << endl;
}
}; class Derived: public Base {
public:
virtual void mf1() {
cout << "Derived::mf1()" << endl;
}
void mf3() {
cout << "Derived::mf3()" << endl;
}
void mf4() {
cout << "Derived::mf4()" << endl;
}
}; int main() {
Derived d;
d.mf1(); // call Derived::mf1
d.mf1(1); // error, Derived::mf1 hides Base::mf1, can't find Base::mf1(int)
d.mf2(); // ok, call Base::mf2
d.mf3(); //ok, hides Base::mf3, call Derived::mf3(),
d.mf3(1.0); // error, Derived::mf3() hides Base::mf3, can't find Base::mf3(double)
return 0;
}
  1. 如何使被隐藏的函数可见
  • using声明——使得所有某个名字的事物都可见

    using Base::mf1;

    using Base::mf3;

使得Base类中所有名为mf1、mf3的事物都在Derived类中可见。

#include <iostream>
using namespace std; class Base { private:
int x; public:
virtual void mf1() = 0;
virtual void mf1(int) {
cout << "Base::mf1(int)" << endl;
}
virtual void mf2() {
cout << "Base::mf2()" << endl;
}
void mf3() {
cout << "Base::mf3()" << endl;
}
void mf3(double) {
cout << "Base::mf3(double)" << endl;
}
}; class Derived: public Base {
public:
using Base::mf1;
using Base::mf3;
virtual void mf1() {
cout << "Derived::mf1()" << endl;
}
void mf3() {
cout << "Derived::mf3()" << endl;
}
void mf4() {
cout << "Derived::mf4()" << endl;
}
}; int main() {
Derived d;
d.mf1(); // ok
d.mf1(1); // ok, call Base::mf1(int)
d.mf2(); // ok, call Base::mf2
d.mf3(); //ok, call Derived::mf3(),
d.mf3(1.0); // ok, Base::mf3(double)
return 0;
}
  • 内联转发函数——不希望全部可见
class Base {
public:
virtual void mf1() = 0;
virtual void mf1(int);
}; class Derived: private Base {
public:
virtual void mf1() // forwarding function; implicitly inline
{
Base::mf1();
}
};
...
Derived d;
int x;
d.mf1(); // fine, calls Derived::mf1
d.mf1(x); // error! Base::mf1() is hidde

三、重载、重写、隐藏的区别

  1. 函数重载发生在相同作用域

重载时机:参数数量、类型、返回值、是否const函数

  1. 函数隐藏发生在不同作用域

隐藏时机:隐藏上一级同名变量、函数(不管参数类型、返回类型)

  1. 函数覆盖就是函数重写。准确地叫做虚函数覆盖和虚函数重写,也是函数隐藏的特例。

Effective C++——Item33: 避免隐藏继承的名字的更多相关文章

  1. effective C++ Item 33 避免隐藏继承而来的名字

    1. 普通作用域中的隐藏 名字实际上和继承没有关系.有关系的是作用域.我们都知道像下面的代码: int x; // global variable void someFunc() { double x ...

  2. 读书笔记 effective C++ Item 33 避免隐藏继承而来的名字

    1. 普通作用域中的隐藏 名字实际上和继承没有关系.有关系的是作用域.我们都知道像下面的代码: int x; // global variable void someFunc() { double x ...

  3. Effective JavaScript Item 40 避免继承标准类型

    本系列作为Effective JavaScript的读书笔记. ECMAScript标准库不大.可是提供了一些重要的类型如Array,Function和Date.在一些场合下.你或许会考虑继承当中的某 ...

  4. 1.隐藏继承的成员new / 虚方法(override)/ abstract / 多态 ----- 重写

    总结: 1. 在继承上, new/override没区别 2. 在多态上,new不支持多态,override支持 在C#中改变类中相同名称的方法的实现过程中有三种方式:重载.重写和覆盖. 重载:指具有 ...

  5. Effective C++ 34 区分接口继承和实现继承

    public继承从根本上讲,有两部分:接口继承和实现继承.两者之前的区别很像函数声明与函数定义. 具体设计中,会呈现三种形式:derived class只继承成员函数的接口(纯虚函数):derived ...

  6. Effective C++ 33 避免遮掩继承而来的名称

    首先介绍一个原则LSP(Liskov Substitution Principle),如果Class D以Public方式继承Class B,则所有B对象可以派上用场的任何地方,D对象一样可以派上用场 ...

  7. [Effective C++ --033]避免遮掩继承而来的名称

    这一章一直在想怎么写,因为比较基础,很容易理解,就按照需要来写吧. 假设我们有这样一个类: class Base { private: int x; public: ; virtual void mf ...

  8. 读书笔记 effective c++ Item 43 了解如何访问模板化基类中的名字

    1. 问题的引入——派生类不会发现模板基类中的名字 假设我们需要写一个应用,使用它可以为不同的公司发送消息.消息可以以加密或者明文(未加密)的方式被发送.如果在编译阶段我们有足够的信息来确定哪个信息会 ...

  9. Effective C++(20) 继承与面向对象设计

    本文主要参考<Effective C++ 3rd>中的第六章部分章节的内容. 关注的问题集中在继承.派生.virtual函数等.如: virtual? non-virtual? pure ...

  10. 读书笔记 effective C++ Item 40 明智而谨慎的使用多继承

    1. 多继承的两个阵营 当我们谈论到多继承(MI)的时候,C++委员会被分为两个基本阵营.一个阵营相信如果单继承是好的C++性质,那么多继承肯定会更好.另外一个阵营则争辩道单继承诚然是好的,但多继承太 ...

随机推荐

  1. Linux常用指令及shell脚本记录

    记录一些常用指令在博客上,以防哪天因太久不敲而忘却,还可以直接翻看博客记录,不用再一条条百度搜...... 一.Linux常用指令 一.设置文件权限为aapp用户及用户组-- chown -R app ...

  2. k8s 实战 3----标签

    如果你对k8s还不了解,可以看下前文k8s 实战 1 ---- 初识 (https://www.cnblogs.com/jilodream/p/18245222)k8s 实战 2 ---- pod 基 ...

  3. 国内版Unity 2023编辑器无法切换到DX12模式的解决方法

    2024.7.4更新:至6000.0.5f1c1,此问题已修复 在{安装目录}\Editor里(也就是Unity安装的根目录)建立一个名叫D3D12的文件夹. 去{安装目录}\Editor\Data\ ...

  4. 首届 DIVE 精彩回顾丨践行企业数字化,基础软件如何创新

    "墙高基下,虽得必失."在构建数字企业大厦的工程中,基础软件的重要性不言而喻.但对于各行各业而言,面向传统经营模式设计的基础软件已经难以支撑数字业务的创新,唯有汲取专业团队的经验, ...

  5. [oeasy]python0083_十进制数如何存入计算机_八卦纪事_BCD编码_Binary_Coded_Decimal

    编码进化 回忆上次内容 上次 研究了 视频终端的 演化 从VT05 到 VT100 从 黑底绿字 到 RGB 24位真彩色 形成了 VT100选项 从而 将颜色 数字化 了 生活中我们更常用 10个数 ...

  6. [oeasy]python0082_VT100_演化_颜色设置_VT选项_基础色_高亮色_索引色_RGB总结

    更多颜色 回忆上次内容 上次 了解了控制序列 背后的故事 一切标准 都是 从无到有 的 就连 负责标准的组织 也是 从无到有 的 VT-05 奠定了 基础颜色 黑底 绿字 隔行 扫描 但 多颜色设置 ...

  7. oeasy教您玩转vim - 40 - # 复制粘贴

    ​ 复制粘贴 回忆上节课内容 我们上次的内容是粘贴 小写p意味着在光标下面或者后面粘贴 大写P意味着在光标上面或者前面粘贴 p的意思是放上去,就是put 把什么放上去呢? 把 reg 中 " ...

  8. 封装的grid控件

    class CGridCtrl : public CWnd { DECLARE_DYNAMIC(CGridCtrl) public: void Create(CWnd* pParent, DWORD ...

  9. 系动词&使役动词

    系动词 系动词的作用就是赋值 I am a rabbit 把 a rabbit赋值给i我 我是一只兔子 The rabbit is smart 这兔子是聪明的 smart赋值给兔子 系动词连系的方式, ...

  10. 从pytest源码的角度分析pytest工作原理

    从pytest源码的角度分析pytest工作原理 从 pytest 源代码的角度来分析其工作原理,我们需要关注几个关键的部分,特别是 pytest 的启动过程以及测试的收集与执行.下面是基于 pyte ...