C ++清除动态分配对象指针的向量会导致异常吗?

时间:2014-05-15 11:11:39

标签: c++ exception pointers vector erase

我有一个名为ClassA的班级。此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个对象的向量。我目前的情况是:如果我的ClassA向量中没有任何ClassC个对象,那么一切正常。但是如果我有对象,然后在ClassC中的向量上调用.erase(),则抛出异常。通过日志记录,我已经确定调用了ClassA析构函数(因为我的理解是.erase()会破坏每个向量成员),但是当循环开始时抛出异常。

有谁知道可能导致这种情况的原因?谢谢!

5 个答案:

答案 0 :(得分:3)

关注rule of zero,然后使用以下内容宣告您的m_vActiveB成员向量:

std::vector<ClassB> m_vActiveB;

让默认构造函数处理销毁。您也可以使用std::unique_ptr的向量,但是,就像您当前的解决方案一样,您不会利用数组中连续元素的自然效率。

  

在我的系统中,ClassB的对象本身需要从向量中不断地添加,删除和更新,以保持状态模型。

如果您的数据模型自然不是基于元素是连续的并且具有特定顺序的事实,那么像std::unordered_mapstd::unordered_set这样的“未排序”关联容器可能是更好的解决方案,特别是如果你插入和删除没有驻留在向量末尾的元素(而是坐在中间)。

您可能还想查看std::liststd::deque,它们分别提供快速插入和删除(不会使迭代器无效)以及两端快速插入和删除(不要使迭代器无效。)

  

我记得改变向量内容会使所有指针无效..这是正确的吗?

如果调用std::vector::erase是,则所有迭代器都将失效(包括过去的结束迭代器)。调用erase也会使删除对象的所有引用和指针无效(就像引用任何被销毁/解除分配的对象一样无效)。

答案 1 :(得分:2)

来自文档:http://en.cppreference.com/w/cpp/container/vector/erase

  

在擦除点或之后使迭代器和引用无效,包括end()迭代器。

这意味着您的ie迭代器都已失效。所以不要像你一样在循环中使用erase

还要考虑:

  1. 使用智能指针,例如std::shared_ptr,而不是自己进行内存管理。
  2. 查看erase-remove,虽然它在这里没有用,因为:
  3. 您正在删除向量中的每个元素。您不需要在删除时删除它们,只需在删除它们时使用m_vActiveB.clear(),但您甚至不需要这样做,因为:
  4. 这是析构函数,m_vActiveB是成员。它会为你整理,不要自己打扫它。

答案 2 :(得分:1)

使用std::vector::erase会导致擦除点处和之后的迭代器失效。发生这种情况时,在第一次迭代后,过去的结束迭代器e不再有效。

而是直接与每次迭代时从std::vector::end返回的迭代器进行比较。但是,在您的示例中,不需要删除任何内容,因为m_vActiveB是成员,并且在运行dtor后会自动销毁(delete数据指向 然后需要)。

当擦除发生时,迭代器i也会失效,因为复制迭代器tmp的副本i并且擦除它仍然会导致从向量中删除相同的元素(删除迭代器的副本没有帮助。)

ClassA::~ClassA() {
    for (vector<ClassB*>::iterator i = m_vActiveB.begin(); i != m_vActiveB.end(); ++i) {
        //vector<ClassB*>::iterator tmp(i++); // Unnecessary
        delete *i;
        //m_vActiveB.erase(i); // Unnecessary as elements will be erased automatically.
    }
}

等效C ++ 11代码:

ClassA::~ClassA() {
    for (auto ptr : m_vActiveB) {
        delete ptr;
    }
}

更好的选择是使用C ++ 11智能指针作为例如std::unique_ptr,可以自动处理分配和解除分配。

class ClassA {
    std::vector<std::unique_ptr<ClassB>> m_vActiveB; // No need for explicit destructor.
    /* ... */
};

或者,如果您没有使用多态或有其他特定原因使用指针,那么为什么要使用指针呢?只需使用std:vector<ClassB>

答案 3 :(得分:0)

您正在迭代它时从矢量中删除元素。这使迭代器无效。

它应该是这样的:

ClassA::~ClassA() 
{
    for (auto e : m_vActiveB)
    {
        delete e;
    }
}

你不必清除向量,它在析构函数完成后无论如何都会被销毁。

答案 4 :(得分:-1)

使用vector::erase时,它会使向量中的任何其他迭代器失效。

所以你不能使用“传统的”迭代器循环。

最简单的方法是Rakibul Hasan建议,只需删除所有指针然后clear向量。