قبل C++11، كل ذاكرة تُحجز بـ new يجب تحريرها يدويًا بـ delete — ونسيان
ذلك أشهر مصدر أخطاء اللغة تاريخيًا. المؤشّرات الذكية حلّت هذه المشكلة
جذريًا.
المشكلة الأصلية
void riskyFunction() {
int* data = new int[1000];
if (someCondition()) {
return; // ❌ تسريب ذاكرة! نسينا delete[] هنا
}
delete[] data;
}
كل نقطة خروج ممكنة من الدالة (return مبكّر، استثناء) تحتاج تذكّر delete —
عرضة للنسيان بمشاريع كبيرة، ومصدر تسريبات الذاكرة الشهيرة بـ C++.
unique_ptr — الملكية الحصرية
المؤشّر الذكي الافتراضي والأكثر استخدامًا. كائن واحد فقط يملك المورد، ويُحرَّر تلقائيًا عند خروجه من نطاقه:
#include <memory>
void safeFunction() {
std::unique_ptr<int[]> data = std::make_unique<int[]>(1000);
if (someCondition()) {
return; // ✅ يتحرّر تلقائيًا هنا، بلا delete يدوي
}
} // ✅ يتحرّر هنا أيضًا مهما حدث
لا يمكن نسخ unique_ptr — الملكية حصرية دائمًا:
std::unique_ptr<int> a = std::make_unique<int>(5);
// std::unique_ptr<int> b = a; // ❌ خطأ ترجمة متعمَّد
std::unique_ptr<int> b = std::move(a); // ✅ نقل الملكية مسموح
// a أصبح nullptr الآن
shared_ptr — الملكية المشتركة
لما يحتاج أكثر من جزء بالبرنامج امتلاك نفس الكائن معًا، عبر عدّاد مراجع داخلي يحرّر الذاكرة عند وصوله للصفر:
std::shared_ptr<int> a = std::make_shared<int>(42);
std::cout << a.use_count(); // 1
{
std::shared_ptr<int> b = a; // نسخ مسموح، العدّاد = 2
} // b يخرج من النطاق، العدّاد يعود = 1
weak_ptr — كسر دورات المراجع
مشكلة shared_ptr: لو كائنان يشير كل منهما للآخر، عدّاد المراجع لن يصل
للصفر أبدًا — تسريب صامت. weak_ptr يراقب الكائن دون زيادة العدّاد:
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // weak يكسر الدورة
};
جدول المقارنة السريع
| النوع | الملكية | التكلفة | يُستخدم متى |
|---|---|---|---|
unique_ptr | حصرية | شبه معدومة | افتراضيًا دائمًا |
shared_ptr | مشتركة | عدّاد مراجع | ملكية مشتركة فعلية |
weak_ptr | مراقبة فقط | شبه معدومة | كسر دورات المراجع |
مؤشّر خام (new/delete) | يدوية بالكامل | — | نادرًا، بكود حديث |
القاعدة العملية
ابدأ دائمًا بـ unique_ptr. انتقل لـ shared_ptr فقط لو اكتشفت فعليًا أن أكثر
من جزء بالكود يحتاج امتلاك نفس الكائن بنفس الوقت. تجنّب new/delete
المباشرة تمامًا في كود C++ حديث إلا لحالات نادرة جدًا.
تعمّق أكثر بدرس المؤشّرات الذكية الكامل أو اقرأ عن مبدأ RAII الذي يقوم عليه كل هذا.