从向量中删除元素后出现段错误

问题描述 投票:2回答:2

我目前正在从事一个项目,更确切地说是七巧板游戏。我遇到段故障,我不明白为什么。

鉴于我有一个完整的项目,我将尝试简化问题:我有一个GameManager类,其中特别包含一个Menu对象(以及其他内容,但是我认为这并不重要。gameManager用于初始化该对象并进行管理。菜单包含一个Button矢量(每个按钮都有一个lambda,当用户单击它时可以执行操作)。

std::vector<std::unique_ptr<Button>> buttons;

[为了说明它是如何工作的,我将举一个例子:如果用户单击“加载”按钮,gameManager将删除菜单中包含的当前按钮,并在该菜单中添加新按钮。

void GameManager::initMainMenuButtons() {
...
menu -> addButton(std::unique_ptr<Button>(new Button(x1, y1, x2, y2, "Create",
    [this]{
        std::cout << "Create level" << std::endl;
        menu->clear()
        initCreateLevelButtons();
        actionManager->setMenu(menu);
    }
)));
...
}

在该代码示例中,我有一个initMainMenuButtons方法,该方法在菜单中添加了多个按钮,例如“ Load”或“ Quit”。当用户单击“创建”时,我想更改界面(添加和删除按钮)。因此,要删除按钮,我调用方法clear()

void Menu::clear() {
  buttons.clear();
  decorationPieces.clear(); // not interesting
}

我正在使用unique_ptr,因此,我不必手动删除按钮。到目前为止,没有问题:按钮的向量似乎为空(大小为0)。接下来,调用initCreateLevelButtons()方法。此方法与initMainMenu非常相似:它在菜单中添加按钮,仅此而已。在此调用过程中,按钮似乎已正确添加到引导程序中,我在末尾打印了引导程序的内容,并且引导程序包含正确的按钮。

并且,出现问题:调用initCreateLevelButtons()之后,当我想使用菜单时出现段错误,因此actionManager->setMenu(menu);不起作用。我试图打印菜单std::cout << menu << std::endl,并测试此指针是否为nullptr,但它也不起作用。我不明白为什么菜单在initCreateLevelButtons()的最后一行似乎正确,而在此之后才变得无效。如果我不清除按钮矢量(菜单->清除指令),则程序可以运行,但是最后的按钮仍在此处)。

我尝试使用原始指针,我注意到只要不删除按钮,程序就可以清除向量(如果我添加了一个循环来删除按钮,则会出现问题),因此,我得出结论:问题是按钮正在删除。我不明白为什么,我被卡住了。我不知道是否解释过,因为正如我已经说过的那样,代码是整个项目的一部分,如果不引入其他内容就很难引入类。如果您需要详细信息或方法的完整代码,我可以提供它们。

c++ vector segmentation-fault smart-pointers raw-pointer
2个回答
5
投票
  1. [menu维持某些button的寿命
  2. [buttonlambda维持寿命
  3. 单击buttonlambda清除menu
  4. [menu析构函数清除buttonbutton清除lambda
  5. lambda实际上已经被破坏时继续执行->未定义的行为以崩溃结尾

现在的问题是:您是否拥有Button类?如果是,那么最简单的解决方法是在按钮中调用lambda的副本。


3
投票

[当您呼叫menu->clear()时会呼叫buttons.clear()

当您调用buttons.clear()时,它会破坏buttons的所有元素。

[销毁“创建”按钮的unique_ptr时,它销毁了“创建”按钮。

我假设button的回调是std::function。当button被破坏时,std::function也被破坏。

std::function被销毁时,您的回调lambda对象([this]{...})被销毁。

lambda内的this指针存储在lambda对象中。因此,现在已经释放了保存this指针的内存。

由于actionManagerGameManager的成员变量,所以actionManager->setMenu(menu)实际上是this->actionManager->setMenu(menu),它由于使用了悬挂指针而崩溃。

一种解决方法是将按钮代码放入GameManager函数中(因为GameManager未销毁),然后从lambda调用它。然后,如果您在该函数内部销毁按钮就可以了。 It's okay to destroy an object whose code is currently running,只要小心不要破坏该对象即可! std::function也可以。即:

    [this]{
        // move the rest of the code to the CreateLevel function
        this->CreateLevel();

        // At this point the lambda has been destroyed, but it's not a problem
        // because we don't do anything.
    }
© www.soinside.com 2019 - 2024. All rights reserved.