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 prend correctement en charge l'affectation entre des objets existants. Il s'agit d'un scénario pratique dans lequel vous devez transférer des données de chaîne d'un objet à un autre en toute sécurité, sans fuites mémoire ni pointeurs pendants.

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 à l'aide de mémoire dynamique. Votre classe doit gérer :
    • Un pointeur char* privé pour les données de la chaîne
    • Un size_t privé pour la longueur
    • Un constructeur qui accepte un const char* et crée une copie complète
    • Un destructeur qui libère correctement la mémoire allouée
    • Un opérateur d'affectation par copie qui effectue une copie complète avec protection contre l'auto-affectation
    • Un opérateur d'affectation par déplacement qui transfère efficacement la propriété
    • Une méthode c_str() qui renvoie la chaîne interne (const)
    • Une méthode length() qui renvoie 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, libérer les ressources existantes, copier les nouvelles données et renvoyer *this. L'opérateur d'affectation par déplacement doit s'emparer du 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 depuis l'entrée (chacune sur une ligne distincte). 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 (elle doit être égale à 0 après le déplacement)

    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 avec noexcept. Incluez <utility> dans main.cpp pour std::move. N'oubliez pas les protections contre les inclusions multiples 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

Entraînez-vous par vous-même : Compilateur C++ en ligne