随笔-89  评论-944  文章-0  trackbacks-0

我们先来看一组接口定义:

 

struct IX

{

    virtual void MethodX() = 0;

};

 

struct IXA : public IX

{

    virtual void MethodXA() = 0;

};

 

struct IXB : public IX

{

    virtual void MethodXB() = 0;

};

 

struct IXAB : public IXA, public IXB

{

    virtual void MethodXAB() = 0;

};

 

然后我们来实现 IX

 

struct CX : public IX

{

    virtual void MethodX()

    {

   

    }

};

 

然后再来实现 IXA

 

struct CXA : public IXA

{

    virtual void MethodX()

    {

   

    }

 

    virtual void MethodXA()

    {

   

    }

};

 

这样写当然是没问题的。但是,有木有发现,这里出现MethodX了。如果,我们需要之前CX中对MethodX的实现,那怎么办呢?

 

一开始我傻傻地想,继承CX不就好了么:

 

struct CXA : public IXA, public CX

{

    virtual void MethodXA()

    {

   

    }

};

 

结果实例化CXA的时候报错:

error C2259: 'CXA' : cannot instantiate abstract class

due to following members:

void IX::MethodX(void)' : is abstract

see declaration of 'IX::MethodX'

 

(嘿嘿。我不知道是不是挺多人一开始都会这么想的。)究其原因,是因为添加多继承不会产生覆盖虚函数的效果。此时虚函数的状态是:

 

MethodX

来自于IXA

来自于IX

未实现

MethodXA

来自于IXA

 

CXA实现

MethodX

来自于CX

来自于IX

CX实现

 

新添加的CX中的MethodX,只是覆盖了CX多继承的IX中的那个MethodX。退一步,即便CX是个独立的类,没有继承于IXCX中的MethoxX也无法为覆盖IXA中的MethodX。只有处于同一继承链中的各个类才会产生覆盖虚函数的效果。

 

假如,CX就是继承于IXA的,且只实现了MethodX,未实现MethodA

 

struct CX : public IXA

{

    virtual void MethodX()

    {

   

    }

};

 

然后CXA不再继承IXA,而是直接继承CX,补充实现MethodA

 

struct CXA : public CX

{

    virtual void MethodXA()

    {

   

    }

};

 

这样,CXA是可以直接实例化的。但是,CX又无法单独使用了!

 

CX面临的问题是,需要随时被改变基类,也就是基类类型可配置。于是我们想到了模版:

 

template <typename T = IX>

struct CX : public T

{

    virtual void MethodX()

    {

   

    }

};

 

struct CXA : public CX<IXA>

{

    virtual void MethodXA()

    {

   

    }

};

 

CX单独使用的时候,使用默认模版参数,让它继承于IX;被CXA使用的时候,让它继承于IXA,问题就解决了!同样的方法,我们实现CXB

 

struct CXB : public CX<IXB>

{

    virtual void MethodXB()

    {

   

    }

};

 

下面我们来实现IXAB

 

struct CXAB : public IXAB

{

    virtual void MethodX()

    {

   

    }

 

    virtual void MethodXA()

    {

   

    }

 

    virtual void MethodXB()

    {

   

    }

 

    virtual void MethodXAB()

    {

   

    }

};

 

注意到这里只有一个MethodX。由于MethodXIXAIXB是同源的,IXAB里面虽然本应该有两个MethodX,但编译器进行合并处理,对其中一个做跳转,所以我们只需要实现一次就够了。(目前我是这样理解的。)不过这不是本文要探讨的内容,我们关注的是,MethodXMethodXAMethodXB又要重复写一遍了。

 

看来我们要对CXACXB也做模版处理:

 

template <typename T = IXA>

struct CXA : public CX<T>

{

    virtual void MethodXA()

    {

   

    }

};

 

template <typename T = IXB>

struct CXB : public CX<T>

{

    virtual void MethodXB()

    {

   

    }

};

 

struct CXAB : public CXA<IXAB>, public CXB<IXAB>

{

    virtual void MethodXAB()

    {

   

    }

};

 

可惜的是,还是有错误:

error C2259: 'CXAB' : cannot instantiate abstract class

due to following members:

'void IXB::MethodXB(void)' : is abstract

see declaration of 'IXB::MethodXB'

'void IXA::MethodXA(void)' : is abstract

see declaration of 'IXA::MethodXA'

 

说的是CXA<IXAB>中的MethodXB未实现,CXB<IXAB>中的MethodXA未实现……所以,以上方案不适合接口中存在多继承的情形。

 

恰好COM中貌似也没有接口多继承的情形。我们将CX改名为IXImpl,将CXA改名为IXAImpl,将CXB改名为IXBImpl

 

struct IX

{

    virtual void MethodX() = 0;

};

 

struct IXA : public IX

{

    virtual void MethodXA() = 0;

};

 

struct IXB : public IX

{

    virtual void MethodXB() = 0;

};

 

template <typename T = IX>

struct IXImpl : public T

{

    virtual void MethodX()

    {

   

    }

};

 

template <typename T = IXA>

struct IXAImpl : public IXImpl<T>

{

    virtual void MethodXA()

    {

   

    }

};

 

template <typename T = IXB>

struct IXBImpl : public IXImpl<T>

{

    virtual void MethodXB()

    {

   

    }

};

 

是不是跟ATL对上号了?这些*Impl类可以直接使用(如果是需要默认实现就够了),也可以被继承,被继承的时候也可以更换基类。如:

 

struct CXA : public IXAImpl<>

{

 

};

 

struct IY : public IXB

{

    virtual void MethodY() = 0;

};

 

struct CY : public IXBImpl<IY>

{

    virtual void MethodY()

    {

   

    }

};

 

第二个例子中,IXB是库提供的接口,IY是应用程序设计的接口,CY是应用程序实现的类。

 

好了,ATL中的*Impl类的做法就是如此,只是多了其他一些模版参数,暂时不理它们。

 

posted on 2012-09-02 13:56 溪流 阅读(2779) 评论(1)  编辑 收藏 引用 所属分类: C++WindowsCOM

评论:
# re: 学习下 ATL 的继承链处理 2012-09-04 00:30 | 毕达哥拉斯半圆
good point!  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理