سؤال

إذا كان لدي وظيفة تحتاج إلى العمل مع ملف shared_ptr, ، أليس من الأفضل تمرير إشارة إليها (لتجنب نسخ ملف shared_ptr هدف)؟ما هي الآثار الجانبية السيئة المحتملة؟أتصور حالتين محتملتين:

1) داخل الدالة يتم عمل نسخة من الوسيطة، كما هو الحال في

ClassA::take_copy_of_sp(boost::shared_ptr<foo> &sp)  
{  
     ...  
     m_sp_member=sp; //This will copy the object, incrementing refcount  
     ...  
}  

2) داخل الدالة يتم استخدام الوسيطة فقط، كما هو الحال في

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}  

لا أستطيع أن أرى في كلتا الحالتين سببًا وجيهًا لتمرير boost::shared_ptr<foo> حسب القيمة بدلا من المرجع.سيؤدي المرور حسب القيمة إلى زيادة عدد المراجع "مؤقتًا" فقط بسبب النسخ، ثم تقليله عند الخروج من نطاق الوظيفة.هل أطل على شيء ما؟

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

هل كانت مفيدة؟

المحلول

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

Class::only_work_with_sp(boost::shared_ptr<foo> sp)
{
    // sp points to an object that cannot be destroyed during this function
}

لذلك باستخدام إشارة إلى أ shared_ptr, ، يمكنك تعطيل هذا الضمان.لذلك في حالتك الثانية:

Class::only_work_with_sp(boost::shared_ptr<foo> &sp) //Again, no copy here  
{    
    ...  
    sp->do_something();  
    ...  
}

كيف تعرف ذلك sp->do_something() لن تنفجر بسبب مؤشر فارغ؟

كل هذا يتوقف على ما هو موجود في تلك الأقسام "..." من الكود.ماذا لو اتصلت بشيء خلال "..." الأول له تأثير جانبي (في مكان ما في جزء آخر من الكود) وهو مسح shared_ptr لنفس الكائن؟وماذا لو كان هو الوحيد المتبقي المتميز shared_ptr لهذا الكائن؟وداعًا للكائن، في المكان الذي أنت على وشك تجربته واستخدامه.

إذن هناك طريقتان للإجابة على هذا السؤال:

  1. قم بفحص مصدر البرنامج بأكمله بعناية شديدة حتى تتأكد من أن الكائن لن يموت أثناء الجسم الوظيفي.

  2. قم بتغيير المعلمة مرة أخرى لتكون كائنًا مميزًا بدلاً من مرجع.

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

تحديث للمعلق JQ

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

لدينا وظيفة من شأنها أن ترسل رسالة في مكان ما.قد تكون رسالة كبيرة لذا بدلاً من استخدام ملف std::string من المحتمل أن يتم نسخها أثناء تمريرها إلى أماكن متعددة، نستخدم a shared_ptr إلى سلسلة:

void send_message(std::shared_ptr<std::string> msg)
{
    std::cout << (*msg.get()) << std::endl;
}

(نحن فقط "نرسلها" إلى وحدة التحكم في هذا المثال).

الآن نريد إضافة وسيلة لتذكر الرسالة السابقة.نريد السلوك التالي:يجب أن يكون هناك متغير يحتوي على أحدث رسالة تم إرسالها، ولكن أثناء إرسال الرسالة حاليًا، يجب ألا تكون هناك رسالة سابقة (يجب إعادة تعيين المتغير قبل الإرسال).لذلك نعلن عن المتغير الجديد:

std::shared_ptr<std::string> previous_message;

ثم نقوم بتعديل وظيفتنا وفقًا للقواعد التي حددناها:

void send_message(std::shared_ptr<std::string> msg)
{
    previous_message = 0;
    std::cout << *msg << std::endl;
    previous_message = msg;
}

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

send_message(std::shared_ptr<std::string>(new std::string("Hi")));
send_message(previous_message);

وكما هو متوقع، هذا يطبع Hi! مرتين.

الآن يأتي السيد كونتينر، الذي ينظر إلى الكود ويفكر:مهلا، تلك المعلمة ل send_message هو shared_ptr:

void send_message(std::shared_ptr<std::string> msg)

من الواضح أنه يمكن تغيير ذلك إلى:

void send_message(const std::shared_ptr<std::string> &msg)

فكر في تحسين الأداء الذي سيجلبه هذا!(ناهيك عن أننا على وشك إرسال رسالة كبيرة عادةً عبر بعض القنوات، وبالتالي فإن تحسين الأداء سيكون صغيرًا جدًا بحيث لا يمكن قياسه).

