了解在C ++中分配容器时的对象复制

时间:2018-08-10 19:41:34

标签: c++ std

我试图了解涉及容器的对象复制和对象移动的行为。这是我写的示例代码。

#include<memory>
#include<iostream>
#include<vector>
using namespace std;
class X{
  int val;
public:
  X(int i):val(i){cout<<"constructor X"<< val <<endl;}
  X(){cout<<"constructor X"<< val <<endl;}
  X(const X &a) {cout<<"copy constructor X"<< val <<endl;}
  X(X &&a) {cout<<"move constructor X"<< val <<endl;}
  ~X(){cout<<"Distructor X"<< val << endl;}
  X& operator = (X a){ cout<<"Assignment operator"<<endl; return a;}
  void do1(){cout<<"Do"<<endl;}
};

vector<X> container_copy(){
  vector<X> a1(10);
  return a1;
}

main(){
  vector<X> b;

  vector<X> a = container_copy(); //#1 .
  b = a; // #2

  cout<<"Done"<<endl;
}

这是我使用

获得的示例输出
constructor X0
constructor X0
constructor X0
copy constructor X0
copy constructor X0
copy constructor X0
Done
Distructor X0
Distructor X0
Distructor X0
Distructor X0
Distructor X0
Distructor X0

这是我的查询。

  1. 为什么未调用标记为#1的语句的move构造函数

  2. 为什么调用拷贝构造函数而不是#2的赋值运算符

我正在使用以下命令在gcc下进行编译

g++ <file_name>.cpp --std=c++11

3 个答案:

答案 0 :(得分:1)

  

为什么未调用标记为#1的语句的move构造函数

由于返回值的优化,向量本身没有移动。该函数直接在调用者空间中构造其返回值,因此不会移动或复制。向量 elements 均不会移动。向量本身被复制或移动构造时,元素永远不会移动。

  

为什么调用#2的拷贝构造函数而不是赋值运算符

容器分配的工作方式是这样的:丢弃旧元素,然后复制新元素。在第二阶段,容器没有要分配的元素,因此也没有分配。为什么要丢掉旧元素?为了保持一致:现有元素的数量可以大于或小于新元素的数量,并且我们不希望此操作复杂且实现结果不一致。

答案 1 :(得分:1)

依次回答两个问题:

在第一种情况下,无论如何,在这种情况下都不会触发元素move-constructor。给定您的输出,函数调用正在进行RVO。即a实际上是被构造的,就好像它是函数中的向量一样。即使没有RVO,仍然不需要移动元素。而是,您会看到与您接下来要描述的第二种情况类似的东西

在第二种情况下,您询问为什么没有调用赋值运算符。调用复制构造是因为没有要分配的东西(转移到的要少得多),因此这里也不会发生元素移动。 b最初是空的,还记得吗? b = a的结果将简单地为a中的元素保留空间,然后从a中复制构造每个元素以完成b向量。您的元素或任何向量都不需要(也不希望)移动。您索要一份副本,这就是您得到的。

值得注意的是,您是否要求向量的移动分配而不是副本分配:

b = std::move(a);

still 不会获得逐元素的移动构造。向量本身将被移动。如果先前填充了b,那么这些内容将被销毁,结果将是b拥有a元素的所有权。

简而言之,给定代码的移动构造函数零使用。它永远不会被调用,因为它永远不会被调用。

答案 2 :(得分:0)

由于返回值的优化,调用container_copy可能不涉及复制/移动。

如果在打开优化的情况下进行编译,则几乎可以肯定该函数足够小以进行内联,因此您的主函数将变为甚至不需要优化返回值:

main(){
  vector<X> b;

  vector<X> a(10); //#1 .
  b = a; // #2

  cout<<"Done"<<endl;
}

#2正在调用复制构造函数,因为它是被分配的向量本身,在内部将执行类似的操作(大致来说,此代码不是最佳方法,只是为了给您一个想法):< / p>

std::vector<X> operator = ( const std::vector<X>& other )
{
   clear();
   for (const auto& x : other)
   {
      push_back(X(x)); // actually its probably doing a placement new rather than push_back which would result in an additional copy
   }
}