不同模板类型的多重继承

时间:2009-12-27 22:45:41

标签: c++ event-handling multiple-inheritance

我正在使用C ++进行事件处理并处理事件通知,我有一个类EventGenerator,任何生成事件的类都可以继承。 EventGenerator有一个方法,其他类可以用来添加回调,一个方法在事件发生时调用回调

为了处理不同类型事件的通知,我在模板类型T上对EventGenerator进行了参数化,然后通知程序类可以在不同类型的参数化上多次从EventGenerator继承。

为了完整起见,这里是EventGenerator的代码

#ifndef _EventGenerator
#define _EventGenerator

#include <list>

#include "EventListener.h"

template <class Event>
class EventGenerator {
private: 
    std::list<EventListener<Event>*> listeners;
protected:
    EventGenerator() {}

    void changeEvent(Event event) {
        std::list<EventListener<Event>*>::const_iterator it = listeners->begin();
        for (; it != listeners->end(); it++) {
            (*it)->changeEvent(event);
        }
    }

public:
    void addListener(EventListener<Event>* listener) {
            listeners->push_back(listener);
        }
};

#endif



这里是EventListener的代码,任何想要添加回调的类都继承自 -

#ifndef _EventListener
#define _EventListener

template <class Event>
class EventListener {
private:
    EventListener(const EventListener<Event>& event);
protected:
    EventListener() {}
public:
    virtual void changeEvent(Event event) = 0;
};

#endif



我觉得这不是一个非常好的设计,并且想知道是否有更好的设计来解决这个问题。

编辑:我正在使用多重继承这一事实令人烦恼。我经常被警告不要使用它,所以我想我想看看这样的设计是否会导致将来发生的坏事

由于

4 个答案:

答案 0 :(得分:2)

谨防钻石继承heirarchies。还要注意,重载虚函数是件坏事。所以,如果你有这样的事情:

class Handler : public EventHandler<int>, public EventHandler<string> { ... };

将调用哪个changeEvent()函数?不要指望它!

如果你小心上面的代码应该没问题,但如果你想完全避免继承,那么我建议使用与一些唯一标识符相关联的函数引用。举个例子:

class Listener { public: virtual ~Listener ( ) { } };
template<typename Event> class Distributor : public Listener {
    public:
    void addListener (shared_ptr<Listener>, function<void (Event)>);
    void listen (Event e) { 
        for_each(_listeners.begin(), _listeners.end(), 
            bind(&ListenNode::listen, _1, e));
    }
    private:
    struct ListenNode { 
        weak_ptr<Listener> listener;
        function<void (Event)> callback;
        void listen (Event e) {
            shared_ptr<Listener> l = listener.lock();
            if(l) callback(e);
        }
    };
    list<ListenNode> _listeners;
};

通过此设置,所有侦听器都可以虚拟地从一个基类派生。监听器可以注册多个回调,并且可以链接分销商。当然,您不必使用shared_ptr,但我喜欢它们,因为它们可以避免取消注册侦听器的麻烦。您可以按照自己喜欢的方式注册回调,将它们与字符串,整数或其他任何内容相关联。

我省略了很多细节,事件分发是一项复杂的业务。我认为Andrei Alexandrescu写了一篇关于这个主题的详细文章,请查阅。

答案 1 :(得分:1)

正如其他人所说,你可能会遇到钻石继承问题。此外,从事件处理程序库继承可能违反单一责任原则。我要做的是在需要知道某个事件的类中使用嵌套类:

class CNeedsToHandleEvent {
//...
private:

  void OnChange (Event event) {
    //Do processing of the event
  }

  class ChangeEventHandler : public EventListener {
    virtual void changeEvent(Event event) {
      CNeedsToHandleEvent* parent = OUTERCLASS(CNeedsToHandleEvent, m_ChangeEventHandler);
      parent->OnChange(event);
    }
  } m_ChangeEventHandler;

  friend class ChangeEventHandler;
};

这是OUTERCLASS宏。有些人可能认为它的使用有争议,也许它可能有可移植性问题,但它非常方便:

// Get a pointer to outer class of a nested class
#ifndef OUTERCLASS
#define OUTERCLASS(className, memberName) \
    reinterpret_cast<className*>(reinterpret_cast<unsigned char*>(this) - offsetof(className, memberName))
#endif

您始终可以使用指向父类的指针来实例化嵌套类,以便它可以调用实际处理程序而不是依赖宏。

答案 2 :(得分:0)

这是糟糕的设计,因为在你的情况下,当你发送事件时,事情可能不会起作用......除非你有一些聪明的元编程方法来迭代你派生的所有类型。

总的来说,我认为一般来说,或者这种类型的多重继承没有任何问题。

答案 3 :(得分:0)

当您创建使用事件的多个类派生的类时,如果这些父类使用相同的事件,您可能会遇到问题。

另外请注意,您需要使用以下语法来定义抽象回调函数:

template<class T>
class Base
{
    virtual void listener() = 0;
}

class Derived
    : public class Base<int>
    , public class Base<float>
{
    void Base<int>::listener(){...}
    void Base<float>::listener(){...}
}

如果你需要调用Derived中的任何一个,你可能需要使用类似的语法(虽然我真的不知道),但如果你从Base或Base引用它,它都会工作很好。另外,除非实际定义了侦听器功能,否则不会编译

或多或少,多重继承意味着您需要更多关注,但它是该语言的一个有价值的功能。我认为可能还有更多的模板诡计可以防止钻石继承,但代价是优雅。