ProgrammationDéveloppeur C++

Qu'est-ce qu'un constructeur de copie et un opérateur d'assignation ? Comment éviter les erreurs lors de la copie d'objets avec une mémoire dynamique ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Le constructeur de copie et l'opérateur d'assignation par copie sont nécessaires pour travailler avec des objets qui possèdent des ressources (par exemple, de la mémoire dynamique). Par défaut, le compilateur générera une copie champ par champ, ce qui est dangereux pour un pointeur brut :

class Buffer { public: Buffer(size_t size) { data = new int[size]; this->size = size; } ~Buffer() { delete[] data; } // Implémentation correcte du constructeur de copie Buffer(const Buffer& other) : size(other.size) { data = new int[size]; std::copy(other.data, other.data + size, data); } // Opérateur d'assignation correct Buffer& operator=(const Buffer& other) { if (this != &other) { delete[] data; size = other.size; data = new int[size]; std::copy(other.data, other.data + size, data); } return *this; } private: int* data; size_t size; };

Sinon, lors de la copie de deux objets, l'un libère la mémoire, l'autre reste avec un pointeur pendu (double free ou use after free).

Question piégeuse

Que se passe-t-il si l'on déclare explicitement seulement le constructeur de copie, mais pas l'opérateur d'assignation ? Quand est-il nécessaire, et quand ne l'est-il pas ?

Réponse : Si seulement le constructeur de copie est déclaré, mais que l'opérateur d'assignation n'est pas déclaré, le compilateur générera un opérateur d'assignation par défaut (copie bit à bit), ce qui est dangereux si l'objet contient des ressources dynamiques, c'est-à-dire qu'il détruit le même pointeur deux fois.

Dans le cas de la gestion de la mémoire, il est nécessaire de mettre en œuvre les deux : à la fois le constructeur de copie et l'opérateur d'assignation, afin d'éviter les erreurs de copie/de fuite de mémoire.

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


Histoire

Dans un serveur multimédia, lors de la copie d'un objet tampon, un constructeur de copie a été utilisé, mais l'opérateur d'assignation a été oublié. Lorsque l'on assignait un tampon à un autre, une double libération de mémoire se produisait lors de la destruction des objets. L'erreur s'est manifestée lors des tests de stress.


Histoire

Dans un projet de logistique de transport, l'opérateur d'assignation par défaut copiait une structure avec un pointeur vers un tableau de coordonnées. Après la suppression d'un objet, le second accédait à la mémoire libérée, provoquant des erreurs de segmentation.


Histoire

Dans un de nos projets sur des ressources graphiques, nous avons oublié de mettre en œuvre le constructeur de copie lors de la transmission d'objets entre des threads. La passation par valeur entraînait une copie superficielle, et après la modification de l'objet dans un autre thread, cela entraînait une corruption des données et un crash du programme.