多继承时基类命名冲突的解决方案
众所周知,C++与其他语言(如C#,JAVA)一个很大的不同就是C++支持从多个类继承。
但是多继承经常遇到这样一种情况,如果有两个或多个基类有相同名字和标记的方法,
继承类该怎么去实现。如有2个基类,代码如下:
{
public:
virtual ~CBaseA(){}
virtual void Init(){}
};
class CBaseB
{
public:
virtual ~CBaseB(){}
virtual void Init(){}
};
小弟我研究了一下MFC和ATL中COM实现的部分,发现它们都很好地解决了这一问题,难能可贵的是
它们的解决方式都不一样。下面我简单地介绍一下。由于MFC和ATL源码都比较庞大,而且充斥着很多宏,
在屏幕中列出来势必会影响篇幅,小弟不才,仿照它们的风格简单地写个示例。
先看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实现中有这样的一行代码:
这段代码很精妙,看了好几遍我才看懂,原来在嵌套类中获取外覆类的实例可以这样做啊。以前我只会在嵌套类里保存
外覆类的指针,确实落了下乘。获得外覆类的指针后,直接调用外覆类的成员方法BaseAInit,BaseAInit方法是专门为CBaseA的Init方法实现的。对于每个基类都可以以此类推。
由于是模拟多继承,而不是真的继承,因此要想将CDeriveMFCStyle 实例动态地转换成父类指针,必须提供一个成员方法进行转换,在这里我使用了模板方法CastTo,内部就是一个简单的分支语句来实现的。
使用示例:
CBaseA *pBaseA = NULL;
pDerive->CastTo( &pBaseA );
pBaseA->Init();
CBaseB *pBaseB = NULL;
pDerive->CastTo( &pBaseB );
pBaseB->Init();
delete pDerive;
看看效果,基本上模拟了多继承,而且很好地解决了基类命名的冲突。
下面介绍一下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") );
}
};
这段代码,我初学ATL的时候也是看了好多遍才看懂,不过既然看懂了也就爱上这种风格了,同MFC风格的实现一样,获得子类实例pThis后,就直接调用子类中针对CBaseA::Init的实现BaseAInit成员方法了。
使用示例:
CBaseA *pBaseA = dynamic_cast<CBaseA*>(pDerive);
pBaseA->Init();
CBaseB *pBaseB = dynamic_cast<CBaseB*>(pDerive);
pBaseB->Init();
delete pDerive;
好了,两种实现都介绍完毕了,欢迎诸位大侠提出批评。
参考资料:
《MFC Window程序设计》
《深入解析ATL(第2版)》