ProgrammationDéveloppeur C++

Quelle est la différence entre l'initialisation directe et l'initialisation indirecte des objets en C++, et quelles peuvent être les conséquences lors de l'utilisation des types intégrés et des types utilisateur ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

En C++, on distingue l'initialisation directe (direct) et l'initialisation indirecte (copy) des objets :

  • Initialisation directe :

    MyClass obj(arg1, arg2); int x(5);

    Pour les classes, cela appelle directement le constructeur approprié.

  • Initialisation indirecte :

    MyClass obj = MyClass(arg1, arg2); int x = 5;

    Cela crée un objet temporaire, puis le copie (ou le déplace) (utilise le constructeur de copie / de déplacement).

Différences :

  1. Pour les types intégrés, le comportement est identique. Pour les classes, il peut y avoir un ensemble différent de constructeurs appelés ou même une initialisation interdite.
  2. La liste d'initialisation des membres n'est pas toujours équivalente pour les différentes façons d'initialiser en raison des exigences du compilateur.
  3. L'élimination de copie (optimisation du compilateur pour éviter des copies inutiles) réduit souvent la différence à néant (mais pas toujours, surtout avant C++17).

Exemple :

struct NonCopyable { NonCopyable(int) {} NonCopyable(const NonCopyable&) = delete; }; NonCopyable a(5); // OK : direct NonCopyable b = NonCopyable(5); // OK avec optimisation, erreur avant C++17 sans elle NonCopyable c = 5; // Erreur : pas de constructeur de copie

Question piège

"L'initialisation par copie peut-elle provoquer plus d'appels de constructeurs que l'initialisation directe, si C++11 est utilisé avec un compilateur optimiseur ?"

Réponse : Théoriquement — oui. Sans l'élimination de copie, l'initialisation par copie crée un objet temporaire, puis appelle le constructeur de copie ou de déplacement. L'initialisation directe appelle toujours uniquement le constructeur principal. Cependant, les compilateurs modernes avec optimisation (C++17 et plus) éliminent cette différence grâce à l'élimination de copie garantie.

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet


Histoire

Dans un projet financier, on a utilisé l'initialisation par copie avec un objet temporaire, alors que le type requis n'avait pas de constructeur de copie. L'application ne se compilait pas sur les anciens compilateurs (avant C++17), alors que l'initialisation directe fonctionnait.


Histoire

Dans un utilitaire de sérialisation de données, les programmeurs pensaient que l'initialisation directe et l'initialisation par copie étaient équivalentes. En conséquence, des copies inutiles de grandes structures ont eu lieu, entraînant des baisses de performances.


Histoire

Lors de l'initialisation de tableaux globaux d'objets utilisateur, l'initialisation indirecte a été utilisée. Les erreurs apparaissaient uniquement sur une plateforme, où l'élimination de copie ne se produisait pas, entraînant des appels implicites de constructeurs supplémentaires en raison des particularités de l'ABI.