Menu
Coddy logo textTech

Surcharge de l'opérateur d'affectation

Fait partie de la section Programmation Orientée Objet du Journey C++ de Coddy — leçon 44 sur 104.

L'opérateur d'affectation par copie (=) est appelé lorsque vous affectez un objet existant à un autre. Contrairement au constructeur de copie (qui crée un nouvel objet), l'opérateur d'affectation travaille avec un objet qui existe déjà et qui peut avoir des ressources devant être libérées au préalable.

class Buffer {
    int* data;
    size_t size;
    
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    ~Buffer() { delete[] data; }
    
    Buffer& operator=(const Buffer& other) {
        if (this != &other) {              // 1. Vérification de l'auto-affectation
            delete[] data;                  // 2. Nettoyage de la ressource existante
            size = other.size;              // 3. Copie des données
            data = new int[size];
            for (size_t i = 0; i < size; ++i)
                data[i] = other.data[i];
        }
        return *this;                       // 4. Retourner *this
    }
};

La vérification de l'auto-affectation (this != &other) est critique. Sans elle, b = b; supprimerait les données avant de tenter de les copier, provoquant un comportement indéfini.

L'opérateur retourne *this par référence pour permettre le chaînage comme a = b = c;. Cela correspond au comportement des types intégrés.

Pour les classes gérant des ressources, vous implémenterez souvent à la fois les opérateurs d'affectation par copie et par déplacement :

Buffer& operator=(Buffer&& other) noexcept {
    if (this != &other) {
        delete[] data;           // Nettoyer la ressource actuelle
        data = other.data;       // Voler la ressource
        size = other.size;
        other.data = nullptr;    // Laisser la source dans un état valide
        other.size = 0;
    }
    return *this;
}

L'opérateur d'affectation par déplacement transfère la propriété au lieu de copier, rendant des opérations comme vec[0] = createBuffer(); bien plus efficaces.

challenge icon

Défi

Facile

Construisons une classe DynamicString qui gère un tableau de caractères alloué dynamiquement et traite correctement l'affectation entre des objets existants. Il s'agit d'un scénario pratique où vous devez transférer en toute sécurité des données de chaîne d'un objet à un autre sans fuites de mémoire ni pointeurs suspendus.

Vous allez créer deux fichiers pour organiser votre code :

  • DynamicString.h : Définissez une classe DynamicString qui stocke une chaîne de caractères de style C en utilisant la mémoire dynamique. Votre classe doit gérer :
    • Un pointeur privé char* pour les données de la chaîne
    • Un size_t privé pour la longueur
    • Un constructeur qui prend un const char* et crée une copie profonde (deep copy)
    • Un destructeur qui libère correctement la mémoire allouée
    • Un opérateur d'affectation par copie qui effectue une copie profonde avec une protection contre l'auto-affectation
    • Un opérateur d'affectation par déplacement qui transfère la propriété de manière efficace
    • Une méthode c_str() qui retourne la chaîne interne (const)
    • Une méthode length() qui retourne la longueur de la chaîne (const)

    Votre opérateur d'affectation par copie doit suivre les quatre étapes essentielles : vérifier l'auto-affectation, nettoyer les ressources existantes, copier les nouvelles données et retourner *this. L'opérateur d'affectation par déplacement doit "voler" le pointeur de la source et laisser la source dans un état vide valide (nullptr avec une longueur de 0).

  • main.cpp : Lisez trois chaînes de caractères à partir de l'entrée (chacune sur une ligne séparée). Créez trois objets DynamicString : str1 avec la première entrée, str2 avec la deuxième entrée, et str3 avec la troisième entrée.

    Démontrez vos opérateurs d'affectation :

    1. Affichez l'état initial des trois chaînes
    2. Utilisez l'affectation par copie : str1 = str2;
    3. Affichez str1 et str2 après l'affectation par copie (les deux doivent avoir le même contenu)
    4. Utilisez l'affectation par déplacement : str2 = std::move(str3);
    5. Affichez str2 après l'affectation par déplacement, ainsi que la longueur de str3 (qui devrait être 0 après avoir été déplacée)

    Format de sortie :

    Initial:
    str1: <value>
    str2: <value>
    str3: <value>
    After copy (str1 = str2):
    str1: <value>
    str2: <value>
    After move (str2 = std::move(str3)):
    str2: <value>
    str3 length: 0

Utilisez <cstring> pour strlen et strcpy. Marquez votre opérateur d'affectation par déplacement comme noexcept. Incluez <utility> dans main.cpp pour std::move. N'oubliez pas les gardes d'inclusion (header guards) dans votre fichier d'en-tête.

Essayez vous-même

#include <iostream>
#include <string>
#include <utility>
#include "DynamicString.h"

using namespace std;

int main() {
    // Lire trois chaînes de caractères depuis l'entrée
    string input1, input2, input3;
    getline(cin, input1);
    getline(cin, input2);
    getline(cin, input3);

    // Créer trois objets DynamicString
    DynamicString str1(input1.c_str());
    DynamicString str2(input2.c_str());
    DynamicString str3(input3.c_str());

    // TODO: Afficher l'état initial des trois chaînes
    // Format:
    // Initial:
    // str1: <value>
    // str2: <value>
    // str3: <value>

    // TODO: Utiliser l'affectation par copie : str1 = str2;

    // TODO: Afficher str1 et str2 après l'affectation par copie
    // Format:
    // After copy (str1 = str2):
    // str1: <value>
    // str2: <value>

    // TODO: Utiliser l'affectation par déplacement : str2 = std::move(str3);

    // TODO: Afficher str2 après le déplacement, et la longueur de str3
    // Format:
    // After move (str2 = std::move(str3)):
    // str2: <value>
    // str3 length: 0

    return 0;
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet