Règle de trois en C++ : ce que tout développeur doit savoir

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Qu'est-ce que la Règle de Trois en C++ ? Analyse avec exemples



Si vous avez déjà écrit en C++ une classe qui gère la mémoire dynamique (par exemple, contenant new ou malloc), vous avez probablement rencontré la Règle de trois (Rule of Three). C'est l'une des règles les plus fondamentales du langage, qui évite les fuites mémoire, les doubles suppressions et les comportements indéfinis. Analysons-la avec des exemples concrets pour que vous ne vous contentiez pas de mémoriser la règle, mais que vous compreniez pourquoi elle fonctionne.



Imaginez : vous avez écrit une classe qui alloue de la mémoire dans le constructeur. Le compilateur génère gentiment pour vous le constructeur de copie et l'opérateur d'affectation. Mais il le fait de manière superficielle (shallow copy). Et là, les problèmes commencent. La Règle de trois dit : si votre classe définit explicitement un destructeur, un constructeur de copie ou un opérateur d'affectation, il est fort probable que vous deviez définir les trois. Pourquoi ? Parce que c'est un signal que votre classe gère une ressource (mémoire, fichier, connexion réseau) et que la copie doit être profonde.



Le problème : la copie superficielle (shallow copy)

Regardons un exemple typique. La classe String, qui stocke une chaîne C en mémoire dynamique :



class String {private:    char* data;    size_t size;public:    // Constructeur    String(const char* str) {        size = strlen(str);        data = new char[size + 1];        strcpy(data, str);    }

// Destructeur ~String() { delete[] data; }

// Constructeur de copie — généré par le compilateur (superficiel) // Opérateur d'affectation — généré par le compilateur (superficiel)

void print() const { std::cout << data << std::endl; }};


Tout semble correct, jusqu'à ce que nous essayions de copier l'objet :



int main() {    String s1("Hello");    String s2 = s1;  // Appel du constructeur de copie (généré)    s1.print();    s2.print();    // Le programme plantera avec une erreur double free !    return 0;}


Que s'est-il passé ? Le compilateur a généré un constructeur de copie qui a simplement copié le pointeur data. Désormais, s1.data et s2.data pointent vers le même bloc mémoire. Lorsque les objets sortent de la portée, leurs destructeurs sont appelés. Le premier destructeur libère la mémoire, le second tente de la libérer à nouveau — double free. Le programme plante ou se comporte de manière imprévisible.



La solution : la Règle de trois en action

Pour corriger la situation, il faut implémenter les trois fonctions membres spéciales. C'est ce qu'on appelle la Règle de trois. Écrivons la version correcte de la classe :



class String {private:    char* data;    size_t size;public:    // Constructeur    String(const char* str) {        size = strlen(str);        data = new char[size + 1];        strcpy(data, str);    }

// Destructeur ~String() { delete[] data; }

// Constructeur de copie — copie profonde String(const String& other) { size = other.size; data = new char[size + 1]; strcpy(data, other.data); }

// Opérateur d'affectation — copie profonde String& operator=(const String& other) { if (this == &other) return *this; // protection contre l'auto-affectation

delete[] data; // libération des anciennes ressources

size = other.size; data = new char[size + 1]; strcpy(data, other.data);

return *this; }

void print() const { std::cout << data << std::endl; }};


Maintenant, la copie fonctionne correctement : chaque objet possède son propre bloc mémoire. L'affectation aussi sans

Blogs

Book Recommendations