首页 > 编程语言 > Effective C++——Item33: 避免隐藏继承的名字

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

时间:2023-10-07 22:25:22浏览次数:40  
标签:Effective mf1 作用域 void C++ Item33 Base virtual 隐藏

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,mf1,作用域,void,C++,Item33,Base,virtual,隐藏
From: https://www.cnblogs.com/qiangz/p/17747612.html

相关文章

  • Step by step guide to becoming a C++ developer in 2023
    https://roadmap.sh/cpphttps://roadmap.sh/backend......
  • C++学习网站
      LearnContemporaryC++|Concise&VisualExamples|hackingC++(hackingcpp.com) LearnC++–Skillupwithourfreetutorials(learncpp.com) cplusplus.com/doc/tutorial/   上百张图文并茂C++速查表  https://www.armbbs.cn/forum.php?mod=view......
  • 浅析C++ atomic
    早在C++11就在STL中引入了原子操作支持了。大部分时候,我使用C++11的atomic仅仅是为了原子地操作特定的一个变量,比如load、store、fetch_add等等。然而实际上,C++11的原子操作带着的memoryorder还能起到memorybarrier的作用。本文会从头介绍C++11原子变量的用法,使用的注意事项以及......
  • C++ 跨进程发送信号
    跨进程发送信号接受信号的进程//sig_wait.cpp#include<iostream>//#include<thread>#include<csignal>#include<unistd.h>usingnamespacestd;voidsignal_handler_no_parameter(){cout<<"getsignal:SIGURE1"<<......
  • vscode单步调试Android c++源码
    vscode单步调试Androidc++源码  目录步骤1.运行gdbclient.py脚本2.复制生成的launch.json并新建/home/jetson/android_aosp/aosp/.vscode/launch.json3.运行gdb即可,打断点参考 步骤注意:这个过程需要在Android源码环境中运行,可以使用adb端口转发工具,来......
  • vscode c++ 编译运行配置(信息学竞赛OIer专用)
    vscodec++编译运行OI专用配置在你的文件夹下建立一个名为\(\tt.vscode\)的文件夹。目录是这样的:\(\tt.vscode\)\(\tt|--c\_cpp\_properties.json\)\(\tt|--launch.json\)\(\tt|--tasks.json\)\(\tt.vscode/c\_cpp\_properties.json\){"configurations&qu......
  • C++将角度转为复数
    1.角度转复数,使用std::polar#include<iostream>#include<complex>#include<cmath>intmain(){floattheta=45;floattheta_pi=theta*(M_PI/180);std::cout<<"is"<<std::polar(1.0f,theta_pi)<<......
  • 十四天学会C++之第四天(面向对象编程基础)
    类和对象是什么?在C++中,类是一种用户定义的数据类型,它可以包含数据成员(也就是属性)和成员函数(也就是方法)。类是一种模板或蓝图,用于创建具体的对象。对象是类的实例,它是根据类的定义创建的,可以用来表示现实世界中的各种事物。对象具有类定义的属性和行为。面向对象编程思想面向对象编......
  • C++断言之assert和static_assert的区别
    C++断言之assert和static_assert的区别参考链接:c++11:static_assert与assert_夜夜夜夜-CSDN博客_static_assert断言分为动态断言和静态断言2种。c++11引入了static_assert关键字,用来实现编译期间的断言,叫静态断言。1.static_assert静态断言static_assert(常量表达式,要提示......
  • C++ 信号
    信号概念信号是Linux进程间通信的一种机制,是软件层次上对中断的一种模拟,用在进程之间的传递消息。来源内核产生内存错误,除0错误等由其他进程产生,传递给目标进程kill信号自定义信号:SIGURG硬件产生键盘处理方式发送阶段内核将信号放到对应的pendi......