我有一个单身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}}
答案 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