删除指针向量时出现问题

Having trouble deleting vector of pointers

本文关键字:问题 指针 向量 删除      更新时间:2023-10-16

我有一个管理器类,它持有指向虚拟基类的指针向量,以允许在那里存储各种子类。在这个管理器类的析构函数中,我希望它循环遍历它持有的所有指针并删除它们。然而,我已经尝试了许多遇到的方法,程序在执行过程中不断崩溃。

我的当前代码如下所示:-

for (std::vector<GameState*>::iterator it = gamestates_.begin(); it != gamestates_.end(); ++it){
    delete *it;
    it = gamestates_.erase(it);
}

有一件事我还没有尝试过,那就是使用unique_ptr,但我相信这应该能够在不使用它们的情况下处理它。如果我错了,请纠正我。

编辑:我知道我应该在循环后清除向量,但这是我在尝试了所有删除指针的正常方法后得到的结果。它似乎不喜欢delete命令。

从向量中删除元素会使迭代器无效,因此之后无法继续迭代。在这种情况下,我不会擦除循环中的元素;之后我会清除矢量:

for (auto it = gamestates_.begin(); it != gamestates_.end(); ++it){
    delete *it;
}
gamestates_.clear();

虽然,如果它在析构函数中,并且向量即将被销毁,那么清除它也没有意义。

如果您确实需要在循环中擦除(可能是因为您只想擦除一些元素),那么您需要更加小心地保持迭代器的有效性:

for (auto it = gamestates_.begin(); it != gamestates_.end();){ // No ++ here
    if (should_erase(it)) {
        it = gamestates_.erase(it);
    } else {
        ++it;
    }
}

有一件事我还没有尝试过,那就是使用unique_ptr,但我相信这应该能够在不使用它们的情况下处理它。如果我错了,请纠正我。

如果你真的想通过steam来管理动态对象,那么请确保遵循三条规则:你需要实现(或删除)复制构造函数和复制赋值运算符,以防止"浅层"复制给你留下两个试图删除相同对象的向量。您还需要注意在任何其他位置删除或替换对象。存储智能指针(或者对象本身,如果你不需要多态性指针的话)会为你处理所有这些事情,所以我总是建议你这样做。

我知道我应该在循环后清除向量,但这是我在尝试了所有删除指针的普通方法后得到的结果。它似乎不喜欢delete命令。

最有可能的原因是您没有遵循"三条规则",并且在复制向量后意外地尝试删除相同的对象两次。也有可能GameState是一个基类,而您忘记给它一个虚拟析构函数,或者指针已被其他代码损坏。

您的迭代器在每个循环中更新两次:

it = gamestates_.erase(it);

it++

您只需要第一个——它已经指向容器中的"下一个对象"。

从向量中删除元素将使迭代器无效。删除对象指针中的元素,然后clear()向量的内容。

去掉for循环头中的++it

erase已经为您预付了。

或者,迭代、删除,然后在迭代.clear()之后。

更喜欢使用unique_ptr。你说你应该能够在不使用它们的情况下处理它,就好像让一个智能指针为你做这项工作是一种可怕的强加。

他们在那里是为了让你的生活更轻松,你不必因为没有亲手做艰苦的工作而感到内疚。

使用现有的代码,不要调用erase。矢量无论如何都会被摧毁,对吧?这一切都会自己解决的。

问题是it增加了两次。首先,当您调用返回下一个元素的it = .erase(it)时,然后在循环中调用++i。你可能会不经意地跳过终点,事情可能会出错,更不用说你只会删除向量的每一个元素。

一个简单的解决方案是不更改循环中的it(无++it)。

更好的方法是从向量的末尾实际删除数组,因为从向量内部擦除元素会导致所有后续元素的昂贵移动。您当前的算法将在N^2时间内工作。试试这样的东西:

while (!gamestates_.empty()) {
    delete gamestates_.back();
    gamestates_.erase(gamestates_.end()-1);
}

您也可以迭代向量的所有元素,然后清除它:

for (std::vector<GameState*>::iterator it = gamestates_.begin(); it != gamestates_.end(); ++it){
    delete *it;
}
gamestates_.clear();

还要注意,向量的clear()运算也在其析构函数中完成。如果删除过程是某些销毁过程的一部分,其中gamestates_被完全销毁,那么您根本不必调用clear()