内容简介:前几天在写程序时,发现一个多重继承类,调用virtual函数会出现一个问题,该问题比较隐晦(因为不会引起程序core dump等严重的效果,我是很偶然的在单元测试中发现的),不容易定位,但是如果出现,可能对程序逻辑会带来致命的问题。
本文目的
前几天在写程序时,发现一个多重继承类,调用virtual函数会出现一个问题,该问题比较隐晦(因为不会引起程序core dump等严重的效果,我是很偶然的在单元测试中发现的),不容易定位,但是如果出现,可能对程序逻辑会带来致命的问题。
一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <iostream> using namespace std; class Base1{ public: virtual void foo1() {}; }; class Base2{ public: virtual void foo2() {}; }; class MI : public Base1, public Base2{ public: virtual void foo1 () {cout << "MI::foo1" << endl;} virtual void foo2 () {cout << "MI::foo2" << endl;} }; int main(){ MI oMI; Base1* pB1 = &oMI; pB1->foo1(); Base2* pB2 = (Base2*)(pB1); // 指针强行转换,没有偏移 pB2->foo2(); pB2 = dynamic_cast<Base2*>(pB1); // 指针动态转换,dynamic_cast帮你偏移 pB2->foo2(); return 0; } |
你会认为屏幕上会输出什么?是下面的结果吗?
这样认为没有什么不对的,因为C++的多态性保证用父类指针可以正确的找到子类实现,并调用。所以会有上面的输出。
但是,现实却不是这样,下面是真实的输出:
(以上实现在VC 2005和Linux Gcc 4.1.2效果一致)
为什么
为什么会出现上面的情况呢,上面代码中的注释部分也许解释了,这里再来详细的来分析一下。
首先,C++使用一种称之为vtable(google “vtable” for more details)的东西实现virtual函数多态调用。vtable每个类中都有一个,该类的所有对象公用,由编译器帮你生成,只要有virtual函数的类,均会有vtable。在继承过程中,由于类Base1和类Base2都有vtable,所以类MI继承了两个vtable。简单的分析一下对象oMI内存结构,如下:
其实很简单,就两个vtable的指针,0和4代表相对地址,指针地址大小为4。
pB1的值为0(pB1 == 0),所以调用“pB1->foo1()”时,可以正确的找到MI::fool这个函数执行。
但是当使用强行转换,将pB1转给pB2,那么实质上pB2的值也是0(pB2 == 0),当调用“pB2->foo2()”时,无法在第一个vtalbe中找到对应的函数,但是却不报错,而是选择执行函数MI::foo1,不知道为什么会有这种行为,但是这种行为却十分恶心,导致结果无法预期的(最后调用的函数会与函数申明的循序有关),不太会引起注意,使得bug十分隐晦。
可以设想,当一个有复杂的业务逻辑的程序,而类似这种函数调用和指针强行转换分布在不同的函数或模块中,可想而知,bug定位十分困难。
当使用动态转换时,也就是“pB2 = dynamic_cast(pB1)”,dynamic_cast函数会根据尖括号中的类型进行指针偏移,所以pB2的值为4(pB2 == 4),这样调用“pB2->foo2()”就会按照期望的方式执行。
结论
上面的现象在单继承中是不会出现的,因为只有一个vtable(子类的virtual函数会自动追加到第一个父类的vtable的结尾)。所以不会出现上面的现象,而多重继承却出现了上面的想象,所以需要注意以下两点:
1. 多重继承需要慎用
2. 类型转换尽量采用c++内置的类型转换函数,而不要强行转换
参考资料
以上所述就是小编给大家介绍的《C++多重继承要慎用!》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- CaffeineCache 慎用weakKeys
- 慎用golang中的map,特别是在并发操作中
- 慎用!BLEU 评价 NLP 文本输出质量存在严重问题
- 慎用 ToLower 和 ToUpper,小心把你的系统给拖垮了
- 028.Python面向对象继承(单继承,多继承,super,菱形继承)
- PHP类继承、接口继承关系概述
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
如何把事情做到最好
乔治·伦纳德 / 张乐 / 中国青年出版社 / 2014-2 / 29.90元
•改变全球9800万人的人生指导书 •全美第一本系统阐述学习与成功之道的经典著作 •长期盘踞全美畅销书榜单 •21年后,这本传奇之书终于在中国震撼上市 •把事情做到最好,第一不强求天赋,第二不介意起步的早晚,你要做的就是“起步走”并“不停地走” 《如何把事情做到最好》出 版于1992年,经久不衰,经过一代又一代的读者口碑相传后,畅销至今。作者以其独特的视角告诉人们,如......一起来看看 《如何把事情做到最好》 这本书的介绍吧!
Base64 编码/解码
Base64 编码/解码
SHA 加密
SHA 加密工具