assignment - copy constructor struct c++
Autodestrucción: this-> MyClass:: ~ MyClass() vs. this-> ~ MyClass() (3)
Puedes decidir cómo llamar al destructor:
this->MyClass::~MyClass(); // Non-virtual call
this->~MyClass(); // Virtual call
En mi búsqueda por aprender C ++, me topé con el artículo Escribiendo constructores de copia y operadores de asignación, que propone un mecanismo para evitar la duplicación de código entre los constructores de copia y los operadores de asignación.
Para resumir / duplicar el contenido de ese enlace, el mecanismo propuesto es:
struct UtilityClass
{
...
UtilityClass(UtilityClass const &rhs)
: data_(new int(*rhs_.data_))
{
// nothing left to do here
}
UtilityClass &operator=(UtilityClass const &rhs)
{
//
// Leaves all the work to the copy constructor.
//
if(this != &rhs)
{
// deconstruct myself
this->UtilityClass::~UtilityClass();
// reconstruct myself by copying from the right hand side.
new(this) UtilityClass(rhs);
}
return *this;
}
...
};
Esta parece ser una buena forma de evitar la duplicación de código al tiempo que garantiza la "integridad programática", pero debe sopesarse con el riesgo de perder el esfuerzo liberando y luego asignando memoria anidada que podría, en cambio, reutilizarse (como señala su autor).
Pero no estoy familiarizado con la sintaxis que se encuentra en su núcleo:
this->UtilityClass::~UtilityClass()
Supongo que esta es una forma de llamar al destructor del objeto (para destruir el contenido de la estructura del objeto) manteniendo la estructura en sí. Para un novato en C ++, la sintaxis parece una mezcla extraña de un método de objeto y un método de clase.
¿Podría alguien explicarme esta sintaxis o indicarme un recurso que lo explique?
¿En qué se diferencia esa llamada de la siguiente?
this->~UtilityClass()
¿Es esta una llamada legítima? ¿Esto destruye adicionalmente la estructura del objeto (libre del montón; se desprende de la pila)?
TL, versión DR: NO SIGA NINGÚN CONSEJO DADO POR EL AUTOR DE ESTE ENLACE
El enlace sugiere que esta técnica se puede utilizar en una clase base siempre que no se use una llamada de destructor virtual, ya que al hacerlo se destruirían partes de la clase derivada, que no es responsabilidad del operator=
clase base operator=
.
Esta línea de razonamiento falla totalmente. La técnica nunca se puede utilizar en una clase base. La razón es que el estándar de C ++ solo permite la sustitución en el lugar de un objeto por otro objeto del mismo tipo (consulte la sección 3.8 del estándar):
Si, una vez que finaliza la vida útil de un objeto y antes de que se reutilice o libere el almacenamiento que ocupó el objeto ocupado, se creará un nuevo objeto en la ubicación de almacenamiento que ocupaba el objeto original, un puntero que apuntaba al objeto original, una referencia que referido al objeto original, o el nombre del objeto original se referirá automáticamente al nuevo objeto y, una vez que ha comenzado la vida útil del nuevo objeto, puede usarse para manipular el nuevo objeto si:
- el almacenamiento para el nuevo objeto superpone exactamente la ubicación de almacenamiento que ocupaba el objeto original, y
- el nuevo objeto es del mismo tipo que el objeto original (ignorando los cali fi cadores cv de nivel superior), y
- el tipo del objeto original no es constante y, si es un tipo de clase, no contiene ningún miembro de datos no estáticos cuyo tipo sea constante o un tipo de referencia, y
- el objeto original era el objeto más derivado (1.8) de tipo
T
y el nuevo objeto es el objeto más derivado de tipoT
(es decir, no son subobjetos de clase base).
En el código original, ambos return *this;
y el uso posterior del objeto es un comportamiento indefinido; acceden a un objeto que ha sido destruido, no al objeto recién creado.
Este es un problema en la práctica también: la nueva llamada de ubicación configurará un ptr de v-table correspondiente a la clase base, no el tipo derivado correcto del objeto.
Incluso para las clases foliares (clases no básicas) la técnica es altamente cuestionable.
TL; DR NO HAGAS ESTO.
Para responder a la pregunta específica:
En este ejemplo particular, no hay diferencia. Como se explica en el artículo al que se vincula, habría una diferencia si se tratara de una clase base polimórfica, con un destructor virtual.
Una llamada calificada:
this->UtilityClass::~UtilityClass()
llamaría específicamente al destructor de esta clase, no al de la clase más derivada. Por lo tanto, solo destruye el subobjeto asignado, no todo el objeto.
Una llamada no calificada:
this->~UtilityClass()
usaría el despacho virtual para llamar al destructor más derivado, destruyendo el objeto completo.
El escritor del artículo afirma que lo primero es lo que desea, de modo que solo se asigna al subobjeto base, dejando intactas las partes derivadas. Sin embargo, lo que realmente hace es sobrescribir parte del objeto con un nuevo objeto del tipo base; ha cambiado el tipo dinámico y ha filtrado todo lo que estaba en las partes derivadas del objeto antiguo. Esto es algo malo de hacer en cualquier circunstancia. También ha introducido un problema de excepción: si la construcción del nuevo objeto falla, entonces el objeto antiguo se queda en un estado no válido y ni siquiera se puede destruir de forma segura.
ACTUALIZACIÓN : también tiene un comportamiento indefinido ya que, como se describe en otra respuesta, está prohibido usar la ubicación nueva para crear un objeto encima de (parte de) un objeto de tipo diferente.
Para los tipos no polimórficos, una buena manera de escribir un operador de asignación de copia es con el lenguaje de copia e intercambio . Eso evita la duplicación al reutilizar el constructor de copia y proporciona una garantía de excepción sólida: si la asignación falla, el objeto original no se modifica.
Para los tipos polimórficos, copiar objetos es más complicado, y generalmente no se puede hacer con un operador de asignación simple. Un enfoque común es una función de clone
virtual, que cada tipo anula para asignar dinámicamente una copia de sí mismo con el tipo correcto.