如何在创建范围结束时自动从单一实例管理器类中删除shared_ptr
How to automatically remove shared_ptr from Singleton Manager class at the end of scope of its creation
我有一个包含Button
对象的向量的单例ButtonManager
。ButtonManager
主体之所以存在,是因为它在调用HandleEvents
时将事件分发给所有Button
观察者。出于其他一些原因,它需要成为单例。
问题是我想消除我的Button
必须手动从我的ButtonManager
中删除自己的需要。理想情况下,在我Create
Button
的作用域结束时,ButtonManager 也会失去对它的引用。
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 );
};
当前方案:
{
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
}
在范围退出时释放资源可以使用 RAII 完成。创建一个 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
}
这个问题是失效的侦听器问题:http://en.wikipedia.org/wiki/Lapsed_listener_problem
解决方案是使用 weak_ptr
vector
而不是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
相关文章:
- 如何维护资源管理器项目视图中当前可见的项目列表
- 使用"Task"函数指针队列定义作业管理器
- 实现动态插件管理器
- 编译器资源管理器和 GCC 具有不同的输出
- Windows 资源管理器中的图标在使用 resource.rc 时显示 2 个不同的图标
- C++ 事件管理器的回调,使用 std::function 和 std:bind 以及派生类作为参数
- Linux 包管理器如何处理 C++20 个模块?
- 编译器资源管理器(godbolt)如何安全地运行代码?
- 创建跨平台 C++ 触摸管理器.在 c++ 中传递 Objective-c 对象涉及代码
- 从 GUI 文件资源管理器执行程序,并且不生成终端
- 自定义内存管理器在发布模式下工作正常,但在调试模式下则不然
- 无法删除EXE崩溃后,即使进程未显示在任务管理器中
- 任务管理器 - C/C++ 应用程序 - 分配的地址空间与已用内存
- 如何以编程方式获取任务管理器进程CPU使用率(不是PerfMon API)
- 管理器类和对象类之间的通信
- WinDBG适用于从Visual Studio 2015保存的转储,但不适用于任务管理器。显示异常代码"not found"
- 在C++中将系统与ECS(实体组件系统)管理器分离
- 是否可以检索与柯南包管理器一起打包的库的源代码,以便在其中进行调试?
- 简单C++程序在多个实例中启动,任务管理器无法关闭它
- 如何在创建范围结束时自动从单一实例管理器类中删除shared_ptr