【问题标题】:Multiple inheritance: calling all the overriden functions多重继承:调用所有被覆盖的函数
【发布时间】:2019-08-05 20:32:36
【问题描述】:

我有几个我希望班级拥有的行为。我想隔离这些行为,以便我可以重用该代码,随意混合和匹配。

例如,一种方法是:

class BehaviorAbstract {
  protected:
     virtual void processInfo(Info i) = 0;
}

class Behavior1: public BehaviorAbstract {
   protected:
     virtual void processInfo(Info i) { ... }
     void performBehavior1()  { ... }
}

class Behavior2: public BehaviorAbstract {
   protected:
     virtual void processInfo(Info i) { ... }
     void performBehavior2()  { ... }
}

class ConcreteObject: public Behavior1, Behavior2 {
   protected:
     void processInfo(Info i) {
       // needs to call processInfo of Behavior1 and Behavior2
       Behavior1::processInfo(i);
       Behavior2::processInfo(i);
     }
     void perform() {
       this->performBehavior1(); this->performBehavior2();
     }
 }

所以问题的关键是:ConcreteObject 需要调用它所继承的所有类的两个函数processInfo(同名,同一个参数)。想象一下,所有的行为类都是由不同的开发人员编写的。该函数必须具有相同的名称,因为它们都派生自 BehaviorAbstract。

这样做的合理设计模式是什么?我怀疑多重继承在这里可能是错误的,也许“多重组合”会更好,但我需要所有 Behavior 类和 ConcreteObject 派生自 BehaviorAbstract 并且它们都需要在同一个受保护的BehaviorAbstract 的数据成员。

我上面写的解决方案感觉错误和丑陋。有没有办法自动调用所有实现 processInfo 的父类,而无需显式地重写它们的名称?

非常感谢您的帮助。

【问题讨论】:

  • 警告在使用多重继承时使用虚拟继承(class Behavior1: virtual public BehaviorAbstract { 等)制作“钻石”,添加缺少的“;”,并将最小方法放在 protected 中比私人
  • 你的代码有很多语法错误,你所有的方法都是私有的,但是一旦我修复了代码似乎没问题:wandbox.org/permlink/9fu554mXWXrPSzcV
  • 谢谢,会解决问题
  • 如果我错了,请纠正我,但我认为问题是关于重构 ConcreteObject 类以避免使用多重继承......你试过模板吗?特征还是政策?
  • 另外我建议你阅读xy problem。您的问题集中在您已经意识到您不喜欢它的解决方案上,但尚不清楚您要解决的实际问题是什么

标签: c++ design-patterns multiple-inheritance


【解决方案1】:

如果我猜对了,那么这个问题是关于重构 ConcreteObject 类的。

方法一:

如果您可以使performBehavior() 成为BehaviorAbstract 基类的一部分,那么您可以简单地使用BehaviorAbstract* 的向量并让多态来完成它的工作。我认为这可以看作是一种策略模式。

#include <iostream>
#include <vector>

typedef int Info;

struct BehaviorAbstract
{
    virtual void processInfo(Info i) = 0;
    virtual void performBehavior() = 0;
};

struct Behavior1 : BehaviorAbstract 
{
     void processInfo(Info i) override
     { std::cout<< "Behavior1::processInfo()" <<std::endl; }

     void performBehavior() override
     { std::cout<< "Behavior1::performBehavior()" <<std::endl; }
};

struct Behavior2 : BehaviorAbstract
{
     void processInfo(Info i) override
     { std::cout<< "Behavior2::processInfo()" <<std::endl; }

     void performBehavior() override
     { std::cout<< "Behavior2::performBehavior()" <<std::endl; }
};

//------------------------------------------------//

struct ConcreteObject
{
    typedef std::vector<BehaviorAbstract*> vec_behavior;

    vec_behavior vba;

    ConcreteObject(vec_behavior &&v) : vba(v)
    {;}

    void processInfo(Info i)
    {
        for (auto &&itr : vba)
            itr->processInfo(i);
    }

    void perform()
    {
        for (auto &&itr : vba)
            itr->performBehavior();
    }
};

int main()
{
    ConcreteObject foo = {{new Behavior1(), new Behavior2()}};
    foo.processInfo(23);
    foo.perform();
}

示例:https://rextester.com/UXR42210

方法 #2:

使用创建元组的可变参数模板。迭代该元组并运行函数。同样,如果performBehavior1()performBehavior2() 可以共享相同的函数名,那么它会变得更容易。这里的额外复杂性是您需要编写手动迭代该元组的方法。为简单起见,我直接从 iterate_tuple 结构体中调用了 processInfo()

#include <iostream>
#include <tuple>

typedef int Info;

struct BehaviorAbstract
{
     virtual void processInfo(Info i) = 0;
};

struct Behavior1 : BehaviorAbstract 
{
     void processInfo(Info i) override
     { std::cout<< "Behavior1::processInfo()" <<std::endl; }

     void performBehavior1()
     { std::cout<< "Behavior1::performBehavior1()" <<std::endl; }
};

struct Behavior2 : BehaviorAbstract
{
     void processInfo(Info i) override
     { std::cout<< "Behavior2::processInfo()" <<std::endl; }

     void performBehavior2()
     { std::cout<< "Behavior2::performBehavior2()" <<std::endl; }
};


//------------------------------------------------//

template<typename T, std::size_t N>
struct iterate_tuple 
{
    static void run(T &t, Info i) 
    {
        std::get<N>(t).processInfo(i); 
        iterate_tuple<T, N-1>::run(t,i); 
    }
}; 

template<typename T>
struct iterate_tuple<T, 0> 
{
    static void run(T &t, Info i) 
    {
        std::get<0>(t).processInfo(i); 
    }
};

//------------------------------------------------//

template<typename ...T>
struct ConcreteObject
{
    std::tuple<T ...> tmp;
    static constexpr std::size_t tuple_size = std::tuple_size<decltype(tmp)>::value;

    ConcreteObject() : tmp{std::forward<T>(T()) ...}
    {;}

    void processInfo(Info i)
    {
        iterate_tuple<decltype(tmp), tuple_size-1>::run(tmp, i);
    }

    void perform()
    {
        std::get<0>(tmp).performBehavior1();
        std::get<1>(tmp).performBehavior2();
    }
};


int main()
{
    ConcreteObject<Behavior1,Behavior2> foo;
    foo.processInfo(23);
    foo.perform();
}

示例:https://rextester.com/SBRE16218

这两种方法都避免了多重继承,据我了解,这是您想要避免的。仅供参考,越简单越好。

【讨论】:

  • 谢谢,这很酷。这确实是我所追求的。策略模式很有趣。我读它的方式,这只是一种组合模式,由多个对象组合而成。是否有权威的设计模式列表?当您说战略模式时,是几本书会说的,还是业界公认的?
  • @DevShark:当然!我会推荐以下关于设计模式的书籍:1) 设计模式 - Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 的可重用面向对象软件的元素,2) Robert Nystrom 的游戏编程模式,3) Head First Design Patterns埃里克弗里曼,伊丽莎白弗里曼。后一本书 (3) 是用 Java 编写的,并没有涵盖所有众所周知的设计模式,但它是一本有趣的书,可以让您顺利地了解设计模式。第一本书 (1) 是您书架上的必备品。这就像一本关于设计模式的字典。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-01
  • 2014-02-17
  • 2019-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多