المؤشرات الذكية في C++
جزء من قسم Object Oriented Programming في رحلة C++ على Coddy — الدرس 15 من 104.
المؤشرات الذكية (Smart pointers) هي فئات تغليف (wrapper classes) تدير ذاكرة الكومة (heap memory) تلقائيًا نيابة عنك. فهي تلغي الحاجة إلى استدعاءات delete اليدوية من خلال تنظيف الذاكرة عندما يخرج المؤشر عن النطاق (scope).
قم بتضمين الترويسة <memory> لاستخدام المؤشرات الذكية.
unique_ptr - الملكية الحصرية. يمكن لمؤشر واحد فقط امتلاك المورد في المرة الواحدة.
#include <memory>
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // استخدمه مثل المؤشر العادي
// لا يمكن النسخ، ولكن يمكن نقل الملكية
std::unique_ptr<Player> player2 = std::move(player);
// player هو الآن nullptrshared_ptr - ملكية مشتركة. يمكن لمؤشرات متعددة امتلاك نفس المورد. يتم تحرير الذاكرة عندما يتم تدمير آخر مالك.
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // كلاهما يمتلك المورد
std::cout << p1.use_count(); // يطبع 2weak_ptr - مراقب غير مالك لـ shared_ptr. مفيد لكسر المراجع الدائرية.
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // تحويل إلى shared_ptr
locked->attack();
}استخدم unique_ptr بشكل افتراضي للملكية الفردية. استخدم shared_ptr عندما تحتاج كائنات متعددة إلى مشاركة مورد ما. استخدم weak_ptr للمراقبة دون تمديد فترة الحياة.
يفضل استخدام make_unique و make_shared بدلاً من new الخام.
التحدي
سهللنقم ببناء نظام مشاركة مستندات يوضح نماذج الملكية المختلفة للمؤشرات الذكية. سترى كيف يفرض unique_ptr الملكية الحصرية، وكيف يسمح shared_ptr بمالكين متعددين، وكيف يمكن لـ weak_ptr المراقبة دون التأثير على دورة الحياة.
ستقوم بإنشاء ملفين لتنظيم الكود الخاص بك:
Document.h: قم بتعريف فئةDocumentالتي تمثل مستنداً قابلاً للمشاركة. يجب أن تحتوي على:- سمة
titleخاصة (string) - منشئ (constructor) يأخذ عنواناً ويطبع
"Document created: <title>" - مدمر (destructor) يطبع
"Document deleted: <title>" - دالة
getTitle()تعيد العنوان
- سمة
main.cpp: قم بتوضيح جميع أنواع المؤشرات الذكية الثلاثة من خلال قراءة عنوان مستند من المدخلات، ثم:- إنشاء
unique_ptrلمستندDocumentبالعنوان المدخل - طباعة
"Unique owner: <title>" - نقل الملكية إلى
unique_ptrآخر باستخدامstd::move - طباعة
"Ownership transferred" - إنشاء
shared_ptrلمستندDocumentجديد بالعنوان"SharedDoc" - إنشاء
shared_ptrثانٍ يشير إلى نفس المستند - طباعة
"Shared owners: <count>"باستخدامuse_count() - إنشاء
weak_ptrمن أحد المؤشرات المشتركة - استخدام
lock()للوصول إلى المستند عبر المؤشر الضعيف وطباعة"Weak access: <title>"
- إنشاء
تذكر تضمين <memory> للمؤشرات الذكية. استخدم std::make_unique و std::make_shared لإنشاء مؤشراتك الذكية. ستساعدك رسائل المدمر في رؤية متى يتم تنظيف كل مستند تلقائياً في نهاية البرنامج.
قم بتضمين ملف الرأس الخاص بك في main.cpp باستخدام #include "Document.h".
ورقة مرجعية
المؤشرات الذكية (Smart pointers) هي فئات مغلفة تدير ذاكرة الكومة (heap memory) تلقائياً. قم بتضمين الترويسة <memory> لاستخدامها.
unique_ptr
ملكية حصرية - يمكن لمؤشر واحد فقط امتلاك المورد في المرة الواحدة:
std::unique_ptr<Player> player = std::make_unique<Player>("Hero");
player->attack(); // استخدمه مثل المؤشر العادي
// نقل الملكية باستخدام std::move
std::unique_ptr<Player> player2 = std::move(player);
// player هو الآن nullptrshared_ptr
ملكية مشتركة - يمكن لعدة مؤشرات امتلاك نفس المورد. يتم تحرير الذاكرة عند تدمير آخر مالك:
std::shared_ptr<Player> p1 = std::make_shared<Player>("Hero");
std::shared_ptr<Player> p2 = p1; // كلاهما يمتلك المورد
std::cout << p1.use_count(); // يطبع 2weak_ptr
مراقب غير مالك لـ shared_ptr. مفيد لكسر المراجع الدائرية:
std::shared_ptr<Player> shared = std::make_shared<Player>("Hero");
std::weak_ptr<Player> weak = shared;
if (auto locked = weak.lock()) { // التحويل إلى shared_ptr
locked->attack();
}أفضل الممارسات: استخدم unique_ptr بشكل افتراضي للملكية الفردية. استخدم shared_ptr عندما تحتاج كائنات متعددة لمشاركة مورد ما. استخدم weak_ptr للمراقبة دون تمديد فترة الحياة. فضل استخدام make_unique و make_shared على new الخام.
جرّب بنفسك
#include <iostream>
#include <memory>
#include <string>
#include "Document.h"
using namespace std;
int main() {
string inputTitle;
cin >> inputTitle;
// TODO: قم بإنشاء unique_ptr لـ Document باستخدام inputTitle
// باستخدام std::make_unique
// TODO: اطبع "Unique owner: <title>"
// TODO: انقل الملكية إلى unique_ptr آخر باستخدام std::move
// TODO: اطبع "Ownership transferred"
// TODO: قم بإنشاء shared_ptr لـ Document جديد بالعنوان "SharedDoc"
// باستخدام std::make_shared
// TODO: قم بإنشاء shared_ptr ثانٍ يشير إلى نفس المستند
// TODO: اطبع "Shared owners: <count>" باستخدام use_count()
// TODO: قم بإنشاء weak_ptr من أحد المؤشرات المشتركة (shared pointers)
// TODO: استخدم lock() للوصول إلى المستند من خلال weak_ptr
// واطبع "Weak access: <title>"
return 0;
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات الـ OOP
الملفات الخارجيةبناء وتجميع (Compilation) لغة C++ملفات الـ Header وملفات الـ Sourceالـ Namespaces والـ Scopeمقدمة في الـ OOP في C++الـ Classes مقابل الـ Objectsالمؤشر 'this'الـ Methods (الدوال الأعضاء)الـ Attributes (بيانات الأعضاء)أساسيات الـ Ctors والـ Dtorsمراجعة - آلة حاسبة بسيطة4خصائص الـ Class
أعضاء الـ Instance مقابل الـ Staticدوال الـ Getters والـ Settersدوال الأعضاء الثابتة (Const)الكلمة المفتاحية Mutableالدوال والمتغيرات الساكنة (Static)الدوال والفئات الصديقة (Friend)مراجعة - مدير الحساب البنكي7الوراثة
الوراثة الأساسيةمستويات الوصول في الوراثةترتيب استدعاء الـ Ctor والـ Dtorإعادة تعريف الدوال (Method Overriding)الدوال الافتراضية والـ VTableالوراثة المتعددةالوراثة الافتراضيةمراجعة - هيكلية الموظفين10نظرة عامة على STL
نظرة عامة وفلسفة STLحاويات STLالـ Iteratorsخوارزميات STLالـ Functors وتعبيرات Lambdaمراجعة - تكرار الكلمات13أنماط التصميم - الجزء الأول
مقدمة في أنماط التصميمنمط Singletonنمط Factory و Abstract Factoryنمط Builderنمط Observerنمط Strategy2إدارة الذاكرة
ذاكرة Stack مقابل Heapالمؤشرات والمراجعالذاكرة الديناميكية (new/delete)المؤشرات الذكية في C++RAII في C++مراجعة - مدير المصفوفات الديناميكية5التغليف (Encapsulation)
محددات الوصول في C++محددات الوصول بشكل متعمقإخفاء المعلوماتStruct مقابل Classالأصناف المتداخلة والداخليةمراجعة - نظام سجلات الطلاب8تعدد الأشكال (Polymorphism)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالتحميل الزائد للدوال (Function Overloading)مراجعة الدوال الافتراضية (Virtual Functions)الدوال الافتراضية البحتة (Pure Virtual Functions)الأصناف المجردة (Abstract Classes)تصميم الواجهات (Interface) في C++التحويل الديناميكي (Dynamic Casting) و RTTIملخص - حاسبة الأشكال11مفاهيم OOP المتقدمة
التركيب مقابل الوراثةالـ Mixins عبر CRTPنمط Pimplمحو النوع (Type Erasure)أصناف Enum والأنواع القويةمعالجة الاستثناءات في OOPتسلسلات الاستثناءات المخصصة14أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط قالب الطريقةنمط الحالةنمط التركيبRAII كنمط3المنشئات والموادم
المنشئ الافتراضيالمنشئ ذو المعاملاتمنشئ النسخمنشئ النقلقوائم تهيئة المنشئالمنشئات المفوضةتعمق في الموادمقاعدة الثلاثة / الخمسة / الصفرمراجعة - فئة String6تحميل العوامل (Operator Overloading)
مقدمة في تحميل العواملتحميل العوامل الحسابيةتحميل عوامل المقارنةعوامل Streamتحميل عامل التعيينتحميل العوامل [] و ()عوامل تحويل النوعمراجعة - Matrix Class9القوالب
قوالب الدوالقوالب الأصنافتخصيص القوالبالقوالب المتغيرةأساسيات SFINAE و Type Traitsمراجعة - الحاويات العامة