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. 大语言模型的应用探索—AI Agent初探!

    前言 大语言模型的应用之一是与大语言模型进行聊天也就是一个ChatBot,这个应用已经很广泛了. 接下来的一个应用就是AI Agent. AI Agent是人工智能代理(Artificial Inte ...

  2. mac svn管理工具

    App Store中搜索snailsvn 分付费(98元)和免费试用

  3. 洛谷P3009

    #include<iostream> #include<utility> using namespace std; typedef long long ll; #define ...

  4. PHP中引用的详解(引用计数、写时拷贝)

    转载:https://blog.csdn.net/ljguo212/article/details/8972865 1. PHP中引用的特性 PHP中引用意味着用不同的名字访问同一个变量内容,引用不是 ...

  5. [oeasy]python0041_teletype历史_博多码_shift_capslock_字符数字切换_gear

    teletypewriter 历史 回忆上次内容 上次见到了一个真的机械打字机 感受到了蒸汽朋克的时代背景 上上次区分了一些概念 terminal终端,电脑连线最终的端点 TeleTYpewriter ...

  6. 彻底理解Linux的DISPLAY变量的作用

    背景 最近遇到个两年前遇到的问题,使用virt-manager提示(virt-manager:873): Gtk-WARNING **: 14:53:28.147: cannot open displ ...

  7. 第六节 JMeter基础-中级登录【用户自定义变量】

    1.认识JMeter (1)配置元件:配置对应的一些数据 (例如:HTTP请求默认值.用户定义的变量) (2)[HTTP请求默认值]:HTTP请求默认值是设置的Web服务器部分信息,可以贯穿多个接口. ...

  8. MySQL预处理语句PREPARE、EXECUTE、DEALLOCATE使用大全

    说明 MySQL官方将PREPARE.EXECUTE.DEALLOCATE统称为PREPARE STATEMENT,我习惯称其为[预处理语句]. 其语法为: PREPARE stmt_name FRO ...

  9. 句子成分&分类 被动

    句子成分&分类 简单句的两个主要句子成分,分别是主语 + 谓语 谓语的核心是谓语动词 谓语 不等于 谓语动词 主语 谓语 [The rabbit] [ate a carrot] ate 为谓语 ...

  10. 【Java】用户在线人数统计的简单实现

    一.需求效果: 就是进入首页时能查看在线人数,没有特定要求,那我就不刷这个接口了 就进入首页加载一次 二.实现思路: 思路参考博客: https://blog.csdn.net/GitLuckyd/a ...