تخطَّ إلى المحتوى

شرح C++

المؤشّرات الذكية (Smart Pointers)

الدرس 26 من 31· ⏱ 2 دقائق قراءة

مشكلة new/delete اليدوية

تعلّمت سابقًا new وdelete لإدارة الذاكرة الديناميكية — لكنها تحمّلك مسؤولية كاملة: نسيان delete يسبب تسريب ذاكرة، واستخدام مؤشّر بعد حذفه يسبب انهيارًا:

void risky() {
    int* p = new int(42);
    if (someCondition()) return; // ❌ تسريب! نسينا delete هنا
    delete p;
}

المؤشّرات الذكية تحل هذا بتطبيق مبدأ RAII: الذاكرة تتحرر تلقائيًا عند خروج الكائن من نطاقه، حتى لو حدث استثناء أو return مبكّر.

std::unique_ptr — الملكية الحصرية

الخيار الافتراضي لإدارة أي ذاكرة ديناميكية. كائن واحد فقط يملك المورد:

#include <memory>

std::unique_ptr<int> p = std::make_unique<int>(42);
cout << *p; // 42

// std::unique_ptr<int> p2 = p; // ❌ خطأ ترجمة — النسخ ممنوع
std::unique_ptr<int> p2 = std::move(p); // ✅ نقل الملكية
// p الآن nullptr، p2 يملك القيمة

} // p2 يتحرّر تلقائيًا هنا — بلا delete يدوي

مع أصنافك الخاصة:

class Report {
    string title;
public:
    Report(string t) : title(t) { cout << "أُنشئ: " << title << "\n"; }
    ~Report() { cout << "تحرّر: " << title << "\n"; }
};

void generate() {
    auto r = std::make_unique<Report>("تقرير 2026");
} // "تحرّر: تقرير 2026" يُطبع تلقائيًا هنا

std::shared_ptr — الملكية المشتركة

لما يحتاج أكثر من جزء بالبرنامج امتلاك نفس الكائن معًا. يستخدم عدّاد مراجع داخليًا:

std::shared_ptr<int> a = std::make_shared<int>(42);
cout << a.use_count(); // 1

{
    std::shared_ptr<int> b = a; // نسخ مسموح — العدّاد يصبح 2
    cout << a.use_count(); // 2
} // b يخرج من النطاق، العدّاد يعود لـ 1

cout << a.use_count(); // 1
// الذاكرة تتحرّر تلقائيًا لما يصل العدّاد للصفر

std::weak_ptr — كسر دورة المراجع

لو كائنان يشير كل منهما للآخر بـ shared_ptr، عدّاد المراجع لن يصل للصفر أبدًا (تسريب صامت). weak_ptr يراقب الكائن دون زيادة العدّاد:

struct Node {
    shared_ptr<Node> next;
    weak_ptr<Node> prev; // weak لكسر الدورة
};

auto a = make_shared<Node>();
auto b = make_shared<Node>();
a->next = b;
b->prev = a; // لا يزيد عدّاد a

if (auto p = b->prev.lock()) { // lock() يُرجع shared_ptr مؤقّت
    cout << "الأب ما زال موجودًا";
}

جدول المقارنة

النوعالملكيةيُستخدم متى
unique_ptrحصرية (مالك واحد)الخيار الافتراضي دائمًا
shared_ptrمشتركة (عدّاد مراجع)عدة أجزاء تملك نفس الكائن فعليًا
weak_ptrمراقبة بلا ملكيةكسر دورات المراجع

أخطاء شائعة

  • استخدام shared_ptr بكل مكان افتراضيًا بدل unique_ptr — تكلفة أداء غير ضرورية.
  • إنشاء shared_ptr من نفس المؤشّر الخام مرتين بشكل منفصل — يسبب حذفًا مزدوجًا.
  • استخدام get() لحذف المؤشّر يدويًا — يتعارض مع التحرير التلقائي ويسبب انهيارًا.

🎯 التالي: تعبيرات لامدا — دوال مجهولة الاسم تكتبها inline مباشرة.

شرح المؤشّرات الذكية (Smart Pointers) — C++ بالعربي
المؤشّرات الذكية (Smart Pointers)C++ بالعربي · The Code Fix

📚 لمزيد من التعمّق في C++، راجِع مرجع C++ الرسمي.

هل كان هذا الدرس مفيدًا؟