ProgrammationDéveloppeur C++

Que se passe-t-il lors du passage d'un objet de classe par valeur à une fonction et quels pièges faut-il prendre en compte lors de l'implémentation d'un constructeur de copie ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Lors du passage d'un objet par valeur à une fonction en C++, une copie de l'objet est créée à l'aide du constructeur de copie. Si un constructeur de copie personnalisé est défini dans la classe, il est appelé pour initialiser un objet temporaire, l'argument de la fonction. S'il n'est pas défini, le compilateur par défaut est utilisé, qui effectue une copie bit à bit (shallow copy).

Pièges :

  • Si la classe gère des ressources (par exemple, a des pointeurs bruts), le constructeur de copie généré par défaut entraînera des erreurs de double libération de mémoire et des fuites.
  • Il faut s'assurer que le constructeur de copie copie correctement les ressources (généralement deep copy).

Exemple :

class StringWrapper { char* data; public: StringWrapper(const char* str) { data = new char[strlen(str) + 1]; strcpy(data, str); } // Erreur : shallow copy StringWrapper(const StringWrapper& other) : data(other.data) {} ~StringWrapper() { delete [] data; } }; void foo(StringWrapper s) { // ... } int main() { StringWrapper s1("hello"); foo(s1); // UB!!! return 0; }

Question piégée

"Que se passe-t-il si l'on définit le constructeur de copie dans une classe avec un pointeur comme ceci : MyClass(const MyClass &other) : data(other.data) {} ? Quelles conséquences cela engendre-t-il ?"

Réponse correcte : Un tel constructeur de copie créera un objet avec un pointeur vers la même zone de mémoire que celui copié. Lors de la destruction des deux objets, la mémoire sera libérée deux fois (double free), ce qui entraînera un comportement indéfini. Il faut implémenter une deep copy :

MyClass(const MyClass &other) { data = new int(*other.data); }

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


Histoire

Dans un grand projet serveur, des conteneurs d'objets avec un "tableau brut" à l'intérieur et un constructeur de copie standard (shallow copy) étaient utilisés. Lors du passage d'objets par valeur, cela a entraîné une double libération de mémoire et des plantages de l'application, détectés uniquement en production.


Histoire

Dans une ancienne bibliothèque C++ pour le traitement d'images, le constructeur de copie ne copiait pas le tampon graphique, ce qui entraînait la modification d'une copie de l'image lors de la modification d'une autre et des bogues inattendus dans l'interface.


Histoire

Lors du retour d'un objet par valeur d'une fonction dans l'un des systèmes de stockage internes de mots de passe, les données étaient effacées lors de la destruction de l'objet temporaire (shallow copy), ce qui faisait que l'objet réel contenait un pointeur nul, et la fuite a été découverte par hasard lors d'un audit de sécurité.