我应该如何重新构造此事件处理代码? 我最近一直在阅读一些C++书籍(Suttter,迈尔斯),这促使我开始更有效地使用智能指针(和一般的对象销毁)。但现在我不知道如何修复我所拥有的。 具体来说,我现在有一个IntroScene类,它继承自Scene和InputListener
场景并不真正相关,但InputListener在构造时订阅了InputManager, 还有不明嫌犯再次被摧毁我应该如何重新构造此事件处理代码? 我最近一直在阅读一些C++书籍(Suttter,迈尔斯),这促使我开始更有效地使用智能指针(和一般的对象销毁)。但现在我不知道如何修复我所拥有的。 具体来说,我现在有一个IntroScene类,它继承自Scene和InputListener,c++,smart-pointers,C++,Smart Pointers,场景并不真正相关,但InputListener在构造时订阅了InputManager, 还有不明嫌犯再次被摧毁 class IntroScene : public sfg::Scene, public sfg::InputListener { /*structors, inherited methods*/ virtual bool OnEvent(sf::Event&) override; //inputlistener } 但是现在,如果inputmanager将事件发送到场景,并
class IntroScene : public sfg::Scene, public sfg::InputListener {
/*structors, inherited methods*/
virtual bool OnEvent(sf::Event&) override; //inputlistener
}
但是现在,如果inputmanager将事件发送到场景,并且场景决定替换自身
因此,我在一个不再存在的对象上运行函数
bool IntroScene::OnEvent(sf::Event& a_Event) {
if (a_Event.type == sf::Event::MouseButtonPressed) {
sfg::Game::Get()->SceneMgr()->Replace(ScenePtr(new IntroScene()));
} //here the returned smartpointer kills the scene/listener
}
旁白:这有关系吗?我在谷歌上搜索了一下,但没有找到确切的是或否。我确实100%知道
销毁对象后,不会对其调用任何方法。
如果有必要,我可以将Replace()返回值存储到OnEvent()方法的末尾
真正的问题是InputListener
InputListener::InputListener() {
Game::Get()->InputMgr()->Subscribe(this);
}
InputListener::~InputListener() {
if (m_Manager) m_Manager->Unsubscribe(this);
}
因为它是在OneEvent()期间调用的,而InputManager是在HandleEvents()期间调用的
因此,当创建新的Scene+侦听器时,当旧的Scene+侦听器被销毁时,列表m_侦听器在循环期间被修改。所以这东西坏了。
我考虑过在启动和停止循环时设置一个标志,并在单独的列表中存储(取消)设置时发生的订阅,然后进行处理。但感觉有点不舒服
那么,我如何才能正确地重新设计它来防止这种情况呢?提前谢谢
编辑,解决方案:
我最终使用了循环标志和延迟条目列表(下面是inetknight的答案)
仅适用于订阅,因为这可以在以后安全地完成
取消订阅必须立即处理,因此我不存储原始指针,而是存储(指针可变布尔)对(可变,因为集合只返回常量迭代器)。发生这种情况时,我将bool设置为false,并在事件循环中检查它(参见下面dave的评论)。
不确定这是否是最干净的解决方案,但它的效果很好。多谢各位
旁白:这有关系吗?我在谷歌上搜索了它,但没有找到一个确定的是或否。我知道在被销毁的对象被销毁后,100%不会调用任何方法。如果有必要,我可以将Replace()返回值存储到OnEvent()方法的末尾
如果您知道100%没有方法在被破坏的对象上被调用,并且没有成员变量被访问,那么它是安全的。是否是有意的取决于你
您可以有另一个请求取消/订阅的对象列表。然后,在您告诉事件列表中的每个人之后,您将在继续下一个事件之前处理un/订阅请求列表
/* this should be a member of InputManager however you did not provide a class definition */
typedef std::pair<InputListener *, bool> SubscriptionRequest;
bool handleEventsActive = false;
std::vector<SubscriptionRequest> deferredSubscriptionRequests;
void InputManager::HandleEvents(EventQueue& a_Events) const {
// process events
handleEventsActive = true;
while (!a_Events.empty()) {
sf::Event& e = a_Events.front();
for (auto& listener : m_Listeners)
{
//swallow event
if (listener->OnEvent(e)) {
break;
}
}
a_Events.pop();
// process deferred subscription requests occurred during event
while ( not deferredSubscriptionRequests.empty() ) {
SubscriptionRequest request = deferredSubscriptionRequests.back();
deferredSubscriptionRequests.pop_back();
DoSubscriptionRequest(request);
}
}
handleEventsActive = false;
}
void InputManager::DoSubscriptionRequest(SubscriptionRequest &request) {
if ( request.second ) {
m_Listeners.insert(request.first);
request.first->m_Manager = this;
} else {
m_Listeners.erase(request.first);
request.first->m_Manager = nullptr;
}
}
void InputManager::Subscribe(InputListener* a_Listener)
{
SubscriptionRequest request{a_Listener, true};
if ( handleEventsActive ) {
deferredSubscriptionRequests.push_back(request);
} else {
DoSubscriptionRequest(request);
}
}
void InputManager::Unsubscribe(InputListener* a_Listener)
{
SubscriptionRequest request{a_Listener, false};
if ( handleEventsActive ) {
deferredSubscriptionRequests.push_back(request);
} else {
DoSubscriptionRequest(request);
}
}
/*这应该是InputManager的成员,但是您没有提供类定义*/
typedef std::pair SubscriptionRequest;
bool handleEventsActive=false;
std::向量延迟订阅请求;
void InputManager::HandleEvents(事件队列和a_事件)常量{
//处理事件
handleEventsActive=true;
而(!a_Events.empty()){
sf::Event&e=a_Events.front();
用于(自动侦听器:m_侦听器(&U))
{
//吞咽事件
如果(侦听器->OneEvent(e)){
打破
}
}
a_Events.pop();
//处理事件期间发生的延迟订阅请求
while(不延迟SubscriptionRequests.empty()){
SubscriptionRequest=deferredSubscriptionRequests.back();
deferredSubscriptionRequests.pop_back();
DoSubscriptionRequest(请求);
}
}
handleEventsActive=false;
}
void InputManager::DoSubscriptionRequest(SubscriptionRequest和request){
如果(请求秒){
m_Listeners.insert(request.first);
request.first->m_Manager=this;
}否则{
m_Listeners.erase(request.first);
request.first->m_Manager=nullptr;
}
}
void InputManager::订阅(InputListener*a_Listener)
{
SubscriptionRequest请求{a_Listener,true};
如果(handleEventsActive){
推迟订阅请求。推回(请求);
}否则{
DoSubscriptionRequest(请求);
}
}
void InputManager::取消订阅(InputListener*a_Listener)
{
SubscriptionRequest请求{a_Listener,false};
如果(handleEventsActive){
推迟订阅请求。推回(请求);
}否则{
DoSubscriptionRequest(请求);
}
}
我的答案正是你所说的你已经想到的。是什么让你觉得它是黑客的呢?在unsubscribe中清空监听器,让HandleEvents循环删除空的元素。这也可以通过在InputManager中为侦听器存储弱\u ptr并在调用OneEvent之前尝试锁定来实现。这样做不会影响循环遍历,但取消订阅的效果是立竿见影的。这让人感觉有点不舒服,因为它是专门为修复我在“设计”这件事时忽略的东西而写的。此外,一个场景可以包含其他监听器,这些监听器将被销毁,但仍可能通过循环发送事件(因为监听器列表不使用智能指针,因为这样会使监听器的析构函数无效)。所以不明嫌犯必须是立即的,或者被引用的对象必须保持完整,直到循环结束。然而,这只是我刚刚想到的,不在OP中。@Dave我不确定我是否可以在这里使用弱\u ptr,因为共享\u ptr是场景类型,另一个基础。侦听器在一个集合中,因为它们都应该是唯一的和有序的,所以将它们置零可能会导致这种情况。我将尝试使用列表中的指针bool对,将bool设置为unsubscription,并在OnEvent之前检查它。我很确定我并没有忽视这样的事情。谢谢你的回答。谢谢你的回答!我在上一篇alinea中描述了这一点,感觉有点奇怪
/* this should be a member of InputManager however you did not provide a class definition */
typedef std::pair<InputListener *, bool> SubscriptionRequest;
bool handleEventsActive = false;
std::vector<SubscriptionRequest> deferredSubscriptionRequests;
void InputManager::HandleEvents(EventQueue& a_Events) const {
// process events
handleEventsActive = true;
while (!a_Events.empty()) {
sf::Event& e = a_Events.front();
for (auto& listener : m_Listeners)
{
//swallow event
if (listener->OnEvent(e)) {
break;
}
}
a_Events.pop();
// process deferred subscription requests occurred during event
while ( not deferredSubscriptionRequests.empty() ) {
SubscriptionRequest request = deferredSubscriptionRequests.back();
deferredSubscriptionRequests.pop_back();
DoSubscriptionRequest(request);
}
}
handleEventsActive = false;
}
void InputManager::DoSubscriptionRequest(SubscriptionRequest &request) {
if ( request.second ) {
m_Listeners.insert(request.first);
request.first->m_Manager = this;
} else {
m_Listeners.erase(request.first);
request.first->m_Manager = nullptr;
}
}
void InputManager::Subscribe(InputListener* a_Listener)
{
SubscriptionRequest request{a_Listener, true};
if ( handleEventsActive ) {
deferredSubscriptionRequests.push_back(request);
} else {
DoSubscriptionRequest(request);
}
}
void InputManager::Unsubscribe(InputListener* a_Listener)
{
SubscriptionRequest request{a_Listener, false};
if ( handleEventsActive ) {
deferredSubscriptionRequests.push_back(request);
} else {
DoSubscriptionRequest(request);
}
}