C de la construction et-assign question-construction de copie
-
20-09-2019 - |
Question
Voici un extrait de l'article 56 du livre "C ++ Gotchas":
Il est pas rare de voir d'un simple l'initialisation d'un objet Y écrit de trois manières différentes, comme si ils étaient équivalents.
Y a( 1066 );
Y b = Y(1066);
Y c = 1066;
En fait, tous les trois de ces initialisations entraînera probablement dans le même code d'objet étant générés, mais ils ne sont pas équivalents. L'initialisation d'une est connue en tant que initialisation directe, et il ne précisément ce que l'on pourrait s'y attendre. le l'initialisation est réalisée par une invocation directe de Y :: Y (int).
Les initialisations de b et c sont plus complexe. En fait, ils sont trop complexe. Ceux-ci sont à la fois copie initialisations. Dans le cas du initialisation de b, nous demandons la création d'un temporaire anonyme de type Y, initialisé avec la valeur 1066. Nous utilisons ensuite cette temporaire anonyme en tant que paramètre à la copie constructeur pour la classe Y pour initialiser b. Enfin, nous appelons le destructor pour le temporaire anonyme.
Pour tester cela, je l'ai fait une classe simple avec un membre de données (programme ci-joint à la fin) et les résultats ont été surprenants. Il semble que pour le cas c, l'objet a été construit par le constructeur de copie plutôt que comme suggéré dans le livre.
Quelqu'un sait-il si la norme linguistique a changé ou est-ce simplement une fonction d'optimisation du compilateur? J'utilisais Visual Studio 2008.
Exemple de code:
#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;
}
Sortie:
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
J'ai été très surpris par les résultats de la construction d et e. Pour être précis, je me attendais un objet vide à créer, puis un objet à créer et affecter à l'objet vide. Dans la pratique, les objets ont été créés par le constructeur de copie.
La solution
La syntaxe
X a = b;
a et b sont de type X a toujours signifié la construction de copie. Quelles que soient les variantes, telles que:
X a = X();
sont utilisés, il n'y a pas d'affectation en cours, et ne l'a jamais été. Construire et assigner serait quelque chose comme:
X a;
a = X();
Autres conseils
Le compilateur est autorisé à optimiser les cas b
et c
être le même que a
. En outre, la construction de la copie et les appels opérateur d'affectation peuvent être entièrement éliminés par le compilateur de toute façon, tout ce que vous voyez ne sont pas nécessairement les mêmes avec des compilateurs différents ou même les paramètres du compilateur.
de 17 C ++, tous les trois de ces sont équivalent (sauf Y::Y(int)
est explicit
, ce qui serait tout simplement désavouer c
) en raison de ce qu'on appelle souvent obligatoire copie élision .
Même Y c = 1066;
ne crée que l'un objet Y
parce que le résultat de la conversion implicite à Y
est un prvalue qui est utilisé pour initialiser c
plutôt que de créer un temporaire.