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.
Défi
FacileConstruisons 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 classeDynamicStringqui 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_tprivé 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).- Un pointeur
main.cpp: Lisez trois chaînes depuis l'entrée (chacune sur une ligne distincte). Créez trois objetsDynamicString:str1avec la première entrée,str2avec la deuxième entrée etstr3avec la troisième entrée.Démontrez vos opérateurs d'affectation :
- Affichez l'état initial des trois chaînes
- Utilisez l'affectation par copie :
str1 = str2; - Affichez str1 et str2 après l'affectation par copie (les deux doivent avoir le même contenu)
- Utilisez l'affectation par déplacement :
str2 = std::move(str3); - 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;
}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
1Fondamentaux de la POO
Fichiers externesGénération et compilation en C++Fichiers d’en-tête et fichiers sourceEspaces de noms et portéeIntroduction à la POO en C++Classes et objetsLe pointeur « this »Méthodes (fonctions membres)Attributs (membres de données)Bases des constructeurs et destructeursRécapitulatif - Calculatrice simple4Propriétés de classe
Membres d’instance et statiquesAccesseurs et mutateursFonctions membres constMot-clé mutableMéthodes et variables statiquesFonctions et classes amiesRécapitulatif - Gestionnaire de compte bancaire7Héritage
Héritage de baseNiveaux d’accès de l’héritageOrdre d’appel des constructeurs et destructeursRedéfinition des méthodesFonctions virtuelles et VTableHéritage multipleHéritage virtuelRécapitulatif : hiérarchie des employés10Vue d’ensemble de la STL
Vue d’ensemble et philosophie de la STLConteneurs de la STLItérateursAlgorithmes de la STLFoncteurs et expressions lambdaRécapitulatif - fréquence des mots13Modèles de conception, partie 1
Introduction aux modèles de conceptionModèle SingletonFabrique et fabrique abstraiteModèle BuilderModèle ObserverModèle Strategy2Gestion de la mémoire
Mémoire de pile ou de tasPointeurs et référencesMémoire dynamique (new/delete)Pointeurs intelligents en C++RAII en C++Récapitulatif - Gestionnaire de tableaux dynamiques5Encapsulation
Spécificateurs d’accès en C++Spécificateurs d’accès en profondeurMasquage de l’informationStruct vs classeClasses imbriquées et internesRécapitulatif - Système de gestion des dossiers étudiants8Polymorphisme
Polymorphisme à la compilation vs à l’exécutionSurcharge de fonctionsRetour sur les fonctions virtuellesFonctions virtuelles puresClasses abstraitesConception d’interfaces en C++Conversion dynamique et RTTIRécapitulatif - Calculateur de formes3Constructeurs et destructeurs
Constructeur par défautConstructeur paramétréConstructeur de copieConstructeur de déplacementListes d’initialisation des constructeursConstructeurs déléguésApprofondissement des destructeursRègle des trois / cinq / zéroRécapitulatif - Classe String6Surcharge des opérateurs
Introduction à la surcharge des opérateursSurcharge des opérateurs arithmétiquesSurcharge des opérateurs de comparaisonOpérateurs de fluxSurcharge de l’opérateur d’affectationSurcharge des opérateurs [] et ()Opérateurs de conversion de typeRécapitulatif – Classe Matrix9Templates
Templates de fonctionsTemplates de classesSpécialisation des templatesTemplates variadiquesBases de SFINAE et des traits de typesRécapitulatif - Conteneur génériqueEntraînez-vous par vous-même : Compilateur C++ en ligne