"一次实现"可以在某些场合替代菱形继承?

      前不久在C++板块请教过这样一个问题:“多个基类中相同的纯虚函数,只需在派生类中实现一次”是否是标准行为。这个问题直到现在我也还没能确定,不过我在VC2005、VC2008和g++上都试过,该特性在所有这些编译器上都可以正常工作,所以应该八九不离十了。另外,记得当时星星有“语法正确逻辑说不过去”一说,那时想想确实也有些别扭。不过,最近在工作中又遇到了一个类似的问题,再加上一些突发奇想,于是产生了标题中的这个想法。考虑下面的三个类:

   

    ClientOne通过调用Server的method_one()和method_two()方法和Server交互,ClientTwo通过调用Server的余下方法与Server交互。通常在设计良好的OO项目中,为了降低各对象之间的依赖,像这种问题都需要提取接口,并根据客户对接口进行隔离,那么,上面的依赖关系就会变成下面这样:

   

    现在Server只要直接实现InterfaceOne和InterfaceTwo这两个接口就行了,接口实现在C++中表现为继承,所以在C++中Server直接派生自这两个基类就行了。问题似乎解决了。但如果ClientOne和ClientTwo都需要通过另一个叫做“shared_method()"的方法与Server交互,那会怎样?对,继续提取接口:

   

    这里出现了菱形继承,所以在C++中就需要使用虚继承,代码就会是这样:

    class SharedInterface {
    public:
        virtual void shared_method() = 0;
        virtual ~SharedInterface() {}
    };
        
    class InterfaceOne : public virtual SharedInterface {
    public:
        virtual void method_one() = 0;
        virtual void method_two() = 0;
        virtual ~InterfaceOne() {}
    };
    
    class InterfaceTwo : public virtual SharedInterface {
    public:
        virtual void method_three() = 0;
        virtual void method_four() = 0;
        virtual ~InterfaceTwo() {}
    };
        
    class Server : public InterfaceOne, public InterfaceTwo {
    public:
        virtual void method_one();
        virtual void method_two();
        virtual void method_three();
        virtual void method_four();
        virtual void shared_method();
        ...
    };       

    有些时候并不是所有人都愿意使用虚继承,所以某些人就会突发奇想,看看自吸磁力泵能否回避之。我就是其中之一。根据”一次实现“,我把shared_method下推至InterfaceOne和InterfaceTwo,这样就可以把SharedInterface去掉:

   

    哈哈,不但去掉了虚继承,还直接少了一层继承,这个世界干净啦!当然,这个方法对于两个接口中存在很少部分的重叠方法还是挺方便的,但如果重叠方法过多,还是应该提取出一个公共接口,以避免自吸磁力泵过多的重复。

评论列表
周星星2014-4-7 15:44:47
re: “一次实现”可以在某些场合替代棱形继承?

从编译器实现手段上讲,你的这个做法应该是安全的。

"有些时候并不是所有人都愿意使用虚继承"
--- 虚继承挺好的呀!我觉得虚继承唯一的缺点是 如果不知道当时或以后会有InterfacdeTwo的话则无法确定InterfacdeOne需不需要虚继承SharedInterface。

如果接口交叉过多的话,使用虚继承可能层次变得非常复杂且难于扩充,可以使用另一种解决手段是,
class Server
{
public:
    void shared_method();
    void method_one();
    void method_two();
    void method_three();
    void method_four();
};

// InterfaceOne.hpp
class Server;
class InterfaceOne
{
public:
    void shared_method();
    void method_one();
    void method_two();
private:http://www.hrbust.edu.cn/
    Server* pServer_;
};
// InterfaceOne.hpp
#include "Server.hpp"
void InterfaceOne::shared_method() { pServer_->shared_method(); }
void InterfaceOne::method_one() { pServer_->method_one(); }
void InterfaceOne::method_two() { pServer_->method_two(); }
笨笨猪2014-4-7 15:44:47
re: “一次实现”可以在某些场合替代棱形继承?
TO:星星
你的这种思想很好!很多时候我们的确是在滥用继承关系,经常在需要使用委托的时候却选择了继承。不过对于当前这个问题,我觉得继承要比委托更恰当,我想到的有两方面原因:
1. 因为我在这里要表达的是接口与实现之间的关系(我所说的接口指的是纯抽象类,不包含任何状态,我们只能通过生成子类的实例来使用它),而不是其他的什么关系。
2. 继承比委托更简单,虽然委托具有更大的灵活性,但在这里不必要。如果在这个问题上使用委托,我们就必须再额外多管理两个对象的生命周期,因为现在InterfaceOne和InterfaceTwo变成了具体类,而不是接口,必须要被创建和管理。

我并不是说虚继承怎么样不好,而是引入虚继承之后,就出现了不一致性,我必须要时刻记住哪里该用常规继承,哪里该用虚继承。
另外,我觉得这个问题的更本原因是因为C++不支持interface关键字造成的。对于那些有状态的基类,菱形继承中如果不使用虚继承肯定会产生二义性,但是对interface这样的无状态的纯抽象基类,菱形继承中不存在任何二义性

posted @ 2014-05-27 11:19  lanhe  阅读(240)  评论(0编辑  收藏  举报
数据中心