向量的奇怪问题

时间:2011-01-13 16:32:29

标签: c++ qt stl vector

对于stl向量我有一个非常奇怪的问题,当我调用擦除方法时,如果有意义的话,会为正确的对象调用错误的析构函数。
我的代码看起来像这样:

 for(vector<Category>::iterator iter = this->children.begin(); iter != this->children.end(); iter++)
    {
        if((*iter).item == item)
        {
            this->children.erase(iter);
            return;
        }
        -------------------------
    }

它只是一个简单的函数,它在向量中找到要搜索的项目的元素,并从向量中移除所述元素。我的问题是,当调用erase函数时,迭代器指向的对象被破坏,正在调用错误的析构函数。更具体地说,正在调用向量中最后一个元素的析构函数,而不是被删除的实际对象的析构函数。因此,内存将从错误的对象中移除,该对象仍将是向量中的元素,并且从向量中移除的实际对象仍然保持其所有内存的完整性。 对象的costructor如下所示:

Category::Category(const Category &from)
{
    this->name = from.name;
    for(vector<Category>::const_iterator iter = from.children.begin(); iter != from.children.end(); iter++)
        this->children.push_back((*iter));

    this->item = new QTreeWidgetItem;
}

析构函数

Category::~Category()
{
    this->children.clear();
    if(this->item != NULL)
    {
        QTreeWidgetItem* parent = this->item->parent();
        if(parent != NULL) parent->removeChild(this->item);
        delete this->item;
    }
}

3 个答案:

答案 0 :(得分:5)

从向量中删除元素时,将其后的每个元素(使用赋值运算符)复制到向量中的前一个点。一旦完成,矢量中的最后一个元素就被破坏了。这可能就是为什么你看到你的最后一个元素被破坏了。使用STL时的第一条规则是确保对象的复制语义正确。

您应该考虑编写赋值运算符:

Category & operator =(const Category & other);

虽然这可能不像听起来那么简单,但考虑对象会在向量中被复制和破坏多次。

答案 1 :(得分:0)

您可能应该使用标准算法。

我看到的主要问题是你的类的析构函数要求它的父矢量删除它。这可能不对,析构函数只有在向量已经删除它时才会发生。

std :: vector使用placement-new所以将直接调用你的析构函数。我不知道此时回到矢量会产生什么影响。

从析构函数中删除行if (parent != NULL ) parent->removeChild(this->item)。这不是你想要的。

答案 2 :(得分:0)

这是预期的行为。在我的实现(我猜你的)上,当从一个向量中删除一个元素时,从n + 1到结尾的元素被分配到它中,最后一个元素被破坏。

如果您不希望发生这种情况,请使用std::list

演示:

#include <iostream>
#include <vector>
struct Category
{
        int item;
        Category(int n=0) : item(n) {}
        ~Category() { std::cout << "Category " << item << " destroyed\n"; }
};
int main()
{
        std::vector<Category> children(3);
        children[0] = Category(0);
        children[1] = Category(1);
        children[2] = Category(2);

        int item = 0;
        std::cout << " beginning the loop \n";
        for( std::vector<Category>::iterator iter = children.begin();
                                       iter != children.end(); ++iter)
        {
            if(iter->item == item)
            {
                    children.erase(iter); //  prints "Category 2 destroyed"!
                    break;
            }
        }
        std::cout << " loop done \n";
} // this will print "Category 1 destroyed" and "Category 2 destroyed"

是的,显式erase / remove_if比循环更具可读性。