c ++ 11:std :: vector' push_back()仍然涉及复制而不是移动

时间:2017-07-22 08:21:22

标签: c++ c++11 move-semantics

我们都知道std :: vector会动态增加它的容量。例如一个向量在开始时容量= 0,通过推回元素,它的容量逐渐增加到1,2,4,8 ......

每次增加容量时,都必须将旧元素复制/移动到新创建的内存数组中。

我的问题是,当元素类同时具有复制构造函数和rvalue构造函数(用于移动语义)时,std :: vector将更喜欢复制构造函数而不是rvalue构造函数。这是为什么?因为很明显没有人需要旧的变量。

以下是一个例子:

#include <iostream>
#include <vector>
#include <chrono>
#include <cstring>

class foo {
public:
  foo()
    : id(-1)
    , c(nullptr)
  {
    std::cout << "foo()\n";
  }

  foo(int i)
    : id(i)
  {
    c = new char[100000000];
    std::cout << "foo(" << i << ")\n";
  }
//  foo(const foo& rhs)
//  {
//    if (rhs.c)
//    {
//    id = rhs.id;
//    std::cout << "copy " << id << std::endl;
//    c = new char[100000000];
//    std::memcpy(c, rhs.c, 100000000);
//    }
//  }
  foo(foo&& rhs)
  {
    id = rhs.id;
    std::cout << "move " << id << std::endl;
    c = rhs.c;
    rhs.c = nullptr;
  }
  ~foo()
  {
    if (c)
      {
        delete [] c;
        std::cout << "desctruct " << id << std::endl;
      }
  }
public:
  char* c;
  int id = -1;
};

std::vector<foo> getFoo()
{
  std::vector<foo> vec;
//  vec.reserve(3);
  for (int i = 0; i < 3; ++i)
    {
      foo f(i);
      vec.push_back(std::move(f));
      std::cout << "capacity: " << vec.capacity() << std::endl;
    }
  return vec;
}

int main(int argc, char *argv[])
{
  using namespace std::chrono;
  auto tic = high_resolution_clock::now();
  auto&& vec = getFoo();
  std::cout << "vec size: " << vec.size() << std::endl;
  auto toc = high_resolution_clock::now();
  std::cout << duration_cast<microseconds>(toc - tic).count() << "us" << std::endl;
  return 0;
}

如您所见,如果我注释掉复制构造函数,则只涉及移动操作:

foo(0)
move 0
capacity: 1
foo(1)
move 1
move 0
capacity: 2
foo(2)
move 2
move 1
move 0
capacity: 4
vec size: 3
165us
desctruct 2
desctruct 1
desctruct 0

但是,如果我取消注释复制构造函数,那么会有很多复制操作,这非常昂贵:

foo(0)
move 0
capacity: 1
foo(1)
move 1
copy 0
desctruct 0
capacity: 2
foo(2)
move 2
copy 1
copy 0
desctruct 1
desctruct 0
capacity: 4
vec size: 3
1008324us
desctruct 2
desctruct 1
desctruct 0

如何避免此类复制操作?

0 个答案:

没有答案