代码库的一个非常常见的模式是Listener事件,如下所示:

 class Frobulator
 {
 public:
      class Listener
      {
      private:
          // only frobulators can frob
          friend class Frobulator;
          virtual void onFrobbed() = 0;
      }

      void maybeFrob()
      {
          // assume we always frob, but maybe we only do it sometimes
          // and the caller won't know if a call will do it
          for (auto& l: listeners)
          {
               l->onFrobbed();
          }
      }

      void addListener(Listener* l)
      {
           listeners.push_back(l);
      }

 private:
     std::vector<Listener*> listeners;
 }

然后,一个类继承Listener并可以使用Frobulator注册为侦听器。当Frobulator在某个调用者(不一定是监听者)调用后跳动时,将通知监听者。

然后,我真正的问题是,侦听器是否应该“内部”侦听,因此需要对Frobulator的非常量引用,但需要使用private Listener性质进行管理?
 class FrobReactor: private Frobulator::Listener
 {
 public:
      FrobReactor(Frobulator& frobulator_)
           frobulator(frobulator)
      {
           frobulator.addListener(this);
      }

 private:
      void onFrobbed() override
      {
           // react!
      }

      Frobulator& frobulator;
 }

 // and externally ...
 Frobulator theFrobber;
 FrobReactor reactor(theFrobber);
 theFrobber.maybeFrob();

还是应该让侦听器采用const引用(如果不需要,甚至不需要引用),承认FrobReactor不会修改Frobulator,但要宣传它是Frobulator::Listener并期望客户端代码将其连接起来:
class FrobReactor: public Frobulator::Listener
{
public:
    FrobReactor(const Frobulator& frobulator_):
        frobulator(frobulator_)
    {
    }

private:
    void onFrobbed() override
    {
        // react!
    }

    const Frobulator& frobulator;
}

 // and externally
 Frobulator theFrobber;
 FrobReactor reactor(theFrobber);
 theFrobber.addListener(&reactor);
 theFrobber.maybeFrob();

或者,可以将addListener方法设置为const,并在侦听器列表中将其设置为mutable,然后第一种方法也可以与非const引用一起使用,但这听起来像是一种hack。

有没有“正确”的方法来做到这一点?

最佳答案

我不会在FrobReactor中存储对观察到的(或监听到的)Frobulator的引用。相反,我会将对Frobulator实例的const引用传递给onFrobbed方法。

class Listener
{
private:
    // only frobulators can frob
    friend class Frobulator;
    virtual void onFrobbed(const Frobulator& frobulator) = 0;
}

并且,改编maybeFrob:
void maybeFrob()
{
    // assume we always frob, but maybe we only do it sometimes
    // and the caller won't know if a call will do it
    for (auto& l: listeners)
    {
        l->onFrobbed(*this);
    }
}

至于addListener是否应为const(因此,侦听器的 vector 是否应为可变的),这取决于您要实现的目标。我同意这感觉很棘手,但另一方面,如果您要确保API的客户端仅处理const Frobulators,则这是一种实现方法。另一个方法是在您的API中的某处有一个方法,该方法可以像这样向Frobulators添加侦听器:
void addListener(const Frobulator& frobulator, Frobulator::Listener& listener) {
    Frobulator& nonConst = // obtain non-const reference to this frobulator
    nonConst.addListener(listener);
}

这实际上取决于您的设计。否则,如果您要做的就是保护侦听器免于修改Frobulator,那么将const引用传递给'onFrobbed方法似乎就足够了。

最后,我将像这样更改addListener:
void addListener(Listener& listener)
{
    listeners.push_back(&listener);
}

这是一个很小的变化,但是除非有其他原因要传递指针,否则我只希望在转让所有权时传递指针。当然,无论哪种情况,您都必须确保您的监听器不会被删除(因为超出范围)。

07-28 04:48