c++ - بالعربي - معنى كلمة كلير بالانجليزي




ما هو المؤشر الذكي ومتى يجب أن أستخدمه؟ (10)

ما هو المؤشر الذكي ومتى يجب أن أستخدمه؟


"المؤشرات الذكية" هي تلك التي لا داعي للقلق بشأن "إلغاء تخصيص الذاكرة" و "مشاركة الموارد" و "النقل".

يمكنك استخدام هذا المؤشر بشكل جيد بنفس الطريقة التي يعمل بها أي تخصيص في Java. في java Garbage Collector يقوم بالخدعة ، بينما في Smart Pointers ، تتم الحيلة بواسطة Destructors.


أود أن أضيف نقطة أخرى إلى السؤال أعلاه ، فإن std: std :: sharing_ptr الذكي لا يحتوي على مشبِّل منخفض ولا يدعم حسابي ponter ، يمكننا استخدام get () للحصول على مؤشر مدمج.


الإجابات الحالية جيدة ولكن لا تغطي ما يجب فعله عندما لا يكون المؤشر الذكي هو الإجابة (الكاملة) للمشكلة التي تحاول حلها.

من بين أشياء أخرى (موضحة بشكل جيد في إجابات أخرى) باستخدام مؤشر ذكي هو حل ممكن لكيفية استخدام فئة مجردة كنوع إرجاع الدالة؟ والتي تم وضع علامة عليها كمكررة لهذا السؤال. ومع ذلك ، فإن السؤال الأول ما إذا كان يميل إلى تحديد فئة أساسية (أو في الواقع ، أي) الأساسية كنوع الإرجاع في C ++ هو "ماذا تعني حقا؟". هناك مناقشة جيدة (مع مزيد من المراجع) من البرامج الموجهة للكائنات الاصطلاحية في C ++ (وكيف يختلف هذا عن اللغات الأخرى) في توثيق مكتبة حاوية مؤشر التعزيز . باختصار ، في C ++ عليك أن تفكر في الملكية. ما هي المؤشرات الذكية التي تساعدك في حل المشكلة ، ولكنها ليست الحل الوحيد ، أو دائمًا حل كامل (لا يعطيك نسخة متعددة الأشكال) وليست دائمًا حلًا تريد كشفه في واجهتك (وتبدو دالة عودة مرعبة يشبه واجهة). قد يكون كافيًا لإرجاع مرجع ، على سبيل المثال. ولكن في كل هذه الحالات (مؤشر ذكي أو حاوية مؤشر أو ببساطة إرجاع مرجع) قمت بتغيير العائد من قيمة إلى نوع من المرجع . إذا كنت في حاجة للنسخ حقًا ، فقد تحتاج إلى إضافة المزيد من النمطي "idiom" أو تجاوز ما هو اصطلاحي (أو غير ذلك) OOP في C ++ لتعدد أشكال أكثر عمومية باستخدام مكتبات مثل Adobe Poly أو Boost.TypeErasure .


التعريفات المقدمة من قبل كريس ، سيرجديف وليود صحيحة. أنا أفضل تعريف أبسط على الرغم من ذلك ، فقط للحفاظ على حياتي بسيطة: مؤشر ذكي هو مجرد فئة تزيد من تحميل -> و * المشغلين. وهو ما يعني أن الكائن الخاص بك يبدو وكأنه مؤشر ، ولكن يمكنك جعله أكثر برودة ، بما في ذلك احتساب عدد auto_ptr ، التدمير التلقائي ، إلخ. shared_ptr و auto_ptr كافية في معظم الحالات ، ولكن تأتي مع مجموعة خاصة بها من الخصوصيات الصغيرة.


المؤشر الذكي هو فئة تلتف على مؤشر C ++ "خام" (أو "عاري") ، لإدارة عمر الكائن الذي يتم توجيهه. لا يوجد نوع مؤشر ذكي واحد ، ولكن كلهم ​​يحاولون تجريد المؤشر الخام بطريقة عملية.

