vector push_back вызывает copy_constructor более одного раза?

Я немного смущен тем, как работает вектор push_back, со следующим fragmentом, который я ожидал, что конструктор копирования будет вызываться только дважды, но вывод указывает на другое. Это векторная внутренняя реструктуризация, которая приводит к такому поведению.

Вывод:

Inside default Inside copy with my_int = 0 Inside copy with my_int = 0 Inside copy with my_int = 1 
 class Myint { private: int my_int; public: Myint() : my_int(0) { cout << "Inside default " << endl; } Myint(const Myint& x) : my_int(x.my_int) { cout << "Inside copy with my_int = " << x.my_int << endl; } void set(const int &x) { my_int = x; } } vector myints; Myint x; myints.push_back(x); x.set(1); myints.push_back(x); 

Что происходит:

  1. x вставлен через push_back . Возникает одна копия: вновь созданный элемент инициализируется аргументом. my_int принимается за ноль, потому что x s default constructor инициализировал его так.

  2. Второй элемент – push_back ‘d; Вектор должен перераспределить память, так как была достигнута внутренняя емкость .
    Поскольку конструктор перемещения неявно определен для Myint 1, выбран конструктор копирования; Первый элемент копируется во вновь выделенную память (его my_int все равно ноль … так что конструктор my_int снова показывает my_int как 0 ), а затем x копируется для инициализации второго элемента (как с первым в шаге 1.). На этот раз x имеет значение my_int единице, и это говорит нам вывод конструктора копирования.

Таким образом, общая сумма звонков составляет три. Это может варьироваться от одной реализации к другой, поскольку первоначальная емкость может отличаться. Однако два вызова являются минимальными.

Вы можете уменьшить количество копий за счет резервирования большего объема памяти, то есть увеличить емкость векторов, чтобы перераспределение стало ненужным:

 myints.reserve(2); // Now two elements can be inserted without reallocation. 

Кроме того, вы можете высвободить копии при вставке следующим образом:

 myints.emplace_back(0); 

Это «emplaces» новый элемент – emplace_back является вариационным шаблоном и поэтому может принимать произвольное количество аргументов, которые затем пересылаются – без копий или перемещений – в конструктор элементов.

1 Потому что есть объявленный пользователем конструктор копирования.

Когда размер вектора увеличивается со вторым push_back , существующее содержимое вектора должно быть скопировано в новый буфер. Чтобы проверить, выведите myints.capacity() после первого push_back , это должно быть 1 .

Это зависит от того, сколько памяти зарезервировано для объекта типа std::vector . Похоже, что когда push_back был сначала выполнен, выделено память только для одного элемента. Когда был вызван второй раз push_back память была перераспределена для резервирования памяти для второго элемента. В этом случае элемент, который уже находится в векторе, копируется в новом месте. И затем добавляется второй элемент.

Вы могли бы зарезервировать себе достаточно памяти, чтобы избежать второго вызова конструктора копирования:

 vector myints; myints.reserve( 2 ); 

Вы получили это … это было изменение размера. Но я просто укажу, что если вы делаете подсчет бобов на ваших конструкторах, вас может заинтересовать «замена»:

 #include  #include  using namespace std; class Myint { private: int my_int; public: explicit Myint(int value = 0) : my_int(value) { cout << "Inside default " << endl; } Myint(const Myint& x) : my_int(x.my_int) { cout << "Inside copy with my_int = " << x.my_int << endl; } Myint(const Myint&& x) noexcept : my_int(x.my_int) { cout << "Inside move with my_int = " << x.my_int << endl; } }; int main() { vector myints; myints.reserve(2); myints.emplace_back(0); myints.emplace_back(1); // your code goes here return 0; } 

Это должно дать вам:

 Inside default Inside default 

И, из-за noexcept на ходу конструктора … если вы удалите reserve вы получите ход, а не копию:

 Inside default Inside default Inside move with my_int = 0 

Нет никакого реального преимущества в этом типе данных перемещения над копией. Но семантически это может быть большой разницей, если ваш тип данных был более «тяжелым» и имел способ «перемещать» его членов, что было больше похоже на передачу права собственности на некоторый указатель на большую структуру данных.

Вы правы в предположении, что дополнительный вызов конструктора копирования происходит из внутренней реструктуризации вектора.

См. Этот ответ для получения более подробной информации: https://stackoverflow.com/a/10368636/3708904

Или этот ответ по причине необходимости создания копии: https://stackoverflow.com/a/11166959/3708904

Давайте будем гением компьютера.