إيدوم النسخ والتبديل (Copy-and-Swap Idiom) في لغة C++: دليل 2025 مع أمثلة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

ما هو إيدوم النسخ والتبديل ولماذا هو أمر لا غنى عنه في عام 2025



تخيل أنك تكتب كلاسًا للعمل مع الذاكرة الديناميكية. كل شيء يسير على ما يرام، حتى تصل إلى تحميل عامل الإسناد. تكتب operator=، وتتحقق من الإسناد الذاتي، وتحرر الموارد القديمة يدويًا، وتخصص موارد جديدة... وفي 90% من الحالات ترتكب خطأ يؤدي إلى تسرب للذاكرة أو حذف مزدوج. هل هذا مألوف؟



في لغة C++ يوجد حل أنيق — إيدوم النسخ والتبديل (copy-and-swap idiom). هذه ليست مجرد خدعة عصرية، بل هي تقنية أساسية تسمح بكتابة كود آمن ومقاوم للاستثناءات وموجز. في عام 2025، عندما يكون معيار C++23 قيد الاستخدام النشط ومعيار C++26 في الأفق، يظل هذا الإيدوم هو المعيار الذهبي للكلاسات التي تدير الموارد.



في هذه المقالة، سنقوم بتحليل إيدوم النسخ والتبديل بالكامل: بدءًا من المشكلة التي يحلها، وصولاً إلى الأمثلة العملية وأفضل الممارسات لعام 2025. جهز المترجم الخاص بك — سيكون هناك الكثير من الكود!



المشكلة: لماذا عامل الإسناد الساذج هو شر



لنبدأ بمثال تقليدي لكلاس يدير مصفوفة ديناميكية. بدون إيدوم النسخ والتبديل، غالبًا ما يبدو الكود هكذا:



class DumbArray {public:    DumbArray(size_t size)         : size_(size), data_(new int[size]) {}        ~DumbArray() { delete[] data_; }        // عامل الإسناد الساذج    DumbArray& operator=(const DumbArray& other) {        if (this == &other) return *this; // التحقق من الإسناد الذاتي        delete[] data_;                    // تحرير الموارد القديمة        size_ = other.size_;        data_ = new int[size_];           // تخصيص موارد جديدة        std::copy(other.data_, other.data_ + size_, data_);        return *this;    }    private:    size_t size_;    int* data_;};


للوهلة الأولى، كل شيء منطقي. لكن هناك ثلاث مشاكل:

  • غياب الضمان الأساسي للاستثناءات. إذا ألقى new int[size_] استثناء std::bad_alloc، سيظل الكائن في حالة غير صحيحة: data_ سيشير إلى ذاكرة محذوفة.
  • تكرار المنطق. نسخ البيانات قد تم بالفعل في منشئ النسخ. لماذا نكتب نفس الشيء يدويًا؟
  • التحقق من الإسناد الذاتي. يبطئ الكود ويسهل نسيانه. علاوة على ذلك، في 95% من الحالات لا يحدث إسناد ذاتي، ولكن التحقق يتم تنفيذه على أي حال.


وهذا ليس كل شيء. إذا أضفت موارد أخرى إلى الكلاس (مثل واصفات الملفات أو كائنات المزامنة)، سيصبح الكود ليس فقط ضخمًا — بل سيصبح خطيرًا. وهنا يأتي دور إيدوم النسخ والتبديل.



جوهر الإيدوم: ثلاث خطوات نحو عامل إسناد مثالي



يعتمد إيدوم النسخ والتبديل على فكرة بسيطة: لا تكتب منطق النسخ في عامل الإسناد — استخدم منشئ النسخ ودالة swap. تبدو الخوارزمية كالتالي:



  1. النسخ: نقوم بإنشاء نسخة مؤقتة من الكائن باستخدام منشئ النسخ.
  2. التبديل: نتبادل محتويات الكائن الحالي مع النسخة المؤقتة.
  3. الإتلاف: يتم إتلاف الكائن المؤقت، مما يحرر الموارد القديمة.


في الكود، يبدو هذا هكذا:



class SmartArray {public:    SmartArray(size_t size = 0)         : size_(size), data_(size ? new int[size] : nullptr) {}        // منشئ النسخ    SmartArray(const SmartArray& other)         : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) {        std::copy(other.data_, other.data_ + size_, data_);    }        // المدمر    ~SmartArray() { delete[] data_; }        // دالة swap صديقة    friend void swap(SmartArray& first, SmartArray& second) noexcept {        using std::swap;        swap(first.size_, second.siz                

المدونات

توصيات الكتب