ينبغي تفضيل المؤشرات الذكية على مؤشرات الخام. إذا كنت تشعر أنك بحاجة إلى استخدام المؤشرات (فكر أولاً إذا كنت تقوم بذلك فعلاً ) ، فستحتاج عادةً إلى استخدام مؤشر ذكي لأن ذلك يمكن أن يخفف العديد من المشاكل مع المؤشرات الأولية ، وينسى بشكل رئيسي حذف الكائن وتسرب الذاكرة.

مع المؤشرات الأولية ، يجب على المبرمج أن يدمر الكائن بشكل صريح عندما لا يعود مفيدًا.

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

يعرّف المؤشر الذكي من خلال المقارنة سياسة ما عندما يتم تدمير الكائن. لا يزال عليك إنشاء الكائن ، ولكن لا داعي للقلق بشأن تدميره.

SomeSmartPtr<MyObject> ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

تتضمن أبسط سياسة في الاستخدام نطاق كائن مؤشر المؤشر الذكي ، مثل تنفيذها بواسطة boost::scoped_ptr أو std::unique_ptr .

void f()
{
    {
       boost::scoped_ptr<MyObject> ptr(new MyObject());
       ptr->DoSomethingUseful();
    } // boost::scopted_ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.
}

لاحظ أنه لا يمكن نسخ مثيلات scoped_ptr . هذا يمنع المؤشر من حذف عدة مرات (بشكل غير صحيح). ومع ذلك ، يمكنك تمرير إشارات إليه حول الوظائف الأخرى التي تتصل بها.

تفيد المؤشرات Scoped عندما تريد ربط عمر الكائن بكتلة معينة من التعليمة البرمجية ، أو إذا قمت بتضمينها كبيانات عضو داخل كائن آخر ، أي عمر ذلك الكائن الآخر. الكائن موجود حتى يتم إنهاء كتلة التعليمة البرمجية الموجودة ، أو حتى يتم تدمير الكائن المحتوي نفسه.

تتضمن سياسة مؤشر ذكية أكثر تعقيداً مرجع العد المؤشر. هذا لا يسمح بنسخ المؤشر. عند إتلاف "المرجع" الأخير للكائن ، يتم حذف الكائن. يتم تنفيذ هذه السياسة بواسطة boost::shared_ptr و std::shared_ptr .

void f()
{
    typedef std::shared_ptr<MyObject> MyObjectPtr; // nice short alias
    MyObjectPtr p1; // Empty

    {
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
    } // p2 is destroyed, leaving one reference to the object.
} // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

تعد مؤشرات الإحالة المرجعية مفيدة جدًا عندما يكون عمر كائنك أكثر تعقيدًا ، ولا يرتبط مباشرةً بقسم معين من الكود أو بجسم آخر.

هناك عيب واحد للإشارة إلى المؤشرات المحسوبة - إمكانية إنشاء مرجع متدلي:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

الاحتمال الآخر هو إنشاء مراجع دائرية:

struct Owner {
   boost::shared_ptr<Owner> other;
};

boost::shared_ptr<Owner> p1 (new Owner());
boost::shared_ptr<Owner> p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

للتغلب على هذه المشكلة ، قام كل من Boost و C ++ 11 بتعريف weak_ptr لتحديد مرجع ضعيف (غير محسوب) إلى shared_ptr .

تحديث

هذه الإجابة قديمة نوعًا ما ، وتصف ما كان "جيدًا" في ذلك الوقت ، وهو عبارة عن مؤشرات ذكية مقدمة من مكتبة Boost. منذ C ++ 11 ، قامت المكتبة القياسية بتوفير أنواع المؤشرات الذكية الكافية ، ولذلك يجب أن تفضل استخدام std::unique_ptr و std::shared_ptr و std::weak_ptr .

يوجد أيضا std::auto_ptr . إنه يشبه إلى حد كبير مؤشر نطاق ، إلا أنه يمتلك أيضًا القدرة الخطيرة "الخاصة" التي يمكن نسخها - والتي تنقل أيضًا الملكية بشكل غير متوقع! تم إيقافه في أحدث المعايير ، لذا لا يجب استخدامه. استخدم std::unique_ptr بدلاً من ذلك.

