多重继承中的动态绑定

项目开发日志(四)

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();
};

然后在另外一个类中使用传入的指针回调CInheritSecondonThirdCall函数:

/*  
 * 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

发表评论

邮箱地址不会被公开。 必填项已用*标注