首页 > 编程语言 >C++:46---绝不重新定义继承而来的non-virtual函数

C++:46---绝不重新定义继承而来的non-virtual函数

时间:2022-11-01 14:35:02浏览次数:51  
标签:non mf 函数 46 绑定 virtual public class

一、看一个隐藏non-virtual函数的例子

  • 假设class D以public的方式继承于class B,代码如下:
class B {
public:
void mf();
};
class D :public B {};
int main()
{
D x;
B *pB = &x;
pB->mf(); //调用B::mf()
D *pD = &x;
pD->mf(); //调用D::mf()
return 0;
}

二、静态绑定与动态绑定

  • 关于静态绑定、动态绑定的概念之前,大家先了解下静态类型的类变量和动态类型的类变量概念和区别。
  • 静态类型的类变量:在编译时就已经知道是什么类型的了
  • 动态类型的类变量:自己所指的类型不明确,直到运行时才知道
  • 如果表达式既不是引用也不是指针,那么其就没有静态类型和动态类型的概念,因为其只能与自己类型一致的对象绑定到一起

演示案例

  • 当我们使用基类的引用(或指针)时,我们并不清楚该引用(或指针)所绑定的对象的真实类型,该对象可能是基类的对象,也可能是派生类的对象。只有在程序运行的时候我们才知道所绑定的对象的真实类型


  1. class A {};
  2. class B:public A{};

  3. int main()
  4. {
  5. A a; //静态类型
  6. B b; //静态类型
  7. A *p; //动态类型

  8. p = &a; //p指向了a
  9. p = &b; //p又指向了b
  10. return 0;
  11. }



  1. class A {
  2. protected:
  3. int a;
  4. public:
  5. void setA(int num) { a = num; }
  6. void cout_getA() { cout << "A" << a << endl; }
  7. };

  8. class B :public A {
  9. public:
  10. void setA(int num) { a = num; }
  11. void cout_getA() { cout << "B" << a << endl; }
  12. };

  13. int main()
  14. {
  15. A a;
  16. B b;
  17. a.setA(10);
  18. b.setA(20);

  19. A *p1, *p2;
  20. p1 = &a;
  21. p2 = &b;

  22. p1->cout_getA();
  23. p2->cout_getA();
  24. return 0;
  25. }


  • 结果解析:
  • A 10:这个比较简单,因为a的类型为A,且指针也为A,因此调用A的getA()函数
  • A 20:虽然p2指针指向的类类型为B,但是访问规则只与指针/引用的类类型有关,而与指针/引用指向的类型无关。因此b已经被视为一个A对象来看了。此处p2指针的类型为A,因此调用A的getA()函数。又因为b对象使用setA()函数将整个继承体系中的a改为了20,因此打印出来的a为20


静态绑定

  • 当我们调用non-virtual函数时,调用的函数版本与指针的类型有关
  • 例如,上面的pB指针在初始化时,将与D对象中的B对象所绑定;上面的pD指针在初始化时,将与D对象所绑定。这是静态绑定
  • 因此,pB调用的是B::mf();pD调用的是D::mf()

动态绑定

  • 当我们调用virtual函数时,调用的函数版本与指针所指的对象有关
  • 对virtual函数的调用,是在代码运行期间执行的。例如,如果上面的D::mf()是一个虚函数,那么会有:
class B {
public:
virtual void mf();
};
class D :public B {
public:
virtual void mf();
};
int main()
{
D x;
B *pB = &x;
pB->mf(); //调用D::mf()
D *pD = &x;
pD->mf(); //调用D::mf()
return 0;
}

三、为什么不建议派生类隐藏基类的non-virtual函数

  • 在条款32介绍过,public继承意味着is-a关系,条款34描述了为什么在class内声明一个non-virtual函数会为该class建立起一个不变性,凌驾其特异性。如果:
  • 我们在派生类中隐藏了基类的non-virtual函数,那么基类与派生类就会产生行为上的不一致,is-a关系就消失了
  • 如果想要表现出派生类与基类的不同,那么应该将函数声明为virtual(其中虚析构函数是一个例子)

四、总结

  • 绝对不要重新定义继承而来的non-virtual函数


标签:non,mf,函数,46,绑定,virtual,public,class
From: https://blog.51cto.com/u_14934686/5813637

相关文章

  • 深入理解 virtual 关键字
      引言为什么会写这篇文章?主要是因为项目中的代码大量使用了带virtual关键字的类,想通过本文浅谈一下。virtual并没有什么超能力可以化腐朽为神奇,它有其存在的理由,但......
  • 洛谷 P1464 Function(dfs+记忆化搜索)
    https://www.luogu.com.cn/problem/P1464单个返回条件的时候,直接return多个返回条件的时候,采用记忆化搜索思想,边存储边继续往下搜索中间穿插记忆化判断,如果之前有过此......
  • 单片机 ADXL346 IIC通讯
    ​​IIC协议链接​​/*-----------------------------------------头文件-----------------------------------------*/#include"ADXL346.h"#include"math.h"/*--------......
  • 【题解】P4683 [IOI2008] Type Printer
    题目传送门:P4683[IOI2008]TypePrinter板子题贪心+字典树+dfs贪心:把最长的字符留在最后打或者最先打把每个字母插入字典树对trie树dfs一遍#include<cstdio>#inclu......
  • Tree Longest Path 2246
    2246. LongestPathWithDifferentAdjacentCharactersHardYouaregivena tree (i.e.aconnected,undirectedgraphthathasnocycles) rooted atnod......
  • python yield 会什么打印none?
     看一个小例子:cattest.pydefgen1():foriinrange(10):x=yieldiprint("x=%s"%x)这段代码执行结果如下:>>>importtestast>>>......
  • 646 案例全选表格 and647 表单校验
    全选表格、<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>表格全选</title><style>table{border:1pxs......
  • 利用virtualenvwarrper管理虚拟环境
    一、背景python虚拟环境隔离的方法有很多,包括conda,virtualenv等。在不使用conda的情况下,virtualenv+virtualenvwarrper的方法会比较方便。网上例子有很多,这里记录一下自己......
  • 【XSY4746】会议选址(三度化,边分治,点分治)
    这种关键点的重心问题,一般有两种思路。一种是合并:对于两个不交的点集\(S,T\),\(S\cupT\)的重心必在\(S,T\)重心间的路径上,二分即可,需要数据结构支持dfn区间内\(S\c......
  • (数据科学学习手札146)geopandas中拓扑非法问题的发现、诊断与修复
    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes1简介大家好我是费老师,geopandas作为在Python中开展GIS分析的利器,可以......