std :: enable_shared_from_this与不同的所有者

时间:2014-07-28 15:58:14

标签: c++ c++11 boost shared-ptr smart-pointers

另一个enable_shared_from_this问题:基本上,我有三件事。

  1. 包含应用程序逻辑的系统类,可能是也可能不是事件侦听器。
  2. 某种EventManager,它将事件映射到感兴趣系统的shared_ptrs。
  3. 某种SystemManager,它将一个std :: shared_ptrs列表保存到System对象。
  4. 现在,每个系统都应该能够使用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。

    任何有关解决此问题的优雅方法的想法?

1 个答案:

答案 0 :(得分:4)

您的第一个问题是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.
  }
}

现在,当您安装Listener时,您有责任保留EventToken,直到您不想再听它为止。如果您不想再听,EventToken.reset()

EventManager中,当您在人们的监听中进行迭代时,请auto listener = it->lock(); if (!listener) continue;执行weak_ptr,并另外执行快速删除 - 删除 - 如果要删除死EventManager

现在我们的EventManager不拥有其听众的生命周期,他们可以随时(几乎)随时离开。 IEventListener会在下次调用事件时发出通知,并清除死亡事件。

您可以使用shared_from_this对此进行改进,但即使在这里,您也需要一个弱指针,而不是共享指针。在任何地方使用共享指针都会让你遇到麻烦,因为你会有很多循环自支持引用和资源泄漏。

最后,shared_ptr实际上允许您访问拥有您的shared_ptr。这意味着当构建weak_ptr时,shared_from_this会存储在this中,以后即使您只有T*指针也可以提取。{/ p >

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