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();
هي حالة السباق المحتملة.