¿Cómo implemento un constructor de copia para una clase que tiene una unique_ptr
variable miembro? Solo estoy considerando C ++ 11.
c++
c++11
unique-ptr
codefx
fuente
fuente
std::vector
.unique_ptr
, probablemente desee un constructor de movimiento, si su objetivo es poner los datos en unstd::vector
. Por otro lado, el estándar C ++ 11 ha creado automáticamente constructores de movimiento, por lo que tal vez desee un constructor de copia ...Respuestas:
Dado
unique_ptr
que no se puede compartir, debe realizar una copia profunda de su contenido o convertirlounique_ptr
en un archivoshared_ptr
.Puede, como mencionó NPE, usar un move-ctor en lugar de un copy-ctor, pero eso resultaría en una semántica diferente de su clase. Un move-ctor necesitaría hacer que el miembro se pueda mover explícitamente a través de
std::move
:Tener un conjunto completo de los operadores necesarios también conduce a
Si desea utilizar su clase en a
std::vector
, básicamente debe decidir si el vector será el propietario único de un objeto, en cuyo caso sería suficiente para hacer que la clase sea movible, pero no copiable. Si omite copy-ctor y copy-assign, el compilador lo guiará sobre cómo usar un std :: vector con tipos de solo movimiento.fuente
int
. Si tiene ununique_ptr<Base>
que almacena unDerived
, lo anterior se dividirá.A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}
value_ptr
...unique_ptr
más Deleter / copiadora información.El caso habitual para que uno tenga un
unique_ptr
en una clase es poder usar la herencia (de lo contrario, un objeto simple a menudo también lo haría, consulte RAII). Para este caso, no hay una respuesta adecuada en este hilo hasta ahora .Entonces, aquí está el punto de partida:
... y el objetivo es, como se dijo, hacer
Foo
copiable.Para esto, es necesario hacer una copia profunda del puntero contenido para garantizar que la clase derivada se copie correctamente.
Esto se puede lograr agregando el siguiente código:
Básicamente, están sucediendo dos cosas aquí:
El primero es la adición de constructores de copiar y mover, que se eliminan implícitamente en
Foo
el constructor de copia deunique_ptr
. El constructor de movimiento se puede agregar simplemente por= default
... que es solo para que el compilador sepa que el constructor de movimiento habitual no se eliminará (esto funciona,unique_ptr
ya que ya tiene un constructor de movimiento que se puede usar en este caso).Para el constructor de copia de
Foo
, no existe un mecanismo similar ya que no hay constructor de copia deunique_ptr
. Entonces, uno tiene que construir uno nuevounique_ptr
, llenarlo con una copia del pointee original y usarlo como miembro de la clase copiada.En caso de que se trate de una herencia, la copia del pointee original debe hacerse con cuidado. La razón es que hacer una copia simple a través
std::unique_ptr<Base>(*ptr)
del código anterior resultaría en un corte, es decir, solo se copia el componente base del objeto, mientras que falta la parte derivada.Para evitar esto, la copia debe realizarse mediante el patrón de clonación. La idea es hacer la copia a través de una función virtual
clone_impl()
que devuelve aBase*
en la clase base. En la clase derivada, sin embargo, se extiende mediante covarianza para devolver aDerived*
, y este puntero apunta a una copia recién creada de la clase derivada. La clase base puede acceder a este nuevo objeto a través del puntero de la clase baseBase*
, envolverlo en aunique_ptr
y devolverlo a través de laclone()
función real que se llama desde el exterior.fuente
unique_ptr
cuando la contención directa haría lo contrario. ¿¿¿La respuesta??? Herencia .unique_ptr
másoptional
para los tipos anulables.clone_impl
in base, el compilador no te dirá si lo olvidas en la clase derivada. Sin embargo, podría usar otra clase baseCloneable
e implementar un virtual puroclone_impl
allí. Entonces el compilador se quejará si lo olvida en la clase derivada.Pruebe este ayudante para crear copias profundas y afronte cuando la fuente unique_ptr sea nula.
P.ej:
fuente
Daniel Frey menciona sobre la solución de copia, yo hablaría sobre cómo mover el unique_ptr
Se llaman constructor de movimiento y asignación de movimiento.
podrías usarlos así
Necesita envolver ayc por std :: move porque tienen un nombre std :: move le está diciendo al compilador que transforme el valor en una referencia rvalue cualesquiera que sean los parámetros. En sentido técnico, std :: move es una analogía con algo como " std :: rvalue "
Después de mover, el recurso de unique_ptr se transfiere a otro unique_ptr
Hay muchos temas que documentan la referencia de rvalue; este es bastante fácil para empezar .
Editar:
El objeto movido seguirá siendo un estado válido pero no especificado .
C ++ primer 5, ch13 también da una muy buena explicación sobre cómo "mover" el objeto
fuente
a
después de llamar a std :: move (a) en elb
constructor de movimiento? ¿Es totalmente inválido?Sugiero usar make_unique
fuente
unique_ptr
no es copiable, solo es movible.Esto afectará directamente a Test, que es, en su segundo ejemplo, también solo movible y no copiable.
De hecho, es bueno que uses lo
unique_ptr
que te protege de un gran error.Por ejemplo, el problema principal con su primer código es que el puntero nunca se elimina, lo que es realmente malo. Diga, solucionaría esto de la siguiente manera:
Esto también es malo. ¿Qué pasa si copia
Test
? Habrá dos clases que tengan un puntero que apunte a la misma dirección.Cuando uno
Test
se destruye, también destruirá el puntero. CuandoTest
se destruye su segundo , también intentará eliminar la memoria detrás del puntero. Pero ya se ha eliminado y obtendremos algún error de tiempo de ejecución de acceso a la memoria incorrecto (o un comportamiento indefinido si no tenemos suerte).Entonces, la forma correcta es implementar el constructor de copia y el operador de asignación de copia, de modo que el comportamiento sea claro y podamos crear una copia.
unique_ptr
está muy por delante de nosotros aquí. Tiene el significado semántico: " Yo soyunique
, así que no puedes simplemente copiarme. Por lo tanto, nos evita el error de implementar ahora los operadores en cuestión.Puede definir el constructor de copia y el operador de asignación de copia para un comportamiento especial y su código funcionará. Pero estás, con razón (!), Obligado a hacer eso.
La moraleja de la historia: utilizar siempre
unique_ptr
en este tipo de situaciones.fuente