Idiome Copy-and-Swap en C++ : guide 2025 avec exemples

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 l'idiome Copy-and-Swap et pourquoi est-ce un must-have en 2025



Imaginez que vous écrivez une classe pour gérer la mémoire dynamique. Tout va bien, jusqu'à ce que vous arriviez à la surcharge de l'opérateur d'affectation. Vous écrivez operator=, vous vérifiez l'auto-affectation, vous libérez manuellement les anciennes ressources, vous en allouez de nouvelles… Et dans 90 % des cas, vous introduisez un bug qui entraînera une fuite mémoire ou une double suppression. Cela vous dit quelque chose ?



En C++, il existe une solution élégante — l'idiome copy-and-swap. Ce n'est pas simplement une astuce à la mode, mais une technique fondamentale qui permet d'écrire du code sûr, résistant aux exceptions et concis. En 2025, alors que le C++23 est déjà activement utilisé et que la norme C++26 se profile à l'horizon, cet idiome reste l'étalon-or pour les classes qui gèrent des ressources.



Dans cet article, nous allons décortiquer l'idiome copy-and-swap de A à Z : en commençant par le problème qu'il résout, jusqu'aux exemples pratiques et aux meilleures pratiques de 2025. Préparez votre compilateur — il y aura beaucoup de code !



Le problème : pourquoi un operator= naïf est un mal



Commençons par l'exemple classique d'une classe qui gère un tableau dynamique. Sans l'idiome copy-and-swap, le code ressemble souvent à ceci :



class DumbArray {public:    DumbArray(size_t size)         : size_(size), data_(new int[size]) {}        ~DumbArray() { delete[] data_; }        // Opérateur d'affectation naïf    DumbArray& operator=(const DumbArray& other) {        if (this == &other) return *this; // vérification d'auto-affectation        delete[] data_;                    // libération des anciennes ressources        size_ = other.size_;        data_ = new int[size_];           // allocation de nouvelles        std::copy(other.data_, other.data_ + size_, data_);        return *this;    }    private:    size_t size_;    int* data_;};


À première vue, tout semble logique. Mais il y a trois problèmes :

  • Absence de garantie de base contre les exceptions. Si new int[size_] lève std::bad_alloc, l'objet reste dans un état incorrect : data_ pointera vers une mémoire supprimée.
  • Duplication de la logique. La copie des données est déjà implémentée dans le constructeur de copie. Pourquoi écrivons-nous la même chose manuellement ?
  • Vérification d'auto-affectation. Elle ralentit le code et est facilement oubliée. De plus, dans 95 % des cas, l'auto-affectation ne se produit pas, mais la vérification est quand même effectuée.


Et ce n'est pas tout. Si vous ajoutez d'autres ressources à la classe (par exemple, des descripteurs de fichiers ou des mutex), le code deviendra non seulement volumineux, mais aussi dangereux. C'est là que l'idiome copy-and-swap entre en scène.



L'essence de l'idiome : trois étapes vers un operator= idéal



L'idiome copy-and-swap repose sur une idée simple : n'écrivez pas la logique de copie dans l'opérateur d'affectation — utilisez le constructeur de copie et la fonction swap. L'algorithme est le suivant :



  1. Copy : créez une copie temporaire de l'objet à l'aide du constructeur de copie.
  2. Swap : échangez le contenu de l'objet courant avec la copie temporaire.
  3. Destroy : l'objet temporaire est détruit, libérant les anciennes ressources.


En code, cela donne :



class SmartArray {public:    SmartArray(size_t size = 0)         : size_(size), data_(size ? new int[size] : nullptr) {}        // Constructeur de copie    SmartArray(const SmartArray& other)         : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) {        std::copy(other.data_, other.data_ + size_, data_);    }        // Destructeur    ~SmartArray() { delete[] data_; }        // Fonction swap amie    friend void swap(SmartArray& first, SmartArray& second) noexcept {        using std::swap;        swap(first.size_, second.siz                

Blogs

Book Recommendations