我该如何重组这个事件处理代码?

时间:2015-01-12 23:57:35

标签: c++ smart-pointers

我最近一直在阅读一些C ++书籍(Sutters,Meyers),这促使我更有效地开始使用智能指针(以及一般的对象破坏)。但现在我不知道如何解决我的问题。 具体来说,我现在有一个继承自Scene和InputListener的IntroScene类。

场景并不真正相关,但是InputListener在构造时订阅了一个InputManager, 并且在破坏时再次取消。

class IntroScene : public sfg::Scene, public sfg::InputListener {
/*structors, inherited methods*/
virtual bool OnEvent(sf::Event&) override; //inputlistener
}

但是现在,如果输入管理器将事件发送到场景,并且场景决定替换自己 因为它,我有一个不再存在的对象上运行的函数。

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);
}

因为在OnEvent()期间调用它,在HandleEvents()期间由InputManager调用

void InputManager::HandleEvents(EventQueue& a_Events) const {
    while (!a_Events.empty()) {
        sf::Event& e = a_Events.front();
        for (auto& listener : m_Listeners) {
            if (listener->OnEvent(e)) //swallow event
                break;
        }
        a_Events.pop();
    }

void InputManager::Subscribe(InputListener* a_Listener) {
    m_Listeners.insert(a_Listener);
    a_Listener->m_Manager = this;
}

void InputManager::Unsubscribe(InputListener* a_Listener) {
    m_Listeners.erase(a_Listener);
    a_Listener->m_Manager = nullptr;
}

因此,当创建新的Scene + Listener时,以及旧的Scene + Listener被销毁时,在循环期间修改列表m_Listeners。事情就这样破碎了。 我已经考虑过在启动和停止循环时设置一个标志,并存储(un)在单独列表中设置时发生的订阅,并在之后处理。但感觉有点笨拙。

那么,我怎样才能真正重新设计这个以防止这种情况呢?提前谢谢。

编辑,解决方案: 我最终得到了循环标志和延迟条目列表(下面的inetknight'答案) 仅限订阅,因为以后可以安全地完成。

取消订阅必须立即处理,因此我不存储原始指针而是存储一个(指针可变的bool)对(可变,因为一个集只返回一个const_iterator)。当发生这种情况时我将bool设置为false并在事件循环中检查它(请参阅下面的dave评论)。 不确定它是最干净的解决方案,但它就像一个魅力。非常感谢你们

1 个答案:

答案 0 :(得分:1)

  

附带问题:这有关系吗?我用谷歌搜索,但没有找到肯定的是或否。我知道在被销毁的对象被销毁后100%没有被调用的方法。如果必须的话,我可以将Replace()返回值存储到OnEvent()方法的结尾。

如果你知道100%没有调用任何方法,他就破坏了对象,并且没有访问任何成员变量,那么它就是安全。是否意图取决于你。

您可以拥有另一个请求取消/订阅的对象列表。然后,在您告诉事件列表中的每个人之后,您将在继续下一个事件之前处理un / subscription请求列表。

/* 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);
    }
}