Подтвердить что ты не робот

Как инициализировать вектор STL/список с классом без вызова конструктора копирования

У меня есть программа на С++, которая использует std:: list, содержащий экземпляры класса. Если я звоню, например. myList.push_back(MyClass(variable)); он проходит процесс создания временной переменной, а затем сразу же копирует его в вектор, а затем удаляет временную переменную. Это не так эффективно, как я хочу, и отстой, когда вам нужна глубокая копия.

Мне бы хотелось, чтобы конструктор моего класса new что-то и не должен был реализовывать конструктор копирования, просто чтобы выделить мою память во второй раз и тратить время выполнения. Мне также не нужно было бы немедленно найти экземпляр класса из вектора/списка, а затем вручную выделить память (или сделать что-то ужасное, как выделить память в самом конструкторе копирования).

Есть ли способ обойти это (я не использую Visual Studio BTW)?

4b9b3361

Ответ 1

Конструкторы перемещения С++ 0x являются частичным обходным решением: вместо вызываемого конструктора копирования конструктор перемещения будет. Конструктор перемещения похож на конструктор копирования, за исключением того, что ему разрешено аннулировать исходный аргумент.

С++ 0x добавляет еще одну функцию, которая будет делать именно то, что вы хотите: emplace_back. (N3092 §23.2.3) Вы передаете аргументы конструктору, затем он вызывает конструктор с этими аргументами (через ... и пересылку), поэтому никакой другой конструктор не может быть вызван.

Как и для С++ 03, ваш единственный вариант - добавить неинициализированное состояние в ваш класс. Выполните фактическое построение в другой функции, вызванной сразу после push_back. boost::optional может помочь вам избежать инициализации членов класса, но это, в свою очередь, требует их конструктивного копирования. Или, как говорит Фред, выполнить то же самое с первоначально пустыми интеллектуальными указателями.

Ответ 2

Гм. В интересах science, я взломал крошечную тестовую программу, чтобы проверить, не копирует ли компилятор копию:

#include <iostream>
#include <list>
using namespace std;

class Test
{
public:
  Test() { cout<<"Construct\n"; }
  Test(const Test& other) { cout<<"Copy\n"; }
  Test& operator=(const Test& other) { cout<<"Assign\n"; return (*this); }
};

Test rvo() { return Test(); }
int main()
{
  cout<<"Testing rvo:\n";
  Test t = rvo();
  cout<<"Testing list insert:\n";
  list<Test> l;
  l.push_back(Test());
}

А вот мой вывод на MSVС++ 2008:

Testing rvo:
Construct 
Testing list insert:
Construct
Copy

Это то же самое для обеих версий отладки и выпуска: RVO работает, временная передача объектов не оптимизирована.
Если я не ошибаюсь, ссылки Rvalue, добавленные в стандарт С++ 0x, предназначены для решения этой самой проблемы.

Ответ 3

Конструкторы перемещения С++ 0x (доступные с VС++ 2010 и последние компиляторы GNU) - именно то, что вы ищете.

Ответ 4

Фактически, в этом случае компилятор может удалить копию.

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

Ответ 5

Откроется окно Boost ptr_container. Я использую ptr_vector в частности:

boost::ptr_vector<Foo> c;
c.push_back(new Foo(1,2,3) );
c[0].doSomething()

и когда он выходит из области видимости, на каждый элемент вектора будет вызываться delete.

Ответ 6

Используйте shared_ptr или shared_array для управления памятью, которую хочет выделить ваш класс. Затем созданный компилятором копировальный конструктор просто увеличит счетчик ссылок как самих копий shared_ptr. Это важная концепция использования стандартных контейнеров, которые ваши элементы дешевы для копирования. Стандартная библиотека делает копии повсеместно.

Ответ 7

Я бы предложил использовать std::vector<std::unique_ptr>, потому что он автоматически освобождает память при необходимости, с меньшими накладными расходами, чем std::shared_ptr. Единственное предостережение в том, что этот указатель не имеет подсчета ссылок, поэтому вам нужно быть осторожным, чтобы не копировать указатель сам где-то еще, чтобы данные не были удалены, когда они все еще используются где-то еще.