لكن المشكلة الحقيقية هي أن رمز الاختبار سيُظهر الآن سلوكًا غير محدد (في عمليات تصحيح أخطاء Visual C++ 2010، يتعطل).

يتفاجأ السيد كونتينر بهذا، لكنه يضيف فحصًا دفاعيًا إليه send_message في محاولة لوقف حدوث المشكلة:

void send_message(const std::shared_ptr<std::string> &msg)
{
    if (msg == 0)
        return;

ولكن بالطبع لا يزال يمضي قدمًا ويتعطل، لأنه msg لا تكون فارغة أبدًا عندما send_message يسمى.

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

الحل السهل، حيث تريد أن تكون الوظيفة قادرة على الاعتماد على ملف shared_ptr الاستمرار في أن تكون غير فارغة طوال الوقت، هو أن تقوم الوظيفة بتخصيص صحيح خاص بها shared_ptr, ، بدلاً من الاعتماد على إشارة إلى موجود shared_ptr.

الجانب السلبي هو أن نسخ أ shared_ptr ليست مجانية:حتى التطبيقات "الخالية من القفل" يجب أن تستخدم عملية متشابكة للوفاء بضمانات الترابط.لذلك قد تكون هناك مواقف يمكن فيها تسريع البرنامج بشكل كبير عن طريق تغيير ملف shared_ptr الى shared_ptr &.لكن هذا ليس تغييرًا يمكن إجراؤه بأمان على كافة البرامج.يغير المعنى المنطقي للبرنامج.

لاحظ أنه قد يحدث خطأ مماثل إذا استخدمنا std::string في جميع أنحاء بدلا من std::shared_ptr<std::string>, ، وبدلاً من:

previous_message = 0;

ولمسح الرسالة قلنا:

previous_message.clear();

عندها سيكون العَرَض هو الإرسال غير المقصود لرسالة فارغة، بدلاً من السلوك غير المحدد.قد تكون تكلفة نسخة إضافية من سلسلة كبيرة جدًا أكثر أهمية من تكلفة نسخ ملف shared_ptr, ، لذلك قد تكون المقايضة مختلفة.

نصائح أخرى

ولقد وجدت نفسي اختلافه مع الجواب أعلى صوت، لذلك ذهبت تبحث عن opinons الخبراء وهنا هم. من http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2011-Scott-Andrei-and-Herb-Ask-Us-Anything

وعشب سوتر: "عندما كنت تمر في shared_ptr، ونسخا غالية"

وسكوت مايرز: "لا يوجد شيء خاص حول shared_ptr عندما يتعلق الأمر ما إذا كنت تمريرها من حيث القيمة، أو نقله بالرجوع استخدام بالضبط نفس التحليل كنت تستخدم لأي نوع مستخدم آخر تعريف يبدو أن الناس لديهم هذا التصور أن. shared_ptr يحل بطريقة أو بأخرى جميع المشاكل الإدارية، وذلك لأنه صغير، انها غير مكلفة بالضرورة أن يمر من حيث القيمة، ولها ليتم نسخها، وليس هناك تكلفة المرتبطة بذلك ... انها باهظة الثمن لتمريرها من حيث القيمة، حتى لو أستطيع تفلت من العقاب مع معاني الكلمات المناسبة في برنامجي، أنا ستعمل تمرير ذلك بالرجوع إلى CONST أو إشارة بدلا من ذلك "

وعشب سوتر: "تمر دائما لهم بالرجوع إلى CONST، وأحيانا جدا ربما لأنك تعرف ما يسمى يمكن تعديل شيء كنت حصلت على إشارة من، ربما فإنك قد تمر من حيث القيمة ... إذا كنت نسخها كما المعلمات، يا إلهي أنت أبدا تقريبا بحاجة إلى عثرة أن عدد مرجع لأنه محتجز على قيد الحياة على أي حال، ويجب عليك ان تمرر عليه بالرجوع، لذا يرجى أن "

تحديث: توسعت عشب على هذا هنا: http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/ ، على الرغم من أن المغزى من هذه القصة هو أنه لا يجب أن يمر في shared_ptr على الإطلاق "ما لم تريد استخدامها أو التلاعب في مؤشر الذكية نفسها، مثل لتبادل أو نقل ملكية ".

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

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

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

نعم، مع الأخذ في إشارة على ما يرام هناك. كنت لا تنوي اعطاء طريقة الملكية المشتركة. انها تريد فقط للعمل معها. هل يمكن أن تتخذ مرجعا للحالة الأولى أيضا، حيث قمت بنسخ أنه على أية حال. ولكن بالنسبة للحالة الأولى، <م> يأخذ الملكية. هناك هذه الحيلة للا يزال نسخه مرة واحدة فقط:

void ClassA::take_copy_of_sp(boost::shared_ptr<foo> sp) {
    m_sp_member.swap(sp);
}

ويجب عليك أيضا نسخ عند إعادته (أي لن يعود إشارة). لأن صفك لا يعرف ما يقوم به العميل معها (يمكن تخزين مؤشر إلى ذلك ومن ثم يحدث الانفجار الكبير). اذا تبين في وقت لاحق انها عنق الزجاجة (الملف الشخصي الأول!)، ثم لا يزال بإمكانك العودة مرجعا.


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

فمن المنطقي أن يمر shared_ptrs التي كتبها const&. وسوف لا يحتمل السبب مشكلة (ما عدا في حالة غير المرجح أن يتم حذف shared_ptr المشار إليها أثناء المكالمة وظيفة، على النحو الذي فصله Earwicker)، وأنه من المرجح أن تكون أسرع إذا قمت بتمرير الكثير من هذه حولها. تذكر؛ وboost::shared_ptr الافتراضي هو موضوع آمنة، لذلك نسخها يتضمن زيادة آمنة الموضوع.

وحاول استخدام const& بدلا من مجرد &، لأن الكائنات المؤقتة قد لا يتم تمريرها حسب المرجع غير CONST. (على الرغم من أن تمديد لغة في MSVC يسمح لك أن تفعل ذلك على أي حال)

في الحالة الثانية، تفعل هذا هو أبسط:

Class::only_work_with_sp(foo &sp)
{    
    ...  
    sp.do_something();  
    ...  
}

ويمكنك تسميتها ك

only_work_with_sp(*sp);

وأود أن تجنب إشارة "واضحة" ما لم تكن وظيفة explicitely قد يغير من المؤشر.

قد يكون const & من المعقول الأمثل الصغير عند استدعاء دالات صغيرة - على سبيل المثال لتمكين مزيد من التحسينات، مثل رمز مصدر بعيدا بعض الشروط. كما أن زيادة / إنقاص - لأنه موضوع آمنة - هي نقطة التزامن. وأود أن لا نتوقع أن تحدث فرقا كبيرا في معظم الحالات، على الرغم من.

وعموما، يجب عليك استخدام أسلوب أكثر بساطة لم يكن لديك سبب لعدم. ثم، إما استخدام const & باستمرار، أو إضافة تعليق لماذا إذا كنت تستخدم فقط في عدد قليل من الأماكن.

وأود أن الدعوة تمرير مؤشر المشتركة بالرجوع CONST - أ دلالات أن وظيفة يتم تمريرها مع المؤشر لا تملك المؤشر، وهي لغة نظيفة للمطورين

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

وفاة المشتركة مؤشر بالرجوع غير CONST أمر خطير أحيانا - السبب هو تبادل وإعادة تعيين وظائف وظيفة قد تحتج داخل ذلك لتدمير الكائن التي لا تزال تعتبر سارية المفعول بعد ترجع الدالة

ووليس عن التحسين سابق لأوانه، وأعتقد - فهو يقع في حوالي تجنب النفايات غير الضرورية من دورات CPU عندما كنت واضحا ما تريد القيام به وبحزم تم اعتماد لغة الترميز من زملائك المطورين

وفقط بلدي 2 سنت: -)

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

