std::vector的move构造函数是否调用项的move构造器

Does the move constuctor of std::vector call the move constructor of the items

本文关键字:move 构造器 调用 构造函数 std vector 是否      更新时间:2023-10-16

我写了以下代码来理解std::vector 的移动

class PointerHolder
{
public:
PointerHolder()
{
cout << "Constructor called" << endl;
}
//copy constructor
PointerHolder(const PointerHolder& rhs)
{
cout << "Copy Constructor called" << endl;
}
//copy assignment operator
PointerHolder& operator = (const PointerHolder& rhs)
{
cout << "Copy Assignment Operator called" << endl;
return *this;
}
// move constructor
PointerHolder(PointerHolder&& rhs)
{
cout << "Move Constructor called" << endl;
}
// move assignment operator
PointerHolder& operator = (PointerHolder&& rhs)
{
cout << "Move Assignment Operator called" << endl;
return *this;
}   
};
void processVector(std::vector<PointerHolder> vec)
{
}
int main()
{
vector<PointerHolder> vec;
PointerHolder p1;
PointerHolder p2;
vec.push_back(p1);
vec.push_back(p2);
cout << "Calling processVectornn" << endl;
processVector(std::move(vec));
}

由于我在调用processVecor时传递了向量的Rvalue引用,因此当函数参数对象形成时,实际应该调用的是std::向量的move构造函数。是这样吗?

因此,我希望vecor的move构造函数本身会调用PointerHolder类的move构造器。

但没有任何证据可以证实这一点。

你能澄清一下这种行为吗。std::vector的move构造函数不是反过来调用单个项的move构造器吗

否。注意,std::vector的运动控制器的复杂性要求是恒定的。

复杂度

6) 常量。

这意味着std::vector的移动构造函数不会对每个单独的元素执行移动操作,这将使复杂性成为线性的(就像复制构造函数一样)。该实现可以直接移动内部存储器来实现它

否。相反,它会用所有元素窃取整个内存块。既然你可以把所有东西都拿走,为什么还要麻烦移动里面的东西呢?

(如果提供的分配器与源向量的分配器不相等,则分配器扩展的移动构造函数将需要执行成员移动。)

否,在调用std::vector的move构造函数时不需要移动元素。为了理解原因,我认为你应该有一个关于std::vector如何实现的良好心理模型。(大多数实现看起来是这样的,只是它们需要一些更复杂的处理分配器)

那么什么是std::vector

在最简单的形式中,它有3个成员:

  • 容量:(size_t)
  • A大小:(size_t)
  • 指向数据的指针(T*、std_unique_ptr、void*)

大小表示有多少元素在使用,容量表示有多少数据适合当前分配的数据。只有当您的新大小大于容量时,才需要重新分配数据。

分配的数据是未初始化的内存,在其中可以就地构造元素。

因此,考虑到这一点,实现向量的移动将是:

  • 复制容量/大小
  • 复制指针并在原始指针中设置为nullptr(行为与unique_ptr相同)

这样,新实例是完全有效的。旧的在valid but unspecified state中。最后一个意思是:你可以在不破坏程序的情况下调用析构函数。对于矢量,您也可以调用clear将其恢复到有效状态,或者调用operator=

给定这个模型,您可以很容易地解释所有运算符。只有移动任务有点复杂。

否。它不调用move构造函数。若要调用元素的move构造函数,您必须在推送到向量本身的同时调用std::move。

int main()
{
vector<PointerHolder> vec;
PointerHolder p1;
PointerHolder p2;
vec.push_back(std::move(p1));
vec.push_back(p2);
cout << "Calling processVectornn" << endl;
processVector(std::move(vec));
}

输出

名为的构造函数

名为的构造函数

移动名为的构造函数

名为的复制构造函数

调用processVector

如果我们正在查看标准(https://en.cppreference.com/w/cpp/container/vector/vector)它说,移动需要恒定的时间O(1)。

不管怎样,如果我们查看最常见的实现,std::vector是一个动态数组。动态数组首先为8个元素分配例如空间。如果我们需要9的空间,这个8乘以或增加一个特定的量,通常乘以1.44,2或类似的东西。

但关于移动方面:我们的成员变量是什么?我们如何移动它们?好吧,正如所提到的,动态数组只是指向第一个元素的指针,如果我们想移动结构,我们会将指针复制到另一个对象,并将旧指针设置为nullptr(或者NULL,如果你不关心nullptr的实现目的,那么nullptl显然是首选)。当然,像内部保存的大小(如果保存)这样的东西必须被复制,并且在旧对象中也必须设置为零(或者任何移动语义)。