Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我应该如何重新构造此事件处理代码? 我最近一直在阅读一些C++书籍(Suttter,迈尔斯),这促使我开始更有效地使用智能指针(和一般的对象销毁)。但现在我不知道如何修复我所拥有的。 具体来说,我现在有一个IntroScene类,它继承自Scene和InputListener_C++_Smart Pointers - Fatal编程技术网

我应该如何重新构造此事件处理代码? 我最近一直在阅读一些C++书籍(Suttter,迈尔斯),这促使我开始更有效地使用智能指针(和一般的对象销毁)。但现在我不知道如何修复我所拥有的。 具体来说,我现在有一个IntroScene类,它继承自Scene和InputListener

我应该如何重新构造此事件处理代码? 我最近一直在阅读一些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将事件发送到场景,并

场景并不真正相关,但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);
    }
}