Как реализовать конструктор копирования для класса, имеющего unique_ptr
переменную-член? Я рассматриваю только C ++ 11.
c++
c++11
unique-ptr
codefx
источник
источник
std::vector
.unique_ptr
, вы, вероятно, захотите конструктор перемещения, если ваша цель - поместить данные вstd::vector
. С другой стороны, стандарт C ++ 11 автоматически создает конструкторы перемещения, так что, возможно, вам действительно нужен конструктор копирования ...Ответы:
Поскольку
unique_ptr
общий доступ к файлу недоступен, вам необходимо либо глубоко скопировать его содержимое, либо преобразовать егоunique_ptr
в файлshared_ptr
.Вы можете, как упоминалось в NPE, использовать move-ctor вместо copy-ctor, но это приведет к другой семантике вашего класса. Move-ctor должен сделать член подвижным явно с помощью
std::move
:Наличие полного набора необходимых операторов также приводит к
Если вы хотите использовать свой класс в a
std::vector
, вам в основном нужно решить, должен ли вектор быть уникальным владельцем объекта, и в этом случае будет достаточно сделать класс перемещаемым, но не копируемым. Если вы опустите copy-ctor и copy-assignment, компилятор укажет вам, как использовать std :: vector с типами только для перемещения.источник
int
. Если у вас есть a, вunique_ptr<Base>
котором хранится aDerived
, приведенное выше будет нарезано.A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}
value_ptr
-unique_ptr
плюс информация об удалителе / копировщике.Обычный случай наличия a
unique_ptr
в классе - это возможность использовать наследование (в противном случае простой объект также часто подойдет, см. RAII). На этот случай в этой ветке до сих пор нет подходящего ответа .Итак, вот отправная точка:
... и цель, как сказано, сделать
Foo
копируемым.Для этого нужно сделать глубокую копию содержащегося указателя, чтобы убедиться, что производный класс скопирован правильно.
Это можно сделать, добавив следующий код:
Здесь в основном происходят две вещи:
Первый - это добавление конструкторов копирования и перемещения, которые неявно удаляются по
Foo
мере удаления конструктора копированияunique_ptr
. Конструктор перемещения может быть добавлен просто с помощью= default
... что просто чтобы сообщить компилятору, что обычный конструктор перемещения не должен быть удален (это работает, посколькуunique_ptr
уже есть конструктор перемещения, который можно использовать в этом случае).Для конструктора копирования
Foo
не существует аналогичного механизма, так как нет конструктора копированияunique_ptr
. Итак, нужно построить новыйunique_ptr
, заполнить его копией исходного указателя и использовать его как член скопированного класса.В случае, если речь идет о наследовании, копия исходного получателя должна быть сделана осторожно. Причина в том, что выполнение простого копирования с помощью
std::unique_ptr<Base>(*ptr)
в приведенном выше коде приведет к нарезке, т. Е. Копируется только базовый компонент объекта, а производная часть отсутствует.Чтобы этого избежать, копирование должно выполняться через шаблон клонирования. Идея состоит в том, чтобы сделать копию через виртуальную функцию,
clone_impl()
которая возвращаетBase*
в базовом классе. Однако в производном классе он расширяется посредством ковариации, чтобы вернутьDerived*
, и этот указатель указывает на вновь созданную копию производного класса. Затем базовый класс может получить доступ к этому новому объекту через указатель базового классаBase*
, обернуть его вunique_ptr
и вернуть через фактическуюclone()
функцию, которая вызывается извне.источник
unique_ptr
когда прямое сдерживание было бы иначе. Ответ??? Наследование .unique_ptr
болееoptional
обнуляемые тип.clone_impl
in base, компилятор не скажет вам, если вы забудете его в производном классе. Однако вы можете использовать другой базовый классCloneable
и реализовать там чистый виртуальныйclone_impl
. Тогда компилятор пожалуется, если вы забудете это в производном классе.Попробуйте этот помощник для создания глубоких копий и справитесь, когда источник unique_ptr равен нулю.
Например:
источник
Дэниел Фрей упомянул о решении для копирования, я бы рассказал о том, как переместить unique_ptr
Они называются конструктором перемещения и назначением перемещения.
вы могли бы использовать их вот так
Вам нужно обернуть a и c с помощью std :: move, потому что у них есть имя std :: move сообщает компилятору преобразовать значение в ссылку rvalue независимо от параметров. В техническом смысле std :: move аналогичен чему-то вроде " std :: rvalue "
После перемещения ресурс unique_ptr передается другому unique_ptr
Есть много тем, которые содержат ссылки на rvalue; это довольно просто начать .
Редактировать :
Перемещенный объект останется действующим, но в неопределенном состоянии .
Учебник по C ++ 5, ch13 также дает очень хорошее объяснение того, как «перемещать» объект.
источник
a
после вызова std :: move (a) вb
конструкторе перемещения? Это просто совершенно недействительно?Я предлагаю использовать make_unique
источник
unique_ptr
не копируется, его можно только перемещать.Это напрямую повлияет на Test, который во втором примере также только перемещается и не копируется.
На самом деле хорошо, что вы используете то,
unique_ptr
что предохраняет вас от большой ошибки.Например, основная проблема с вашим первым кодом заключается в том, что указатель никогда не удаляется, что очень, очень плохо. Скажем, вы бы исправили это:
Это тоже плохо. Что будет, если скопировать
Test
? Будет два класса, у которых есть указатель, указывающий на один и тот же адрес.Когда один
Test
будет уничтожен, он также уничтожит указатель. Когда ваш второйTest
будет уничтожен, он также попытается удалить память за указателем. Но он уже был удален, и мы получим некоторую ошибку времени выполнения с плохим доступом к памяти (или неопределенное поведение, если нам не повезет).Итак, правильный способ - реализовать конструктор копирования и оператор присваивания копии, чтобы поведение было ясным, и мы могли создать копию.
unique_ptr
здесь далеко впереди нас. Он имеет семантическое значение: « Я естьunique
, поэтому вы не можете просто скопировать меня». Таким образом, он предотвращает ошибку, связанную с реализацией имеющихся операторов.Вы можете определить конструктор копирования и оператор присваивания копии для особого поведения, и ваш код будет работать. Но вы по праву (!) Вынуждены это делать.
Мораль истории: всегда используйте
unique_ptr
в подобных ситуациях.источник