¿Cómo implemento un constructor de copia para una clase que tiene una unique_ptrvariable 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_ptrque no se puede compartir, debe realizar una copia profunda de su contenido o convertirlounique_ptren 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_ptrmás Deleter / copiadora información.El caso habitual para que uno tenga un
unique_ptren 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
Foocopiable.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
Fooel 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_ptrya 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_ptry devolverlo a través de laclone()función real que se llama desde el exterior.fuente
unique_ptrcuando la contención directa haría lo contrario. ¿¿¿La respuesta??? Herencia .unique_ptrmásoptionalpara los tipos anulables.clone_implin base, el compilador no te dirá si lo olvidas en la clase derivada. Sin embargo, podría usar otra clase baseCloneablee implementar un virtual puroclone_implallí. 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
adespués de llamar a std :: move (a) en elbconstructor de movimiento? ¿Es totalmente inválido?Sugiero usar make_unique
fuente
unique_ptrno 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_ptrque 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
Testse destruye, también destruirá el puntero. CuandoTestse 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_ptrestá 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_ptren este tipo de situaciones.fuente