لنعود إلى المثال:

Class::only_work_with_sp( foo &sp ) //Again, no copy here  
{    
    ...  
    sp.do_something();  
    ...  
}

وكيف يمكنك أن تعرف أن sp.do_something() لن نسف بسبب مؤشر التعلق؟

والحقيقة هي أنه shared_ptr أم لا، CONST أم لا، وهذا يمكن أن يحدث إذا كان لديك عيب تصميم، مثل تقاسم مباشرة أو غير مباشرة على ملكية sp بين المواضيع، missusing كائن القيام delete this، لديك ملكية دائرية أو أخطاء ملكية أخرى.

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

وعلى سبيل المثال، هذا الرمز ينتج خطأ، لكنه لن تعمل إذا قمت بتغيير test() بحيث لا يتم تمرير مؤشر المشتركة بالرجوع.

#include <boost/shared_ptr.hpp>

class Base { };
class Derived: public Base { };

// ONLY instances of Base can be passed by reference.  If you have a shared_ptr
// to a derived type, you have to cast it manually.  If you remove the reference
// and pass the shared_ptr by value, then the cast is implicit so you don't have
// to worry about it.
void test(boost::shared_ptr<Base>& b)
{
    return;
}

int main(void)
{
    boost::shared_ptr<Derived> d(new Derived);
    test(d);

    // If you want the above call to work with references, you will have to manually cast
    // pointers like this, EVERY time you call the function.  Since you are creating a new
    // shared pointer, you lose the benefit of passing by reference.
    boost::shared_ptr<Base> b = boost::dynamic_pointer_cast<Base>(d);
    test(b);

    return 0;
}

