У меня есть программа C++, которая использует станд.:: список, содержащий экземпляры класса. Если я звоню, например. myList.push_back(MyClass(variable));
это проходит процесс создания временной переменной, и затем сразу копирует его в вектор и впоследствии удаляет временную переменную. Это совсем не столь эффективно, как я хочу, и сосет, когда Вам нужна глубокая копия.
Я хотел бы иметь конструктора своего класса new
что-то и не должно реализовать конструктора копии только, чтобы выделить мою память во второй раз и потратить впустую время выполнения. Я не должен также сразу найти экземпляр класса от вектора/списка и затем вручную выделить память (или сделать что-то ужасное как выделяет память в конструкторе копии саму).
Есть ли какой-либо путь вокруг этого (я не использую Visual Studio BTW)?
Конструкторы перемещения C ++ 0x - это частичный обходной путь: вместо вызываемого конструктора копирования будет использоваться конструктор перемещения. Конструктор перемещения похож на конструктор копирования, за исключением того, что ему разрешено аннулировать исходный аргумент.
C ++ 0x добавляет еще одну функцию, которая будет делать именно то, что вы хотите: emplace_back
. (N3092 §23.2.3) Вы передаете ему аргументы конструктору, затем он вызывает конструктор с этими аргументами (посредством ...
и пересылки), поэтому никакой другой конструктор никогда не сможет быть вызванным.
Что касается C ++ 03, единственный вариант - добавить в класс неинициализированное состояние. Выполните фактическое построение в другой функции, вызываемой сразу после push_back
. boost :: optional
может помочь вам избежать инициализации членов класса, но это, в свою очередь, требует, чтобы они были копируемыми. Или, как говорит Фред, сделайте то же самое с изначально пустыми интеллектуальными указателями.
Гм. В интересах науки я создал крошечную тестовую программу, чтобы проверить, удаляет ли компилятор копию или нет:
#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());
}
И вот мой результат на MSVC ++ 2008:
Testing rvo: Construct Testing list insert: Construct Copy
То же самое и для отладочной, и для выпускной сборок: RVO работает, временная передача объектов не оптимизирована.
Если я не ошибаюсь, ссылки Rvalue , добавляемые в стандарт C ++ 0x, предназначены для решения именно этой проблемы.
Используйте shared_ptr
или shared_array
для управления памятью, которую хочет выделить ваш класс. Затем предоставленный компилятором конструктор копирования просто увеличит счетчик ссылок, поскольку shared_ptr
копирует себя. Важной концепцией использования стандартных контейнеров является то, что копирование ваших элементов будет дешевым. Стандартная библиотека делает копии повсюду.
Фактически, в этом случае компилятор может исключить копию.
Если ваш компилятор этого не делает, один из способов избежать копирования - это включить в список указатели вместо экземпляров. Вы можете использовать умные указатели, чтобы очистить объекты за вас.
Конструкторы перемещения C ++ 0x (доступные в VC ++ 2010 и последних компиляторах GNU) - это именно то, что вам нужно.
Проверьте библиотека ptr_container . В частности, я использую ptr_vector:
boost::ptr_vector<Foo> c;
c.push_back(new Foo(1,2,3) );
c[0].doSomething()
, и когда он выходит за пределы области видимости, для каждого элемента вектора будет вызываться delete
.