zoukankan      html  css  js  c++  java
  • 多继承时基类命名冲突的解决方案

    众所周知,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版)》

  • 相关阅读:
    vim常用命令
    MYSQL用户管理
    RPM 命令
    Windows下使用xShell向远程Linux上传文件
    Linux PHP 安装过程出现的错误
    完整的 http 错误代码含义解释
    Linux gzip压缩输出
    高性能Mysql主从架构的复制原理及配置详解
    Mybatis多个参数传值方法
    jsp 九大内置对象和其作用详解
  • 原文地址:https://www.cnblogs.com/fangkm/p/1426535.html
Copyright © 2011-2022 走看看