c++ - لشرح - ما نوع المؤشر الذي أستخدمه متى؟




قاموس عربي عربي pdf (3)

حسنا ، آخر مرة كتبت فيها C ++ std::auto_ptr ، std::auto_ptr كان كل lib std متاحة ، وكان boost::shared_ptr هو كل الغضب. أنا أبدا حقا نظرت إلى غيرها من أنواع مؤشر الذكية دفعة المقدمة. أفهم أن C ++ 11 يوفر الآن بعض أنواع الدفعة التي ظهرت ، ولكن ليس كلها.

هل لدى شخص ما خوارزمية بسيطة لتحديد متى يستخدم المؤشر الذكي؟ يفضل أن تتضمن النصيحة بخصوص مؤشرات الغم (مؤشرات الخام مثل T* ) وباقي المؤشرات الذكية للدفع. (شيء من this القبيل سيكون كبيرا).


استخدم unique_ptr<T> كل الوقت إلا عندما تحتاج إلى حساب المرجع ، وفي هذه الحالة استخدم shared_ptr<T> ( weak_ptr<T> نادرة جدًا ، weak_ptr<T> لمنع دورات المرجع). في كل حالة تقريبًا ، تكون الملكية الفريدة القابلة للنقل مناسبة تمامًا.

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

مؤشرات المصفوفة: unique_ptr له تخصص لـ T[] الذي يقوم تلقائيًا unique_ptr delete[] على النتيجة ، حتى تتمكن من القيام بأمان unique_ptr<int[]> p(new int[42]); فمثلا. shared_ptr لا تزال بحاجة إلى أداة تخصيص مخصصة ، لكنك لن تحتاج إلى مؤشر صفيف مشترك أو فريد متخصص. بطبيعة الحال ، عادة ما يتم استبدال هذه الأشياء على أفضل وجه بـ std::vector أي حال. لسوء الحظ ، لا يوفر shared_ptr وظيفة الوصول إلى الصفيف ، لذلك لا يزال عليك استدعاء get() يدويًا ، ولكن unique_ptr<T[]> توفر operator[] بدلاً من operator* unique_ptr<T[]> operator-> . في أي حال ، يجب عليك التحقق من نفسك. وهذا يجعل shared_ptr أقل ملاءمةً للمستخدم ، على الرغم من أنه يمكن القول إن الميزة العامة وعدم الاعتماد على Boost تجعل من unique_ptr و shared_ptr الفائزات مرة أخرى.

مؤشرات unique_ptr : غير ذات صلة بواسطة unique_ptr ، تمامًا مثل auto_ptr .

لا يوجد شيء أكثر من ذلك. في C ++ 03 دون تحريك الدلالات كان هذا الوضع معقدًا للغاية ، ولكن في C ++ 11 كانت النصيحة بسيطة جدًا.

لا تزال هناك استخدامات لمؤشرات ذكية أخرى ، مثل intrusive_ptr أو interprocess_ptr . ومع ذلك ، فهي متخصصة للغاية وغير ضرورية تماما في الحالة العامة.


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

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

إذا كان لديك ملكية فردية للكائن ، استخدم std::unique_ptr<T> .

إذا كان لديك ملكية مشتركة للكائن ...
- في حالة عدم وجود دورات في الملكية ، استخدم std::shared_ptr<T> .
- في حالة وجود دورات ، حدد "direction" واستخدم std::shared_ptr<T> في اتجاه واحد و std::weak_ptr<T> في الآخر.

إذا كان الكائن يملكك ، ولكن هناك احتمال عدم وجود مالك ، استخدم المؤشرات العادية T* (على سبيل المثال ، مؤشرات الأم).

إذا كان الكائن يملكك (أو كان هناك ضمانة أخرى) ، فاستخدم المراجع T& .

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

التكاليف:

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

أمثلة:

struct BinaryTree
{
    Tree* m_parent;
    std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};

لا تمتلك الشجرة الثنائية nullptr ، ولكن وجود شجرة يعني وجود nullptr (أو nullptr للجذر) ، بحيث يستخدم مؤشرًا عاديًا. تمتلك الشجرة الثنائية (مع دلالات القيمة) ملكية خاصة لأولادها ، بحيث تكون std::unique_ptr .

struct ListNode
{
    std::shared_ptr<ListNode> m_next;
    std::weak_ptr<ListNode> m_prev;
};

هنا ، تمتلك عقدة القائمة قائمتها التالية والسابقة ، لذا نحدد اتجاهًا ونستخدم shared_ptr weak_ptr next و weak_ptr لكسر الدورة.


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

لاحظ أن Boost بالإضافة إلى ذلك يقدم shared_array ، والذي قد يكون بديلًا مناسبًا لـ shared_ptr<std::vector<T> const> .

بعد ذلك ، يقدم Boost intrusive_ptr ، وهو حل خفيف إذا كان موردك يقدم إدارة ذات مرجعية بالفعل وترغب في اعتمادها على مبدأ RAII. لم يكن هذا المعتمد من المعيار.

ملكية فريدة:
يحتوي Boost أيضًا على scoped_ptr ، والذي لا يمكن scoped_ptr والذي لا يمكنك تحديده. std::unique_ptr is boost::scoped_ptr on steroids and should be your default choice when you need a smart pointer . يسمح لك بتحديد deleter في وسيطات القالب الخاصة به وهو متحرك ، بخلاف boost::scoped_ptr . كما أنها قابلة للاستخدام بالكامل في حاويات STL طالما أنك لا تستخدم عمليات تحتاج إلى أنواع قابلة للنسخ (من الواضح).

لاحظ مرة أخرى ، أن Boost يحتوي على إصدار مصفوفة: scoped_array ، وهو المعياري الموحد عن طريق طلب std::unique_ptr<T[]> التخصص الجزئي الذي delete[] المؤشر بدلاً من delete (مع default_delete r). std::unique_ptr<T[]> أيضاً operator[] بدلاً من operator* std::unique_ptr<T[]> operator-> .

لاحظ أن std::auto_ptr لا يزال في المعيار ، لكنه تم إهماله . §D.10 [depr.auto.ptr]

تم إيقاف قالب الفئة auto_ptr . [ ملاحظة: يوفر قالب الفئة unique_ptr (20.7.1) حلاً أفضل. لاحظ مذكرة

بلا ملكية:
استخدم مؤشرات غبية (مؤشرات أولية) أو مراجع للإشارات غير المملوكة للموارد وعندما تعرف أن المورد سوف يتجاوز عمر الكائن / النطاق المرجعي. تفضّل المراجع واستخدم المؤشرات الأولية عندما تحتاج إلى nullability أو resettability.

إذا كنت تريد مرجعًا غير مملوك لمورد ، ولكنك لا تعرف ما إذا كان المورد weak_ptr الكائن الذي يشير إليه ، weak_ptr المورد في shared_ptr واستخدم weak_ptr - يمكنك اختبار ما إذا كان weak_ptr الرئيسي على قيد الحياة lock ، والذي سيؤدي إلى وجود قيمة shared_ptr غير فارغة إذا كان المورد لا يزال موجودًا. إذا كنت تريد اختبار ما إذا كان المورد ميتًا أم لا ، expired إنهاء الاستخدام. قد يبدو الاثنان متشابهين ، لكنهما مختلفان تمامًا في مواجهة التنفيذ المتزامن ، حيث أن expired لا تضمن سوى قيمة الإرجاع لهذا البيان الوحيد. اختبار على ما يبدو بريء مثل

if(!wptr.expired())
  something_assuming_the_resource_is_still_alive();

هي حالة السباق المحتملة.







c++-faq