سأفترض أنك على دراية التحسين المبكر ويطلبون ذلك إما لأغراض أكاديمية أو لأنك قمت بعزل بعض التعليمات البرمجية الموجودة مسبقًا والتي تعاني من ضعف الأداء.

المرور حسب المرجع على ما يرام

يعد المرور بمرجع const أفضل، ويمكن استخدامه عادة، لأنه لا يفرض الثبات على الكائن المشار إليه.

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

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

يجب تجنب دائمًا تخزينها المؤشر الذكي الخاص بك كمرجع.لك Class::take_copy_of_sp(&sp) يوضح المثال الاستخدام الصحيح لذلك.

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

void FooTakesReference( boost::shared_ptr< int > & ptr )
{
    ptr.reset(); // We reset, and so does sharedA, memory is deleted.
}

void FooTakesValue( boost::shared_ptr< int > ptr )
{
    ptr.reset(); // Our temporary is reset, however sharedB hasn't.
}

void main()
{
    boost::shared_ptr< int > sharedA( new int( 13 ) );
    boost::shared_ptr< int > sharedB( new int( 14 ) );

    FooTakesReference( sharedA );

    FooTakesValue( sharedB );
}

ومن المثال أعلاه نرى أن يمر <م> sharedA بالرجوع يسمح <م> FooTakesReference لإعادة المؤشر الأصلي، مما يقلل من انها تستخدم العد إلى 0، وتدمير البيانات الخاص به. FooTakesValue ، ومع ذلك، لا يمكن إعادة تعيين المؤشر الأصلي، وضمان <م> sharedB ل البيانات لا تزال صالحة للاستعمال. عندما لا محالة يأتي مطور آخر على طول ويحاول الظهر على جود الهش sharedA، وتستتبعه الفوضى. محظوظ <م> sharedB مطور، ومع ذلك، يذهب إلى البيت باكرا كما كل الحق في عالمه.

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

وساندي كتب: "يبدو أن جميع الايجابيات والسلبيات هنا يمكن بالفعل تعميمها على أي نوع تمريرها حسب المرجع لا shared_ptr فقط"

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

وكتبت "تقريبا" في تلك الجملة السابقة بسبب الأداء يمكن أن يكون مصدر قلق، وانها قد "يكون مبررا في حالات نادرة، ولكن أود أيضا أن تجنب هذا السيناريو نفسي والبحث عن كل الحلول الأمثل أخرى مناسبة نفسي، مثل إلى النظر بجدية إلى إضافة مستوى آخر من المراوغة والتقييم كسول، الخ ..

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

وكنت أعمل في مشروع المبدأ كان قويا جدا حول تمرير مؤشرات الذكية من حيث القيمة. عندما طلب مني القيام ببعض تحليل الأداء - وجدت أنه لزيادة وإنقاص العدادات المرجعية لمؤشرات الذكية تطبيق ينفق ما بين 4-6٪ من وقت المعالج المستخدمة

.

إذا كنت تريد تمرير مؤشرات الذكية من حيث القيمة فقط لتجنب وجود قضايا في حالات غريبة كما هو موضح من دانيال Earwicker تأكد من أنك تفهم الثمن الذي دفع لذلك.

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

وبالإضافة إلى ما قاله litb، أود أن أشير إلى أنه من المحتمل أن تمر بالرجوع CONST في المثال الثاني، وبهذه الطريقة كنت متأكدا من أنك لا تعديله عن طريق الخطأ.

struct A {
  shared_ptr<Message> msg;
  shared_ptr<Message> * ptr_msg;
}
  1. تمر بالقيمة:

    void set(shared_ptr<Message> msg) {
      this->msg = msg; /// create a new shared_ptr, reference count will be added;
    } /// out of method, new created shared_ptr will be deleted, of course, reference count also be reduced;
    
  2. تمر بالإشارة:

    void set(shared_ptr<Message>& msg) {
     this->msg = msg; /// reference count will be added, because reference is just an alias.
     }
    
  3. المرور بالمؤشر:

    void set(shared_ptr<Message>* msg) {
      this->ptr_msg = msg; /// reference count will not be added;
    }
    

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

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

لذا، نعم، IMO، يجب أن يكون الإعداد الافتراضي هو "تمر بمرجع ثابت".

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top