std::auto_ptr<MyObject> p1 (new MyObject());
std::auto_ptr<MyObject> p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.

المؤشر الذكي هو كائن يعمل كمؤشر ، ولكنه يوفر أيضًا التحكم في الإنشاء والتدمير والنسخ والتنقل والتخلص من اللقطات.

يمكن للمرء أن ينفذ المؤشر الذكي الخاص به ، ولكن العديد من المكتبات توفر أيضًا تطبيقات مؤشر ذكية لكل منها مزايا وعيوب مختلفة.

على سبيل المثال ، يوفر Boost تطبيقات المؤشر الذكي التالية:

  • shared_ptr<T> هو مؤشر إلى T باستخدام عدد مرجع لتحديد متى لم تعد هناك حاجة إلى الكائن.
  • scoped_ptr<T> هو مؤشر يتم حذفه تلقائيًا عندما يخرج من النطاق. لا الاحالة ممكن.
  • intrusive_ptr<T> هو مؤشر عد مرجع آخر. يوفر أداء أفضل من shared_ptr ، ولكنه يتطلب النوع T لتوفير آلية حساب الإشارة الخاصة به.
  • weak_ptr<T> هو مؤشر ضعيف ، يعمل بالتزامن مع shared_ptr لتجنب المراجع الدائرية.
  • shared_array<T> يشبه shared_ptr ، ولكن للصفائف من T
  • scoped_array<T> تشبه scoped_ptr ، ولكن للصفائف من T

هذه مجرد وصف خطي واحد لكل منها ويمكن استخدامها حسب الحاجة ، لمزيد من التفاصيل والأمثلة ، يمكن للمرء أن ينظر إلى وثائق Boost.

بالإضافة إلى ذلك ، توفر المكتبة القياسية C ++ ثلاثة مؤشرات ذكية؛ std::unique_ptr للملكية الفريدة ، std::shared_ptr للملكية المشتركة و std::weak_ptr . std::auto_ptr موجود في C ++ 03 ولكنه مهمل الآن.


دع T يكون فئة في هذا البرنامج التعليمي يمكن تقسيم المؤشرات في C ++ إلى 3 أنواع:

1) مؤشرات الخام :

T a;  
T * _ptr = &a; 

لديهم عنوان ذاكرة إلى موقع في الذاكرة. استخدم بحذر ، حيث تصبح البرامج معقدة من الصعب تتبعها.

المؤشرات التي تحتوي على بيانات أو عنوان const {اقرأ للخلف}

T a ; 
const T * ptr1 = &a ; 
T const * ptr1 = &a ;

المؤشر إلى نوع البيانات T الذي هو const. بمعنى أنه لا يمكنك تغيير نوع البيانات باستخدام المؤشر. أي *ptr1 = 19 ؛ لن يعمل. ولكن يمكنك تحريك المؤشر. أي ptr1++ , ptr1-- ؛ الخ سوف تعمل. قراءة للخلف: مؤشر إلى نوع T الذي هو const

  T * const ptr2 ;

مؤشر const إلى نوع البيانات T. بمعنى أنه لا يمكنك تحريك المؤشر ولكن يمكنك تغيير القيمة المشار إليها بواسطة المؤشر. أي سوف يعمل *ptr2 = 19 ولكن ptr2++ ; ptr2-- ptr2++ ; ptr2-- الخ لن تعمل. قراءة إلى الخلف: const const إلى نوع T

const T * const ptr3 ; 

مؤشر const إلى نوع بيانات const T. بمعنى أنه لا يمكنك تحريك المؤشر ولا يمكنك تغيير مؤشر نوع البيانات ليكون المؤشر. أي. ptr3-- ; ptr3++ ; *ptr3 = 19; لن يعمل

