【问题标题】:Why does std::vector::emplace call destructor without any copy constructor called?为什么 std::vector::emplace 调用析构函数而不调用任何复制构造函数?
【发布时间】:2016-04-29 01:34:48
【问题描述】:

我将对象存储在 std::vector 中,并且我想尽可能避免调用析构函数。
我用移动的替换了复制构造函数和赋值:

class Object
{
    Object(const Object&) = delete;
    Object(Object&&);
    Object& operator=(const Object&) = delete;
    Object& operator=(Object&&);
    [...]
};

我是这样初始化的:

std::vector<Object>   container;

container.reserve(42) // Reserve a lot in order to be sure it won't be a problem

然后,我用emplace_back添加两个元素(构造函数接受一个int参数):

container.emplace_back(1);
container.emplace_back(3);

直到那里,一切都很好。但后来我想在最后一个元素之前插入一个带有emplace的元素:

auto it = container.end();

it--; // Last position.
it--; // Before last position.
container.emplace(it, 2);

但是这里调用了析构函数。

我试图找出为什么在 Valgrind 中,emplace 函数调用 _M_insert_aux 调用了我的析构函数。

我怎样才能避免这种情况?

【问题讨论】:

  • 在不调用析构函数至少一次的情况下,您打算如何做到这一点?
  • emplaceemplace_back 不同。 emplace_back只是构造一个新元素,emplace要处理已经构造在指定位置的元素。
  • @T.C.这正是我的问题……

标签: c++ c++11 destructor stdvector


【解决方案1】:

这是无法避免的。这就是vector 的工作原理。这是一个连续的数组。将新元素插入连续数组的唯一方法是将旧元素移动。这意味着使用移动分配将它们移动到新位置。

因此,如果您有以下向量及其内容:

[5][12][16]

如果你在第二个元素之后插入,那么在某些时候你会有这个:

[5][12][*][16]

其中“*”是一个被移动元素的值。

然后是emplaceemplace 显式地构造该值;这就是它的用途。但是,第三个元素中已经存在一个活动对象:移动的值。

因此,必须销毁此对象,然后才能在其位置构造新对象。因此必须调用析构函数。

如果您使用insert 而不是emplace,那么您仍然会调用析构函数。但这将是您传递给insert 函数的对象的析构函数。

所以会有一个名为somewhere的“额外”析构函数。

但实际上,您不应该担心析构函数调用的数量。关注绝对成本。一般来说,如果你有一个只移动类型,一个移动值的析构函数会很便宜。

【讨论】:

  • 我的对象在构造时使用new[] 分配一个元素,并在销毁时使用delete[] 销毁它。由于堆分配可能很重,我想知道如何尽可能避免它。但也许太多了。
  • 这就是移动的目的。您的类应该将 new[]ed 数组的所有权从移出对象传递给移至对象。这消除了在移动对象的析构函数中 delete[] 数组的需要,以及在移动到对象中 new[] 新数组的需要。
  • @MilesBudnek 所以我只需要将原始对象的指针设置为nullptr 以便不删除它...感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-10
  • 2012-06-28
  • 1970-01-01
  • 2012-02-28
相关资源
最近更新 更多