مؤشر ذكي
في علوم الحاسوب ، المؤشر الذكي هو نوع بيانات مجرد يحاكي المؤشر مع توفير ميزات إضافية، مثل إدارة الذاكرة التلقائية أو التحقق من الحدود . تهدف هذه الميزات إلى تقليل الأخطاء الناتجة عن سوء استخدام المؤشرات، مع الحفاظ على الكفاءة. عادةً ما تتتبع المؤشرات الذكية الذاكرة التي تشير إليها، ويمكن استخدامها أيضًا لإدارة موارد أخرى، مثل اتصالات الشبكة ومقابض الملفات . شاع استخدام المؤشرات الذكية لأول مرة في لغة البرمجة C++ خلال النصف الأول من التسعينيات كرد على الانتقادات الموجهة إلى C++ لافتقارها إلى خاصية جمع البيانات المهملة التلقائي . [ 1 ] [ 2 ] لغة Rust ، التي تتجنب المؤشرات الخام وتستخدم الملكية لتحديد دورات حياة المتغيرات، تحتوي أيضًا على مؤشرات ذكية.
يُعدّ سوء استخدام المؤشرات مصدرًا رئيسيًا للأخطاء البرمجية. تمنع المؤشرات الذكية معظم حالات تسرب الذاكرة بجعل تحرير الذاكرة تلقائيًا. وبشكلٍ عام، تجعل المؤشرات الذكية تدمير الكائنات تلقائيًا: يُدمّر الكائن الذي يتحكم به مؤشر ذكي تلقائيًا ( يُنهى ثم يُحرر) عند تدمير آخر (أو المالك الوحيد) للكائن، على سبيل المثال لأن المالك متغير محلي ، ويخرج التنفيذ من نطاق المتغير . كما تُزيل المؤشرات الذكية المؤشرات المعلقة بتأجيل التدمير حتى يصبح الكائن غير مستخدم.
إذا كانت لغة البرمجة تدعم جمع البيانات المهملة تلقائيًا (مثل جافا أو سي شارب )، فإن المؤشرات الذكية تصبح غير ضرورية لاستعادة الذاكرة وجوانب الأمان في إدارة الذاكرة، ولكنها مفيدة لأغراض أخرى، مثل إدارة سكن بنية بيانات ذاكرة التخزين المؤقت وإدارة موارد الكائنات مثل مقابض الملفات أو مقابس الشبكة . وعلى الرغم من أن جافا لا تحتوي على واجهة برمجة تطبيقات للمؤشرات، إلا أنها تمتلك مفاهيم مشابهة للتعامل مع المراجع.
توجد أنواع عديدة من المؤشرات الذكية. بعضها يعمل عن طريق عد المراجع ، والبعض الآخر عن طريق إسناد ملكية كائن ما إلى مؤشر واحد.
تاريخ
على الرغم من أن لغة C++ قد ساهمت في انتشار مفهوم المؤشرات الذكية، وخاصةً النوع الذي يعتمد على عدّ المراجع ، [ 3 ] إلا أن اللغة السابقة مباشرةً لإحدى اللغات التي استُلهم منها تصميم C++ كانت تتضمن مراجعًا مُدمجة تعتمد على عدّ المراجع. استُلهمت C++ جزئيًا من لغة Simula67 . [ 4 ] وكانت Simula I هي سلف Simula67 . وبما أن عنصر Simula I يُشابه مؤشر C++ بدون قيمة فارغة ، وبما أن عملية Simula I التي تحتوي على عبارة وهمية كجسم نشاط تُشابه بنية C++ (التي تُشابه بدورها سجل CAR Hoare في أعمال الستينيات المعاصرة آنذاك)، فقد احتوت Simula I على عناصر تعتمد على عدّ المراجع (أي تعبيرات مؤشرية تحتوي على توجيه غير مباشر) للعمليات (أي السجلات) في موعد أقصاه سبتمبر 1965، كما هو موضح في الفقرات المقتبسة أدناه. [ 5 ]
يمكن الإشارة إلى العمليات بشكل فردي. من الناحية الفيزيائية، تُعدّ الإشارة إلى العملية مؤشرًا إلى منطقة في الذاكرة تحتوي على البيانات المحلية للعملية، بالإضافة إلى بعض المعلومات الإضافية التي تُحدد حالة تنفيذها الحالية. مع ذلك، ولأسباب مذكورة في القسم 2.2، تكون الإشارات إلى العمليات غير مباشرة دائمًا، من خلال عناصر تُسمى عناصر. رسميًا، تُعرّف الإشارة إلى عملية بأنها قيمة تعبير من نوع عنصر . ... يمكن تخزين قيم العناصر واسترجاعها عن طريق عمليات الإسناد والإشارة إلى متغيرات العناصر، وبوسائل أخرى. تحتوي اللغة على آلية لجعل سمات العملية قابلة للوصول من الخارج، أي من داخل عمليات أخرى. يُسمى هذا الوصول عن بُعد. وبالتالي، تُعتبر العملية بنية بيانات قابلة للإشارة.
يجدر ملاحظة التشابه بين عملية يكون جسم نشاطها عبارة عن بيان وهمي، ومفهوم السجل الذي اقترحه مؤخرًا كل من كار هوار ون. ويرث
لأن لغة C++ استعارت نهج Simula لتخصيص الذاكرة - الكلمة المفتاحية الجديدة عند تخصيص عملية/سجل للحصول على عنصر جديد لتلك العملية/السجل - فليس من المستغرب أن C++ أعادت في النهاية إحياء آلية المؤشر الذكي المعتمدة على عد المراجع الخاصة بـ Simula داخل العنصر أيضًا.
سمات
في لغة C++ ، يتم تنفيذ المؤشر الذكي كفئة قالب تحاكي، عن طريق تحميل المعاملات الزائدة ، سلوكيات المؤشر التقليدي (الخام) ، (مثل إلغاء المرجعية، والتعيين) مع توفير ميزات إضافية لإدارة الذاكرة.
تُسهّل المؤشرات الذكية البرمجة المقصودة من خلال التعبير، ضمن نوعها، عن كيفية إدارة ذاكرة الكائن المُشار إليه بالمؤشر. على سبيل المثال، إذا أعادت دالة في لغة C++ مؤشرًا، فلا توجد طريقة لمعرفة ما إذا كان على المُستدعي حذف ذاكرة الكائن المُشار إليه عند انتهاء استخدامه للمعلومات.
X * ambiguousFunction (); // ما الذي يجب فعله بالنتيجة؟تقليديًا، استُخدمت اصطلاحات التسمية لحل الغموض، [ 6 ] وهو أسلوب مُعرّض للأخطاء ويتطلب جهدًا كبيرًا. قدّمت لغة C++11 طريقة لضمان إدارة الذاكرة بشكل صحيح في هذه الحالة من خلال تعريف الدالة بحيث تُرجع قيمة من نوع std::unique_ptr.
باستخدام std :: unique_ptr ؛unique_ptr < X > viousFunction ();يُوضح تعريف نوع إرجاع الدالة كـ `a` std::unique_ptrأن المُستدعي يمتلك النتيجة، ويضمن وقت تشغيل C++ استعادة الذاكرة تلقائيًا. قبل إدخال C++11 لـ `a` std::unique_ptr، std::auto_ptrكان يُستخدم الصنف `a` (الذي أُزيل لاحقًا في C++17 ).
توفر لغة C++ المؤشرات الذكية التالية:
std::auto_ptr<T>(تم إهمالها في C++11 وإزالتها في C++17) [ 7 ] ، "مؤشر تلقائي"std::unique_ptr<T, Del>[ 8 ] ، "مؤشر فريد"std::shared_ptr<T>[ 9 ] ، "مؤشر مشترك"std::weak_ptr<T>[ 10 ] ، "مؤشر ضعيف"
في C++26 ، أُضيف مؤشر جديد، عُرِّف في <hazard_pointer>: مؤشر المخاطر ( std::hazard_pointer). [ 11 ] مع ذلك، ورغم اسمه، فهو ليس مؤشرًا ذكيًا، بل أداة للتزامن. إنه مؤشر أحادي الكتابة ومتعدد القراءة، ويمكن أن يمتلكه خيط واحد على الأكثر في أي وقت. لكي يكون الكائن قابلاً للحماية من المخاطر، يجب أن يرث من std::hazard_pointer_obj_base<T, Del>. كان مؤشر المخاطر موجودًا سابقًا في بعض مكتبات الطرف الثالث. [ 12 ]
إنشاء كائنات جديدة
لتسهيل عملية تخصيص a std::shared_ptr<T>، قدمت لغة C++11 دالة std::make_shared<T>()المصنع :
باستخدام std :: shared_ptr ؛shared_ptr < X > s = std :: make_shared < X > ( /* معلمات المُنشئ هنا */ );وبالمثل std::unique_ptr<T>، قدمت لغة C++14std::make_unique<T>() دالة المصنع:
باستخدام std :: unique_ptr ؛unique_ptr < X > u = std :: make_unique < X > ( /* معلمات المُنشئ هنا */ );يفضل في جميع الظروف تقريبًا استخدام وظائف المصنع هذه بدلاً من الكلمة newالمفتاحية [ 13 ] ، ومع ذلك، فإن هذا ليس ممكنًا دائمًا.
يجب استدعاء std::unique_ptr<T, Del>المُنشئ يدويًا لإضافة منطق حذف مخصص، حيث std::unique_ptr<T, Del>لا يمكن تعيين Delمعلمة القالب (التي تخزن نوع الحذف المخصص) من خلال std::make_unique<T>()).
باستخدام std :: FILE ؛ باستخدام std :: unique_ptr ؛unique_ptr < FILE , decltype ( & std :: fclose ) > filePtr ( std :: fopen ( "data.txt" , "r" ), & std :: fclose );علاوة على ذلك، لاستدعاء المُنشئات الخاصة/المحمية، يجب إنشاء المؤشر الذكي يدويًا، حيث std::make_unique<T>()لا std::make_shared<T>()يمكن استدعاء المُنشئات العامة إلا.
باستخدام std :: unique_ptr ؛class X { private : int n ;X ( ) = default ; public : static unique_ptr <X> create ( int n ) { return unique_ptr <X> ( new X ( n ) ) ; } } ;بالإضافة إلى ذلك، std::make_unique<T>()يتم std::make_shared<T>()تمرير المعلمات عبر الأقواس بدلاً من الأقواس المعقوفة؛ وبالتالي، لا يمكن للمرء إنشاء التجميعات std::initializer_list<T>والمنشئات إلا من خلال إنشاء مؤشر ذكي يدويًا.
باستخدام std :: unique_ptr ؛ باستخدام std :: vector ؛struct Matrix { vector < double > data ; size_t rows ; size_t cols ; };unique_ptr < Matrix > m ( new Matrix { . data = { 1.0 , 0.0 , 0.0 , 1.0 , 0.0 , 0.0 , 0.0 , 1.0 }, . rows = 3 , . cols = 3 } ) ;مؤشرات فريدة
يُقدّم C++11std::unique_ptr ، مُعرّفًا في ملف الرأس <memory>. [ 14 ]
A std::unique_ptrعبارة عن حاوية لمؤشر خام، يُقال std::unique_ptrإن الكائن يمتلكه. std::unique_ptrيمنع A صراحةً نسخ المؤشر الذي يحتويه (كما يحدث مع التعيين العادي)، ولكن std::moveيمكن استخدام الدالة لنقل ملكية المؤشر الموجود بداخله إلى كائن آخر std::unique_ptr. std::unique_ptrلا يمكن نسخ A لأن مُنشئ النسخ وعوامل التعيين الخاصة به محذوفة صراحةً.
استيراد std ؛باستخدام std :: unique_ptr ؛unique_ptr <int> p1 ( new int ( 5 ) ) ; unique_ptr <int> p2 = p1 ; // خطأ في الترجمة. unique_ptr <int> p3 = std :: move ( p1 ) ; // ينقل الملكية. p3 يمتلك الآن الذاكرة، وp1 مُعيّن إلى nullptr .p3.reset ( ) ; // يحذف الذاكرة. p1.reset ( ) ; // لا يفعل شيئًا.std::auto_ptrتم إهمال هذه الخاصية في C++11 وإزالتها تمامًا من C++17std::auto_ptr . لا يقوم مُنشئ النسخ ومعاملات الإسناد بنسخ المؤشر المخزن فعليًا، بل ينقلانه فقط ، تاركين الكائن السابق std::auto_ptrفارغًا. كانت هذه إحدى طرق تطبيق الملكية الصارمة، بحيث لا std::auto_ptrيمكن إلا لكائن واحد امتلاك المؤشر في أي وقت. هذا يعني أنه std::auto_ptrلا ينبغي استخدام هذه الخاصية حيثما تكون هناك حاجة إلى دلالات النسخ. [ 15 ] نظرًا لوجود خاصية النسخ الخاصة بها بالفعل، لم يكن من الممكن ترقيتها لتصبح مؤشرًا للنقل فقط دون الإخلال بالتوافق مع الإصدارات السابقة من التعليمات البرمجية.std::auto_ptr
المكافئ في لغة Rust للمؤشرات الفريدة هو `<include>` std::boxed::Box، الذي يمتلك ملكية فريدة لكائن مُخصص في الذاكرة الديناميكية. [ 16 ] في الإصدارات السابقة من Rust، كان هناك أيضًا ` std::ptr::Unique<include>` يُغلّف `<include>` الخام غير الفارغ *mut T. [ 17 ]
من الممكن استخدام " مؤشرات void الذكية " باستخدام std::unique_ptr، باستخدام معلمة القالب الثانية الخاصة بها Del، والتي تخزن نوع مُحرر الذاكرة (افتراضيًا std::default_delete<T>).
باستخدام std :: unique_ptr ؛struct MyDeleter { void operator ()( void * p ) const { delete static_cast < int *> ( p ); } };int main () { unique_ptr < void , MyDeleter > myPointer ( new int ( 42 ), MyDeleter ()); }المؤشرات المشتركة والمؤشرات الضعيفة
يُقدّم C++11 ميزتي std::shared_ptr`and` std::weak_ptr، المُعرّفتين في ملف الرأس <memory>`. [ 14 ]`. كما يُقدّم C++11 أيضًا ميزة std::make_shared`(` std::make_uniqueالتي تم تقديمها في C++14)` لتخصيص الذاكرة الديناميكية بأمان في نموذج RAII . [ 18 ]
A std::shared_ptrعبارة عن حاوية لمؤشر خام . وهي تحتفظ بملكية عدّ المراجع للمؤشر الذي تحتويه بالتعاون مع جميع نسخ المؤشر std::shared_ptr. سيتم تدمير الكائن المشار إليه بواسطة المؤشر الخام الموجود بداخله عندما std::shared_ptrيتم تدمير جميع نسخ المؤشر فقط.
استيراد std ؛باستخدام std :: shared_ptr ؛shared_ptr <int> p0 ( new int ( 5 )); // صحيح، يُخصص عددًا صحيحًا واحدًا ويُهيئه بالقيمة 5. shared_ptr < int [] > p1 ( new int [ 5 ]); // صحيح، يُخصص 5 أعداد صحيحة. shared_ptr < int [] > p2 = p1 ; // كلاهما يمتلكان الذاكرة الآن.p1.reset (); // لا تزال الذاكرة موجودة، بسبب p2. p2.reset ( ) ; // يحرر الذاكرة، حيث لا أحد آخر يملكها.A std::weak_ptrعبارة عن حاوية لمؤشر خام. يتم إنشاؤها كنسخة من a . لا يؤثر shared_ptrوجود أو تدمير std::weak_ptrنسخ من a على a أو نسخها الأخرى. بعد تدمير جميع نسخ a، تصبح جميع النسخ فارغة.std::shared_ptrstd::shared_ptrstd::shared_ptrstd::weak_ptr
استيراد std ؛باستخدام std :: shared_ptr ؛ باستخدام std :: weak_ptr ؛shared_ptr <int> p1 = std :: make_shared <int> ( 5 ) ; weak_ptr <int> wp1 { p1 } ; // p1 يملك الذاكرة .{ shared_ptr <int> p2 = wp1.lock ( ) ; // الآن ، يمتلك كل من p1 و p2 الذاكرة. // تم تهيئة p2 من مؤشر ضعيف، لذا يجب التحقق مما إذا كانت الذاكرة لا تزال موجودة! if ( p2 ) { useSomePointer ( p2 ); } } // تم حذف p2. الذاكرة مملوكة لـ p1.p1.reset ( ); // تحرير الذاكرة .shared_ptr <int> p3 = wp1.lock (); // تم استهلاك الذاكرة، لذا نحصل على shared_ptr فارغ. if ( p3 ) { // لن يتم تنفيذ الكود useLivePointer ( p3 ) ; }نظراً لأن التنفيذ std::shared_ptrيعتمد على عدّ المراجع ، فإن المراجع الدائرية تُشكّل مشكلة محتملة. std::shared_ptrيمكن كسر سلسلة المراجع الدائرية بتغيير الكود بحيث يكون أحد المراجع من نوع std::weak_ptr.
يمكن لعدة خيوط الوصول بأمان في وقت واحد إلى كائنات مختلفة std::shared_ptrتشير std::weak_ptrإلى نفس الكائن. [ 19 ]
يجب حماية الكائن المشار إليه بشكل منفصل لضمان سلامة الخيوط .
std::shared_ptrوهي std::weak_ptrمبنية على إصدارات تستخدمها مكتبات Boost . وقد قدمها التقرير الفني الأول للغة C++ (TR1) لأول مرة إلى المعيار، كأدوات عامة ، لكن C++11 أضاف المزيد من الوظائف، بما يتماشى مع إصدار Boost.
في لغة جافا، يُعتبر كل كائن بمثابة مرجع مشترك، وتتولى آلة جافا الافتراضية (JVM) تتبع إمكانية الوصول إلى الكائنات. أما في لغة رست، فهناك نوعان من المؤشرات المشتركة: std::rc::Rc(مؤشر يعتمد على عدّ المراجع، أحادي الخيوط) و std::sync::Arc(مؤشر يعتمد على عدّ المراجع بشكل ذري، آمن للاستخدام في بيئات متعددة الخيوط).
تحتوي لغة جافا على مفهوم مكافئ للمؤشرات الضعيفة java.lang.ref.WeakReference(التي تُنفذ مرجعًا ضعيفًا )، والذي لا يزيد من عدد مراجع الكائن، ويمكنه التحقق من أن الكائن لا يزال موجودًا باستخدام ` get(). وبالمثل، تحتوي جافا على java.lang.ref.PhantomReference`(التي تُنفذ مرجعًا وهميًا )`، والذي يُستخدم مع ` java.lang.ref.ReferenceQueue، لإجراء عمليات التنظيف بعد جمع الكائن بواسطة جامع البيانات المهملة، ولكن قبل الإنهاء . على عكس java.lang.ref.WeakReference`)، java.lang.ref.PhantomReferenceلا يمكن لـ ` الوصول إلى الكائن فعليًا. java.lang.ref.SoftReferenceمن ناحية أخرى، يتم مسح `، وفقًا لتقدير جامع البيانات المهملة. تحتوي لغة رست على ``` std::rc::Weakلمرجع ضعيف غير مالك يتم إنشاؤه من ``` std::rc::Rcأو std::sync::Arcباستخدام downgrade()`. [ 20 ]`
أنواع أخرى من المؤشرات الذكية
توجد أنواع أخرى من المؤشرات الذكية (غير الموجودة في معيار C++) مُطبقة في مكتبات C++ الشائعة أو مكتبة STL المخصصة ، ومن الأمثلة على ذلك المؤشر المتطفل. [ 21 ] [ 22 ]
انظر أيضاً
مراجع
- ↑ كلاين، مارشال (سبتمبر 1997). "أقسام C++ FAQs Lite حول المؤشرات الذكية ذات عدّ المراجع ودلالات المراجع عند الكتابة في الأسئلة الشائعة لإدارة freestore" . cis.usouthal.edu . تم الاسترجاع في 2018-04-06 .
- ↑ كولفين، غريغوري (1994). "اقتراح لتوحيد دالة counted_ptr في مكتبة C++ القياسية" (ملف PDF) . open-std.org . تاريخ الاسترجاع: 2018-04-06 .
- ↑ كلابنيك، ستيف؛ نيكولز، كارول (2023) [2018]. "15. المؤشرات الذكية". لغة برمجة رست ( الطبعة الثانية). سان فرانسيسكو، كاليفورنيا، الولايات المتحدة الأمريكية: دار نشر نو ستارش. الصفحات 315-351 . ISBN 978-1-7185-0310-6.(xxix+1+527+3 صفحات)
- ↑ ستروستروب، بيارن. "تاريخ لغة C++: 1979-1991" (ملف PDF) . تم الاطلاع عليه بتاريخ 2018-04-06 .
- ^ دال، أولي-جوهان؛ نيجارد ، كريستين (سبتمبر 1966). "SIMULA — لغة محاكاة تعتمد على ALGOL" (PDF) . Folk.uio.no . تم الاسترجاع 2018-04-06 .
- ↑ "دليل تاليجنت لتصميم البرامج، القسم: استخدام أسماء خاصة لنسخ وإنشاء واعتماد الإجراءات" .
- ↑ cppreference.com. "std::auto_ptr" . cppreference.com . cppreference.com . تم الاطلاع عليه بتاريخ 11 يوليو 2026 .
- ↑ cppreference.com. "std::unique_ptr" . cppreference.com . cppreference.com . تم الاطلاع عليه بتاريخ 11 يوليو 2026 .
- ↑ cppreference.com. "std::shared_ptr" . cppreference.com . cppreference.com . تم الاطلاع عليه بتاريخ 11 يوليو 2026 .
- ↑ cppreference.com. "std::weak_ptr" . cppreference.com . cppreference.com . تم الاطلاع عليه بتاريخ 11 يوليو 2026 .
- ↑ cppreference.com. "رأس المكتبة القياسية <hazard_pointer>" . cppreference.com . cppreference.com . تم الاطلاع عليه بتاريخ 11 يوليو 2026 .
- ↑ "folly/Hazptr.h at main · facebook/folly" . github.com .
- ↑ ساتر، هيرب (20 أبريل 2013). "تقرير رحلة: اجتماع ISO C++ ربيع 2013" . isocpp.org . تم الاطلاع عليه بتاريخ 14 يونيو 2013 .
- 1 2 ISO 14882:2011 20.7.1
- ↑ معيار CERT للبرمجة الآمنة بلغة C++
- ↑ "Box in std::boxed" . doc.rust-lang.org . 2025-08-04.
- ↑ "std::ptr::Unique" . web.mit.edu . 2025-09-11.
- ↑ ISO 14882:2014 20.7.1
- ↑ "boost::shared_ptr أمان الخيوط" .(ملاحظة: لا يغطي هذا بشكل رسمي std::shared_ptr، ولكن يُعتقد أن له نفس قيود الترابط.)
- ↑ "ضعيف في std::rc" . doc.rust-lang.org . 2025-08-04.
- ↑ "Boost.SmartPtr: مكتبة المؤشر الذكي - 1.81.0" . boost.org .
- ↑ "EASTL/intrusive_ptr.h at master · electronicarts/EASTL" . github.com .
للمزيد من القراءة
- مايرز، سكوت (2014). لغة سي++ الحديثة الفعّالة . سيباستوبول، كاليفورنيا، الولايات المتحدة الأمريكية: أورايلي ميديا . ISBN 978-1-49190399-5. OCLC 884480640 .
- ألكسندريسكو، أندريه (2001). "المؤشرات الذكية" . تصميم لغة C++ الحديثة - البرمجة العامة وتطبيقات أنماط التصميم . أديسون-ويسلي .
- ساتر، هيرب (2002-08-01). "لغة سي++ الجديدة: مؤشرات (أكثر ذكاءً)" .
- المؤشرات الذكية - ما هي، ولماذا، وأيها؟ . يونات شارون
- نظرة عامة على المؤشرات الذكية . جون إم. ديلوجوز
روابط خارجية
- أنواع البيانات
- المؤشرات (برمجة الحاسوب)