3) المؤشرات الذكية : { #include <memory> }

المؤشر المشترك :

  T a ; 
     //shared_ptr<T> shptr(new T) ; not recommended but works 
     shared_ptr<T> shptr = make_shared<T>(); // faster + exception safe

     std::cout << shptr.use_count() ; // 1 //  gives the number of " 
things " pointing to it. 
     T * temp = shptr.get(); // gives a pointer to object

     // shared_pointer used like a regular pointer to call member functions
      shptr->memFn();
     (*shptr).memFn(); 

    //
     shptr.reset() ; // frees the object pointed to be the ptr 
     shptr = nullptr ; // frees the object 
     shptr = make_shared<T>() ; // frees the original object and points to new object

تم التنفيذ باستخدام العد المرجعي لتتبع عدد "الأشياء" التي تشير إلى الكائن الذي يشير إليه المؤشر. عندما ينتقل هذا العدد إلى 0 ، يتم حذف الكائن تلقائيًا ، أي يتم الاعتراض عند حذف جميع المشاروات إلى الكائن. هذا يتخلص من صداع الاضطرار إلى حذف الكائنات التي قمت بتخصيصها باستخدام جديد.

ضعيف المؤشر: يساعد على التعامل مع المرجع الدوري الذي ينشأ عند استخدام المؤشر المشترك إذا كان لديك كائنان مشار إليهما بمؤشرين مشتركين وهناك مؤشر داخلي مشترك يشير إلى كل مؤشر مشترك آخر ، فسيكون هناك مرجع دوري ولن يكون الكائن عند حذف المؤشرات المشتركة خارج النطاق. لحل هذه المشكلة ، قم بتغيير العضو الداخلي من shared_ptr إلى weak_ptr. ملاحظة: للوصول إلى العنصر المشار إليه بواسطة قفل استخدام مؤشر ضعيف () ، يؤدي ذلك إلى إرجاع weak_ptr.

T a ; 
shared_ptr<T> shr = make_shared<T>() ; 
weak_ptr<T> wk = shr ; // initialize a weak_ptr from a shared_ptr 
wk.lock()->memFn() ; // use lock to get a shared_ptr 
//   ^^^ Can lead to exception if the shared ptr has gone out of scope
if(!wk.expired()) wk.lock()->memFn() ;
// Check if shared ptr has gone out of scope before access

انظر: متى يكون std :: weak_ptr مفيد؟

مؤشر فريد: مؤشر خفيف الوزن الذكي مع ملكية حصرية. يستخدم عندما يشير المؤشر إلى كائنات فريدة بدون مشاركة الكائنات بين المؤشرات.

unique_ptr<T> uptr(new T);
uptr->memFn(); 

//T * ptr = uptr.release(); // uptr becomes null and object is pointed to by ptr
uptr.reset() ; // deletes the object pointed to by uptr 

لتغيير الكائن المشار إليه بواسطة ptr الفريد ، استخدم دلالات النقل

unique_ptr<T> uptr1(new T);
unique_ptr<T> uptr2(new T);
uptr2 = std::move(uptr1); 
// object pointed by uptr2 is deleted and 
// object pointed by uptr1 is pointed to by uptr2
// uptr1 becomes null 

اﻟﻣراﺟﻊ: ﯾﻣﻛن أن ﺗﻛون ﻋﻠﻰ اﻷﺳﺎس ﻋﻠﻰ أﻧﮭﺎ ﻣﺛل ﻣؤﺷرات ﺛﺎﺑﺗﺔ ، أي ﻣؤﺷر ﯾﻌﺗﻣد ﻋﻠﻰ اﻟﻛوﻛب وﻻ ﯾﻣﻛن ﻧﻘﻠﮫ ﺑﺻﯾﻐﺔ أﻓﺿل.

انظر: ما هي الاختلافات بين متغير مؤشر ومتغير مرجع في C ++؟

r-value reference : reference to a temporary object   
l-value reference : reference to an object whose address can be obtained
const reference : reference to a data type which is const and cannot be modified 

المرجع: https://www.youtube.com/channel/UCEOGtxYTB6vo6MQ-WQ9W_nQ شكرًا على Andre للإشارة إلى هذا السؤال.


هذا هو الرابط للحصول على إجابات مشابهة: http://sickprogrammersarea.blogspot.in/2014/03/technical-interview-questions-on-c_6.html

المؤشر الذكي هو كائن يتصرف ويشعر ويشعر وكأنه مؤشر عادي ولكنه يوفر المزيد من الوظائف. في C ++ ، يتم تطبيق المؤشرات الذكية كطبقات القالب التي تغلف مؤشر وتجاوز عوامل المؤشر القياسية. لديهم عدد من المزايا على المؤشرات العادية. تكون مضمّنة التهيئة كـ مؤشرات فارغة أو مؤشرات إلى كائن كومة الذاكرة المؤقتة. يتم التحقق من عدم التوجيه من خلال مؤشر فارغ. لا يوجد حذف ضروري. يتم تحرير الكائنات تلقائيًا عند اختفاء المؤشر الأخير إليها. مشكلة واحدة كبيرة مع هذه المؤشرات الذكية هي أنه على عكس المؤشرات العادية ، فإنها لا تحترم الميراث. المؤشرات الذكية غير جذابة للرمز متعدد الأشكال. أدناه هو مثال لتنفيذ المؤشرات الذكية.

مثال:

template <class X>
class smart_pointer
{
          public:
               smart_pointer();                          // makes a null pointer
               smart_pointer(const X& x)            // makes pointer to copy of x

               X& operator *( );
               const X& operator*( ) const;
               X* operator->() const;

               smart_pointer(const smart_pointer <X> &);
               const smart_pointer <X> & operator =(const smart_pointer<X>&);
               ~smart_pointer();
          private:
               //...
};

هذه الفئة تطبيق مؤشر ذكية إلى كائن من نوع X. الكائن نفسه موجود على كومة الذاكرة المؤقتة. إليك كيفية استخدامه:

smart_pointer <employee> p= employee("Harris",1333);

مثل مشغلات أخرى ذات حمولة زائدة ، سوف تتصرف p مثل مؤشر عادي ،

cout<<*p;
p->raise_salary(0.5);

http://en.wikipedia.org/wiki/Smart_pointer

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


المؤشر الذكي هو نوع يشبه المؤشر مع بعض الوظائف الإضافية ، مثل إلغاء تخصيص الذاكرة التلقائية ، وإحصاء المرجع إلخ.

مقدمة صغيرة متاحة على الصفحة الذكية مؤشرات - ما ، لماذا ، وماذا؟ .

أحد أنواع المؤشرات الذكية البسيطة هو std::auto_ptr (الفصل 20.4.5 من معيار C ++) ، والذي يسمح std::auto_ptr تخصيص الذاكرة تلقائيًا عندما يكون خارج النطاق ويكون أكثر قوة من استخدام المؤشر البسيط عند طرح استثناءات ، على الرغم من أنه أقل مرونة.

نوع آخر مناسب هو boost::shared_ptr الذي يطبق عد الإشارة ويقوم تلقائيًا بإلغاء تخصيص الذاكرة عند عدم وجود أي مراجع للكائن. هذا يساعد على تجنب تسرب الذاكرة وهو سهل الاستخدام لتنفيذ RAII .

يتم تغطية الموضوع بعمق في كتاب "C ++ Templates: The Complete Guide" للكاتب David Vandevoorde، Nicolai M. Josuttis ، Chapter Chapter 20. Smart Pointers. بعض الموضوعات المشمولة:

  • حماية ضد الاستثناءات
  • أصحاب ، (ملاحظة ، std::auto_ptr هو تنفيذ هذا النوع من مؤشر الذكية)
  • تكوين المورد هو التهيئة (يتم استخدام هذا بشكل متكرر لإدارة موارد الاستثناءات الآمنة في C ++)
  • قيود حامل
  • العد مرجع
  • الوصول المتزامن المضاد
  • التدمير وإلغاء التوزيع




c++-faq