【问题标题】:std::vector calling destructor multiple times during push_back?std::vector 在 push_back 期间多次调用析构函数?
【发布时间】:2020-04-05 19:29:21
【问题描述】:

我将对象类tipo 推送到一个向量,当我推送第一个构造函数时,构造函数被调用(应该如此)并且析构函数被立即调用(我认为这不应该发生)。然后当我推送下一个对象时,构造函数被调用一次,析构函数被调用两次,然后第三次被调用三次,依此类推。

似乎每次我将某些东西推送到向量时,都会调用更多次析构函数。

这是我的课:

class Item
{
protected:
...
public:
    Item();
    Item(char * no, int hit, int ve, char * pathTilesheet, int an, int al, bool inv, bool vol, bool fan, int mh = NULL);
    ~Item();
};


Item::Item(char *no, int hi, int ve, char *pathTilesheet, int an, int al, bool inv, bool vol, bool fan, int mh){    
    // CARGAR SDL
    tileSheet = load_image(pathTilesheet);
    tileSheetEspejo = flip_surface(tileSheet, FLIP_HORIZONTAL);
}

这是正在发生的事情:

std::vector<Item> vecItems;
vecItems.push_back(Item("life",4,0,"assets/imagenes/hp.png", 8, 8, false, false, false));
// HERE THE CONSTRUCTOR AND THE DESTRUCTOR ARE CALLED
vecItems.push_back(Item("lifeXL",8,0,"assets/imagenes/hp-xl.png", 16, 16, false, false, false));
// HERE THE CONSTRUCTOR IS CALLED ONCE AND THE DESTRUCTOR TWICE
vecItems.push_back(Item("blast 1",-4,14,"assets/imagenes/bola.png", 8, 8, false, true, false));
// HERE THE CONSTRUCTOR IS CALLED ONCE AND THE DESTRUCTOR MULTIPLE TIMES

我做错了吗?为什么会发生这种情况?

【问题讨论】:

  • 这可能是因为副本和重新分配。 Vector 使用连续存储,因此如果超出其容量,它必须分配更大的内存块,复制所有现有元素,然后释放之前的内存。
  • 您可能对这个问题感兴趣stackoverflow.com/q/13395984/1741542
  • 要查看@user2802841 是否正确,您可以尝试插入 vecItems.reserve(3);在你的陈述之前。
  • 计数复制构造函数调用看余额是否正确...
  • 如果你写析构函数,你可能需要担心复制和赋值。谷歌“三个 c++ 规则”。

标签: c++ c++11 vector


【解决方案1】:

代码的每一行都会创建一个临时的Item,将它复制到向量的内存中,然后销毁这个临时的。这就是为什么您每次都会看到(至少)一个析构函数调用。

在 C++11 中,您可以避免创建和销毁临时对象,方法是使用 emplace_back(args...) 而不是 push_back(Item(args...)),直接在向量的内存中创建对象。

此外,向量有时需要增长,重新分配更大的内存块,以便将其所有元素保存在一个连续的数组中。当它这样做时,每个元素都被移动到新内存中,旧元素被销毁。这就是为什么你有时会看到不止一个析构函数调用。

如果您知道向量的最终大小,则可以避免重新分配的需要,方法是在开始之前调用reserve() 分配足够的内存。或者,像 dequelist 这样的容器不会随着它们的增长而移动它们的元素,但对于其他操作可能效率较低。

顺便说一句,如 cmets 中所述,如果该类正在管理资源(由析构函数的存在暗示),您可能需要提供或删除复制构造函数和复制赋值运算符Rule of Three,或许可以考虑使其可移动以提高效率。

【讨论】:

  • @Yakk:这与问题没有直接关系;但是是的,如果该类需要析构函数,那么它可能需要修复或禁用其复制语义。
  • 他的问题是为什么析构函数比构造函数更频繁地被调用。那是因为他错过了实现复制构造函数和赋值运算符。如您所见,OP 在 ctor 中分​​配资源,这些资源可能在 dtor 中被清理,当一个 ctor 调用 3 次 thr dtor 时会出现问题。 OP 提出了错误的问题,而正确的答案涉及编写一个复制 ctor。
  • @Yakk:问题询问为什么多次调用析构函数,我的回答解释了原因。我已经添加了一条说明,说明如果该类具有无效的复制语义,为什么会出现问题,即使这只是边缘相关。
猜你喜欢
  • 1970-01-01
  • 2012-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多