多继承时基类命名冲突的解决方案

众所周知,C++与其他语言(如C#,JAVA)一个很大的不同就是C++支持从多个类继承。
但是多继承经常遇到这样一种情况,如果有两个或多个基类有相同名字和标记的方法,
继承类该怎么去实现。如有2个基类,代码如下:

class  CBaseA
{
public:
    
virtual ~CBaseA(){}
    
virtual  void  Init(){}
};

class CBaseB
{
public:
    
virtual ~CBaseB(){}
    
virtual  void  Init(){}
};

 

小弟我研究了一下MFC和ATL中COM实现的部分,发现它们都很好地解决了这一问题,难能可贵的是

它们的解决方式都不一样。下面我简单地介绍一下。由于MFC和ATL源码都比较庞大,而且充斥着很多宏,

在屏幕中列出来势必会影响篇幅,小弟不才,仿照它们的风格简单地写个示例。

先看MFC风格的实现:

MFC采取嵌套类的方式来模拟出现这种问题的多继承,有点麻烦而且不直观,但却是很有一般性。先上代码:

 

//MFC风格
class CDeriveMFCStyle
{
public:
    
void  BaseAInit()
    {
        AfxMessageBox( _T(
"BaseAInit") );
    }
    
void  BaseBInit()
    {
        AfxMessageBox( _T(
"BaseBInit") );
    }

    
class XBaseA : public CBaseA
    {
        
virtual  void  Init();
    }m_xBaseA;

    
class XBaseB : public CBaseB
    {
        
virtual  void  Init();
    }m_xBaseB;

    template
< typename Ty >
    
void CastTo( Ty** ppBase )
    {
        ASSERT( ppBase 
!= NULL );

        
if( typeid( Ty ) == typeid( CBaseA ) )
            
*ppBase  =   ( Ty* )&m_xBaseA;
        
else if( typeid( Ty ) == typeid( CBaseB ) )
            
*ppBase  =   ( Ty* )&m_xBaseB;
    }
};

void  CDeriveMFCStyle::XBaseA::Init()
{
    CDeriveMFCStyle  
*pThis = (CDeriveMFCStyle*)( (BYTE*)this - offsetof( CDeriveMFCStyle, m_xBaseA ));
    pThis
->BaseAInit();
}

void  CDeriveMFCStyle::XBaseB::Init()
{
    CDeriveMFCStyle  
*pThis = (CDeriveMFCStyle*)( (BYTE*)this - offsetof( CDeriveMFCStyle, m_xBaseB ));
    pThis
->BaseBInit();
}

 

从代码可以看出,CDeriveMFCStyle嵌套了两个类XBaseA和XBaseB,它们分别继承自CBaseA和CBaseB,

并且CDeriveMFCStyle声明了实例m_xBaseA和m_xBaseB做为成员变量。在XBaseA的Init实现中有这样的一行代码:

CDeriveMFCStyle  *pThis = (CDeriveMFCStyle*)( (BYTE*)this - offsetof( CDeriveMFCStyle, m_xBaseA ));

这段代码很精妙,看了好几遍我才看懂,原来在嵌套类中获取外覆类的实例可以这样做啊。以前我只会在嵌套类里保存

外覆类的指针,确实落了下乘。获得外覆类的指针后,直接调用外覆类的成员方法BaseAInit,BaseAInit方法是专门为CBaseA的Init方法实现的。对于每个基类都可以以此类推。

由于是模拟多继承,而不是真的继承,因此要想将CDeriveMFCStyle 实例动态地转换成父类指针,必须提供一个成员方法进行转换,在这里我使用了模板方法CastTo,内部就是一个简单的分支语句来实现的。

使用示例:

        CDeriveMFCStyle *pDerive = new CDeriveMFCStyle;

    CBaseA 
*pBaseA = NULL;
    pDerive
->CastTo( &pBaseA );
    pBaseA
->Init();

    CBaseB 
*pBaseB = NULL;
    pDerive
->CastTo( &pBaseB );
    pBaseB
->Init();

    delete pDerive;

看看效果,基本上模拟了多继承,而且很好地解决了基类命名的冲突。

 

下面介绍一下ATL风格的解决方案:

照惯例先上代码:

//ATL风格
template< typename TDerive >
class CAdaptBaseA : public CBaseA
{
public:
    
virtual  void  Init()
    {
        TDerive  
*pThis = static_cast<TDerive*>this );
        pThis
->BaseAInit();
    }
};

template
< typename TDerive >
class CAdaptBaseB : public CBaseB
{
public:
    
virtual  void  Init()
    {
        TDerive  
*pThis = static_cast<TDerive*>this );
        pThis
->BaseBInit();
    }
};

class  CDeriveATLStyle : public CAdaptBaseA<CDeriveATLStyle>public CAdaptBaseB<CDeriveATLStyle>
{
public:
    
void  BaseAInit()
    {
        AfxMessageBox( _T(
"BaseAInit") );
    }
    
void  BaseBInit()
    {
        AfxMessageBox( _T(
"BaseBInit") );
    }
};
这种实现真用了继承,而没有模拟。不过在继承中增加了一个中间层,CAdaptBaseA和CAdaptBaseB,这两个类是模板类,分别继承自CBaseA和CBaseB,模板参数是即将从它们派生的子类。这种模板是典型的ATL风格的模板,十分精妙。中间适配类CAdaptBaseA的Init实现中有一行代码:
TDerive  *pThis = static_cast<TDerive*>this );

这段代码,我初学ATL的时候也是看了好多遍才看懂,不过既然看懂了也就爱上这种风格了,同MFC风格的实现一样,获得子类实例pThis后,就直接调用子类中针对CBaseA::Init的实现BaseAInit成员方法了。

使用示例:

    CDeriveATLStyle *pDerive = new CDeriveATLStyle;

    CBaseA 
*pBaseA = dynamic_cast<CBaseA*>(pDerive);
    pBaseA
->Init();

    CBaseB 
*pBaseB = dynamic_cast<CBaseB*>(pDerive);
    pBaseB
->Init();

    delete pDerive;

好了,两种实现都介绍完毕了,欢迎诸位大侠提出批评。

参考资料:

《MFC Window程序设计》

《深入解析ATL(第2版)》

posted @ 2009-03-31 20:20  孤竹君  阅读(560)  评论(0编辑  收藏  举报