C ++ construcción-y-asignar copia-constructo pregunta
-
20-09-2019 - |
Pregunta
Aquí es un extracto de artículo 56 del libro "C ++ Gotchas":
No es raro ver a un simple la inicialización de un objeto Y escrito cualquiera de tres maneras diferentes, como si que eran equivalentes.
Y a( 1066 );
Y b = Y(1066);
Y c = 1066;
En realidad, los tres de estos inicializaciones probablemente resultarán en el mismo código objeto siendo generan, pero no son equivalentes. La inicialización de un se conoce como una inicialización directa, y lo hace precisamente lo que cabría esperar. los inicialización se logra a través una invocación directa de Y :: Y (int).
Las inicializaciones de b y c son mas complejo. De hecho, son demasiado complejo. Estos son tanto copia inicializaciones. En el caso de la inicialización de b, estamos solicitando la creación de un temporal anónimo de tipo Y, inicializado con el valor 1066. A continuación, utilizamos este temporal anónimo como un parámetro para la copia constructor para la clase Y para inicializar si. Por último, llamamos al destructor para la anónima temporal.
Para probar esto, hice una clase simple con un miembro de datos (programa adjunto al final) y los resultados fueron sorprendentes. Parece que para el caso de C, el objeto fue construido por el constructor de copia en vez de como se sugiere en el libro.
¿Alguien sabe si el estándar del lenguaje ha cambiado o se trata simplemente una función de optimización del compilador? Yo estaba usando Visual Studio 2008.
Ejemplo de código:
#include <iostream>
class Widget
{
std::string name;
public:
// Constructor
Widget(std::string n) { name=n; std::cout << "Constructing Widget " << this->name << std::endl; }
// Copy constructor
Widget (const Widget& rhs) { std::cout << "Copy constructing Widget from " << rhs.name << std::endl; }
// Assignment operator
Widget& operator=(const Widget& rhs) { std::cout << "Assigning Widget from " << rhs.name << " to " << this->name << std::endl; return *this; }
};
int main(void)
{
// construct
Widget a("a");
// copy construct
Widget b(a);
// construct and assign
Widget c("c");
c = a;
// copy construct!
Widget d = a;
// construct!
Widget e = "e";
// construct and assign
Widget f = Widget("f");
return 0;
}
Salida:
Constructing Widget a
Copy constructing Widget from a
Constructing Widget c
Assigning Widget from a to c
Copy constructing Widget from a
Constructing Widget e
Constructing Widget f
Copy constructing Widget from f
Lo que más me sorprendió por los resultados de la construcción dye. Para ser precisos, que estaba esperando un objeto vacío que se crea, y luego un objeto que se creará y se asigna al objeto vacío. En la práctica, los objetos fueron creados por el constructor de copia.
Solución
La sintaxis
X a = b;
donde a y b son de tipo X siempre ha significado construcción copia. Cualquiera que sea variantes, como por ejemplo:
X a = X();
se utilizan, no hay asignación pasando, y nunca lo ha sido. Construir y asignar sería algo así como:
X a;
a = X();
Otros consejos
El compilador está permitido optimizar los casos b
y c
a ser el mismo que a
. Además, la construcción de copia y las llamadas operador de asignación puede ser eliminado por completo, por el compilador de todos modos, así que lo que se ve no es necesariamente lo mismo con diferentes compiladores o incluso configuración del compilador.
A partir de C ++ 17, los tres de estos son equivalente (a menos Y::Y(int)
es explicit
, lo que simplemente no permitir c
) a causa de lo que se denomina obligatoria copia elisión .
A pesar de Y c = 1066;
crea sólo el objeto Y
porque el resultado de la conversión implícita a Y
es un prvalue que se utiliza para inicializar c
en vez de crear un temporal.