如何在Singleton Manager类创建范围的末尾自动删除shared_ptr

时间:2014-04-02 15:25:43

标签: c++

我有一个单身ButtonManager,其中包含Button个对象的向量。 ButtonManager主题存在,因为它在调用Button时将事件分发给所有HandleEvents观察者。由于其他一些原因,它需要是一个单身人士。

我的问题是,我希望我的Button无需手动将ButtonManager移出Create。理想情况下,在我Button class ButtonManager : public EventHandler { public: static ButtonManager & Instance(); public: // push_back a Button to the vector and return it std::shared_ptr<Button> Create(const Rect & _rect, const std::string _text, const Colour & _fg, const Colour & _bg); // Erase the button pointer from the vector void Remove(const std::shared_ptr<Button> & _button); // EventHandler void HandleEvents( const Event & _event ); ... private: std::vector<std::shared_ptr<Button>> buttons; }; class Button : public EventHandler { public: Button(const Rect & _rect, const std::string & _text, const Colour & _fg, const Colour & _bg); ... // EventHandler void HandleEvents( const Event & _event ); }; 的范围结束时,ButtonManager也会失去对它的引用。

{
   std::shared_ptr<Button> ok_button_ptr = UI::ButtonManager::Instance().Create(UI::Rect(5, 5, 5, 1), string("OK"), UI::Colour::White, UI::Colour::DodgerBlue);

   // events are processed and distributed to the buttonmanager 
   // which then distributes to the buttons

   UI::ButtonManager::Instance().Remove(ok_button_ptr);
}

当前情况:

{
   std::shared_ptr<Button> ok_button_ptr = UI::ButtonManager::Instance().Create(UI::Rect(5, 5, 5, 1), string("OK"), UI::Colour::White, UI::Colour::DodgerBlue);

   // events are processed and distributed to the buttonmanager 
   // which then distributes to the buttons

   // ok_button_ptr loses its reference here and the ButtonManager erase's the shared_ptr as well if it holds the last reference to the Button
}

理想情景:

{{1}}

2 个答案:

答案 0 :(得分:1)

可以使用RAII在scope-exit处释放资源。创建一个ButtonHolder类,它将shared_ptr保存为成员,并在其析构函数中调用Remove

class ButtonHolder
{
public:
    ButtonHolder(std::shared_ptr<Button> b): theButton(std::move(b)) {}
    ~ButtonHolder() {
        UI::ButtonManager::Instance().Remove(theButton);
    }

    // could give it shared_ptr interface, e.g.
    Button& operator*() const;
    Button& operator->() const;
    // etc
private:
    std::shared_ptr<Button> theButton;
};

{
   // get button from singleton. Ref count increases by one. Let's say it is now 2.
   ButtonHolder ok_button_ptr( UI::ButtonManager::Instance().Create(...) );

   // events are processed and distributed to the buttonmanager 
   // which then distributes to the buttons

   // ButtonHolder::~ButtonHolder is called which removes the button from the 
   // singleton (ref count = 1) and then deletes its own shared_ptr member (ref count = 0)
   // to delete the button object completely
}

答案 1 :(得分:0)

此问题是Lapsed侦听器问题:http://en.wikipedia.org/wiki/Lapsed_listener_problem

解决方案是使用vector weak_ptr代替shared_ptr,因为weak_ptr是非拥有智能指针。

  class ButtonManager : public IUIObservable
   {
   public:
      static ButtonManager & Instance();
      void Subscribe(const std::shared_ptr<IUIObservable> & _o);
      void HandleEvents( const sf::Event & _event );

      ...

   private:
      std::vector<std::weak_ptr<IUIObservable>> observers;
   };



   void ButtonManager::Subscribe(const std::shared_ptr<IUIObservable> & _o)
   {
      observers.push_back(_o);
   }

   void ButtonManager::HandleEvents( const sf::Event & _event )
   {
        // Remove any dead observers. These are the ones that have expired().
        this->observers.erase(std::remove_if(this->observers.begin(), this->observers.end(),
            [](const std::weak_ptr<IUIObservable>& _element)
        {
            return _element.expired();
        }), this->observers.end());

      // go through all the elements and handle events
      for (auto& observer_weak_ptr : this->observers)
      {
         auto observer_ptr = observer_weak_ptr.lock();
         if (observer_ptr)
         {
            observer_ptr->HandleEvents(_event);
         }
      }
   }

用法示例:

  auto ok_button = std::make_shared<Button>(UI::Rect(5, 5, 5, 1), string("OK"), UI::Colour::White, UI::Colour::DodgerBlue);

  UI::ButtonManager::Instance().Subscribe(ok_button);

  {
     auto tmp_button = std::make_shared<Button>(UI::Rect(6, 5, 5, 1), string("Tmp"), UI::Colour::White, UI::Colour::DodgerBlue);

     UI::ButtonManager::Instance().Subscribe(tmp_button);
     // tmp_button will be expired on the next process events
  }

  // Process Events