还有另一个enable_shared_from_这个问题:基本上,我得到了三件事。


包含应用程序逻辑并且可能是也可能不是事件侦听器的系统类。
某种EventManager,将事件映射到感兴趣的系统的shared_ptrs。
某种类型的SystemManager,它拥有std :: list到系统对象的shared_ptrs。


现在,每个系统都应该能够针对其感兴趣的所有事件在EventManager中进行注册。此外,如果他们不再对这些事件感兴趣,则它们应该能够针对这些事件注销自己。

但是,我不确定如何在系统管理器中创建系统并以避免两个所有权组的方式对其进行注册。

从SystemManager:

void SystemManager::AddSystem(GameSystem* system)
{
    // Take ownership of the specified system.
    systems.push_back(std::shared_ptr<GameSystem>(system));
}


从EventManager中:

typedef std::shared_ptr<IEventListener> EventListenerPtr;

void EventManager::AddListener(EventListenerPtr const & listener, HashedString const & eventType)
{
    // Get the event type entry in the listeners map.
    std::map<unsigned long, std::list<EventListenerPtr>>::iterator iterator = this->listeners.find(eventType.getHash());

    if (iterator != this->listeners.end())
    {
        std::list<EventListenerPtr> & eventListeners = iterator->second;

        // Add listener.
        eventListeners.push_back(listener);
    }
    else
    {
        // Add new entry to listeners map, removed for sake of simplicity of this example.
    }
}


从一些想要接收事件的任意系统中:

void RenderSystem::InitSystem(std::shared_ptr<GameInfrastructure> game)
{
    // BAD: Creates second ownership group.
    this->game->eventManager->AddListener(std::shared_ptr<IEventListener>(this), AppWindowChangedEvent::AppWindowChangedEventType);
}


我已经阅读了有关推荐enable_shared_from_this的参考资料。但是,由于系统不拥有自身,因此无法访问最初创建系统并获得系统所有权的SystemManager所拥有的shared_ptr。

有什么优雅的解决方案的想法吗?

最佳答案

您的第一个问题是IEventListener。我猜它是一个简单的回调的接口。

不要将继承用于简单回调的接口,请在简单回调上使用类型擦除。

typedef std::function<void(EventData)> EventListener;
typedef std::weak_ptr<EventListener> wpEventListener;
typedef std::shared_ptr<EventListener> EventToken;

EventToken EventManager::AddListener(EventListener listener, HashedString const & eventType)
{
  // note type changes (3 of them!) -- change the type of this->listeners to match
  // probably HashedString should have a std::hash<HashedString> specialization to make this
  // less painful
  // also should be auto iterator = -- the type of an iterator is not interesting.
  std::unordered_map<unsigned long, std::vector<wpEventListener>>::iterator iterator = this->listeners.find(eventType.getHash());

  if (iterator != this->listeners.end())
  {
    auto & eventListeners = iterator->second;

    EventToken retval = std::make_shared<EventListener>(std::move(listener));
    eventListeners.push_back(retval);
    return retval;
  } else {
    // Add new entry to listeners map, removed for sake of simplicity of this example.
  }
}


现在,当您安装侦听器时,您有责任持有您的EventToken,直到您不想再听它为止。当您不想再听时,请按EventToken.reset()

EventManager中,当您遍历正在收听的人时,请执行auto listener = it->lock(); if (!listener) continue;,此外,还可以执行快速擦除-删除-if来删除无效的weak_ptr

现在,我们的EventManager不再拥有其侦听器的生命周期,他们可以(几乎)随时随地消失。 EventManager会在下次调用事件时发出通知,并清除失效的事件。

您可以使用IEventListener对其进行改造,但是即使在这里,您也将需要一个弱指针,而不是共享指针。在任何地方使用共享指针都会给您带来麻烦,因为您将拥有循环的自支持引用,并且资源大量泄漏。

最后,shared_from_this实际上允许您访问拥有您的shared_ptr。这意味着在构造shared_ptr时,weak_ptr存储在shared_from_this中,即使您只有this指针,也可以稍后将其提取。

我通常会发现一个不好的迹象:您应该很少在随机上下文中将原始指针升级为共享指针。方法的生命周期语义应该从其接口中显而易见,而采用T*并将其内部转换为shared_ptr<T>会使生命周期语义完全不清楚。

关于c++ - std::enable_shared_from_this与其他所有者,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24999212/

10-09 00:11