我有一个名为 ClassA
的 class . 这个 ClassA
包含一个指向动态分配类 ClassB
的对象的向量 . ClassA
的析构函数如下所示:
ClassA::~ClassA() {
for (vector<ClassB*>::iterator i = m_vActiveB.begin(), e = m_vActiveB.end(); i != e; ) {
vector<ClassB*>::iterator tmp(i++);
delete *tmp;
m_vActiveB.erase(tmp);
}
}
在 ClassC
中,我有一个 ClassA
对象的向量 . 我目前的情况是:如果我的 ClassC
向量中没有任何 ClassA
对象,那么一切正常 . 但是如果我有对象,然后在 ClassC
中对我的向量调用.erase(),则抛出异常 . 通过日志记录,我已经确定调用了 ClassA
析构函数(因为我的理解是 .erase()
会破坏每个向量成员),但是当循环开始时抛出异常 .
有谁知道这可能导致什么?谢谢!
5 回答
按照rule of zero并使用以下内容声明
m_vActiveB
成员向量:并让默认构造函数处理销毁 . 您也可以使用std::unique_ptr的向量,但是,就像您当前的解决方案一样,您不会利用数组中连续元素的自然效率 .
如果您的数据模型自然不是基于元素是连续的并且具有特定顺序的事实,那么
std::unordered_map
,关联容器(如std::unordered_map
或std::unordered_set
)可能是更好的解决方案,特别是如果您插入和删除不是驻留在矢量的末尾(而不是坐在中间) .您可能还想查看std::list和std::deque,它们分别提供快速插入和删除(也可以使迭代器无效) .
如果调用std::vector::erase是,则所有迭代器都将失效(包括过去的结束迭代器) . 调用
erase
也会使删除对象的所有引用和指针无效(就像引用任何被销毁/解除分配的对象一样无效) .来自文档:http://en.cppreference.com/w/cpp/container/vector/erase
这意味着您的
i
和e
迭代器都已失效 . 所以不要像你一样在循环中使用erase
!还要考虑:
使用智能指针,如
std::shared_ptr
而不是自己进行内存管理 .查找erase-remove,虽然它在这里不太有用,因为:
你需要在你去的时候擦掉它们,当你甚至需要这样做的时候使用
m_vActiveB.clear()
,因为:这是析构函数,
m_vActiveB
是成员 . 它自己打算清理它 .使用
std::vector::erase
会导致擦除点处和之后的迭代器失效 . 因此,在第一次迭代之后,过去的结束迭代器e
不再有效 .而是直接与每次迭代时从
std::vector::end
返回的迭代器进行比较 . 但是,在您的示例中,不需要删除任何内容,因为m_vActiveB
是成员,并且在运行dtor后将自动销毁(但需要提供指向的数据的delete
) .当擦除发生时,迭代器
i
也将变为无效,因为复制迭代器i
并删除它仍然会导致从向量中删除相同的元素(擦除迭代器的副本没有帮助) .等效C 11代码:
更好的选择是使用C 11智能指针作为例如
std::unique_ptr
,可以自动处理分配和释放 .或者,如果您不使用多态或有其他特定原因使用指针,那么为什么要使用指针呢?只需使用
std:vector<ClassB>
即可 .您正在迭代它时从向量中删除元素 . 这使迭代器无效 .
它应该是这样的:
您不必清除向量,它在析构函数完成后无论如何都会被销毁 .
当您使用
vector::erase
时,它会使向量中的任何其他迭代器无效 .所以你不能使用“传统的”迭代器循环 .
最简单的方法是Rakibul Hasan建议,只需删除所有指针然后
clear
向量 .