项目开发日志(四)
C++是一种自由度非常高的语言,特别是面向对象中的动态绑定特性,正确使用这种特性会你写代码非常舒畅.但是错误的使用有时候也很致命.
今天在编写战斗逻辑层代码的时候在释放技能回调的地方使用了C++的多重继承,在一个继承了几个回调基类(技能释放完毕回调,移动完毕回调等)的派生类里面出现了无法绑定到正确的基类对象的问题.仔细分析后 发现是由于在第一层类型转换的时候已经把指针转换为void*
,然后在最后一层调用无法正确转换.下面把问题重现:
假设我们的基类是:CInheritFirst
,CInheritSecond
,派生类是CInheritMain
如下:
/* * InheritMain.h * * Created by Rect on 2014-5-30 16:11. * Copyright (c) 2014年 shadowkong.com. All rights reserved. */ struct CInheritFirst { virtual void onFirstCall() = 0; virtual void onSecondCall() = 0; }; struct CInheritSecond { virtual void onThirdCall() = 0; }; class CInheritMain: public CInheritFirst,public CInheritSecond { public: CInheritMain(void); ~CInheritMain(void); virtual void onFirstCall(); virtual void onSecondCall(); virtual void onThirdCall(); };
然后在另外一个类中使用传入的指针回调CInheritSecond
的onThirdCall
函数:
/* * InIeritTest.h * * Created by Rect on 2014-5-30 16:27. * Copyright (c) 2014年 shadowkong.com. All rights reserved. */ #include#include "InheritMain.h" class CInIeritTest { public: CInIeritTest(void){}; ~CInIeritTest(void){}; void test(void* pCSecind = NULL) { CInheritSecond* pTest = (CInheritSecond*)pCSecind; printf("CInIeritTest::test begin\n"); pTest->onThirdCall(); printf("CInIeritTest::test end\n"); }; };
在main
中如下调用:
CInheritMain *pMain = new CInheritMain(); CInIeritTest *pTest = new CInIeritTest(); pTest->test((CInheritSecond*)pMain); pTest->test(pMain);
两次调用test
发现会发生不一样的效果:
pTest->test((CInheritSecond*)pMain)
正确了onThirdCall
;
pTest->test(pMain)
确调用了onFirstCall
也就是说:
- 在第一次调用的时候函数指针动态绑定到了
CInheritSecond
- 在第二次调用的时候函数指针动态绑定到了
CInheritFirst
为什么会发生这种事情呢?
由于我们传入的指针默认转换为void*
pTest->test((CInheritSecond*)pMain)
调用pTest->test((CInheritSecond*)pMain)
的时候 指针先绑定到CInheritSecond
在转换到void*
,这样在test
函数中函数指针无论绑定到那个类 其实都只有CInheritSecond
了.其实转换成任意一个其他类,在调用函数的时候 都会搜索函数表.由于CInheritSecond
只有一个函数 所以无论指针怎么转换 他都只有一个函数可以调用.
pTest->test(pMain)
调用pTest->test(pMain)
其实传入的是CInheritMain*
,这样在使用的时候 指针转换 无论怎么转换 其实都是CInheritMain*
,无论转换到那个基类,调用那个函数,都会调用到CInheritMain
类来.这个类有三个函数,对象函数表里也只有三个函数指针.
可能没有实际测试过代码 看起来有点模糊,总结一句话 就是:
对于传入任何A类型指针,只要形参是void*
,那在函数内部无论转换成B,C,D,E,F还是G,那这个指针实际上都是A.
代码
Sample托管在我的github