删除指针向量时遇到问题
Posted
技术标签:
【中文标题】删除指针向量时遇到问题【英文标题】:Having trouble deleting vector of pointers 【发布时间】:2013-09-02 13:47:44 【问题描述】:我有一个管理器类,其中包含一个指向虚拟基类的指针向量,以允许将各种子类存储在那里。在这个管理器类的析构函数中,我希望它循环遍历它持有的所有指针并删除它们。但是,我尝试了许多我遇到的方法,程序在执行过程中不断崩溃。
我的当前代码如下所示:-
for (std::vector<GameState*>::iterator it = gamestates_.begin(); it != gamestates_.end(); ++it)
delete *it;
it = gamestates_.erase(it);
我还没有尝试过的一件事是使用 unique_ptr 但我确信这应该能够在不使用它们的情况下处理它。如果我错了,请纠正我。
编辑:我知道我应该在循环之后清除向量,但这是我在尝试了所有删除指针的正常方法后得出的结果。它似乎不喜欢删除命令。
【问题讨论】:
【参考方案1】:从向量中删除一个元素会使迭代器失效,因此你不能在之后继续迭代。在这种情况下,我不会删除循环中的元素;之后我会清除向量:
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
,但我确信这应该能够在不使用它们的情况下处理它。如果我错了,请纠正我。
如果您确实想通过这种方式管理动态对象,请确保遵循Rule of Three:您需要实现(或删除)复制构造函数和复制赋值运算符以防止“浅”复制留下两个试图删除相同对象的向量。您还需要注意删除任何其他删除或替换对象的位置。存储智能指针(或对象本身,如果您不需要指针来实现多态性)将为您处理所有这些事情,所以我总是建议这样做。
我知道我应该在循环之后清除向量,但这是我在尝试了所有删除指针的正常方法后得出的结果。它似乎不喜欢删除命令。
最可能的原因是您没有遵循三法则,并且在复制向量后不小心尝试删除相同的对象两次。也有可能GameState
是一个基类,而您忘记给它一个虚拟析构函数,或者指针已被其他代码破坏。
【讨论】:
这似乎也不起作用。我之前确实尝试过这样的事情,但似乎没有任何效果。就好像它不喜欢删除命令一样。愚蠢的问题:我假设我不需要包含某个标题来删除指针。 @RustyC:delete
命令很好,如果它是一个有效的指针。您是否不小心复制了向量并删除了两次对象? GameState
是基类,实际对象有不同的类型吗?如果是这样,请确保它具有虚拟析构函数。如果不是,你为什么要存储指针?
Gamestate 拥有对 init、render、update 和 close 的虚拟调用,但它的每个子级也将拥有许多自己的函数和变量。因为我只希望一次运行一个,所以当状态处于活动状态时管理器调用 init 并在状态完成时调用关闭。析构函数本质上是空的,并且在我引用的行的正上方有一行可以关闭活动状态。我希望这些类非常大,所以我试图通过将它们存储在堆上来节省空间。如果它们本质上是空的,它们是否仍然需要在基类中使用虚拟析构函数?
@RustyC:是的,你总是需要一个虚拟析构函数来通过基类指针删除。确保 GameState
有一个,并确保管理器类不会意外复制向量。
该向量在标题中定义(类私有)并使用 push_back 添加,我看不到它在任何时候将如何被复制。另外,虚拟析构函数需要什么特别的东西吗?【参考方案2】:
您的迭代器在每个循环中更新两次:
it = gamestates_.erase(it);
和
it++
您只需要第一个 - 它已经指向容器中的“下一个对象”。
【讨论】:
+1 鉴于您是第一个指出这一点的答案,您没有投票似乎有点不公平。【参考方案3】:从你的向量中删除一个元素会使迭代器失效。按元素删除对象指针,然后clear()
向量的内容。
【讨论】:
【参考方案4】:去掉for
循环头中的++it
。
erase
已经为你提前了。
或者,迭代,删除,然后在迭代之后.clear()
。
【讨论】:
【参考方案5】:更喜欢使用unique_ptr
。你说你应该能够在不使用它们的情况下处理它,就好像让一个智能指针为你做这项工作是某种可怕的强加。
它们的存在是为了让您的生活更轻松,您不必为没有手工完成这些艰苦的工作而感到内疚。
使用您现有的代码,不要调用erase
。无论如何,向量都会被破坏,对吧?它会自己处理所有这些。
【讨论】:
“用你现有的代码,不要调用erase。无论如何,向量都会被销毁,对吧?它会自己处理所有这些。”除非不是,否则不会! 问题说这是在管理器类的析构函数中,我假设向量是成员子对象。【参考方案6】:问题是您将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()
。
【讨论】:
以上是关于删除指针向量时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章