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.

Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top