首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C++:46---绝不重新定义继承而来的non-virtual函数

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

作者头像
用户3479834
发布于 2021-02-03 06:51:30
发布于 2021-02-03 06:51:30
51300
代码可运行
举报
文章被收录于专栏:游戏开发司机游戏开发司机
运行总次数:0
代码可运行

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

  • 假设class D以public的方式继承于class B,代码如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
}

二、静态绑定与动态绑定

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

演示案例

  • 当我们使用基类的引用(或指针)时,我们并不清楚该引用(或指针)所绑定的对象的真实类型,该对象可能是基类的对象,也可能是派生类的对象。只有在程序运行的时候我们才知道所绑定的对象的真实类型
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {};class B:public A{};
int main(){A a;  //静态类型B b;  //静态类型A *p; //动态类型
p = &a; //p指向了ap = &b; //p又指向了breturn 0;}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class A {protected:int a;public:void setA(int num) { a = num; }void cout_getA() { cout << "A" << a << endl; }};
class B :public A {public:void setA(int num) { a = num; }void cout_getA() { cout << "B" << a << endl; }};
int main(){A a;B b;a.setA(10);b.setA(20);
A *p1, *p2;p1 = &a;p2 = &b;
p1->cout_getA();p2->cout_getA();return 0;}
  • 结果解析:
    • 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()是一个虚函数,那么会有:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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函数
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 游戏开发司机 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验