هل هناك فوائد لتمرير المؤشر على المرور حسب المرجع في C++؟

StackOverflow https://stackoverflow.com/questions/334856

سؤال

ما هي فوائد المرور بالمؤشر على المرور بالمرجع في C++؟

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

مثال:

func(SPRITE *x);

مع نداء

func(&mySprite);

ضد.

func(SPRITE &x);

مع نداء

func(mySprite);
هل كانت مفيدة؟

المحلول

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

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

// Is mySprite passed by value or by reference?  You can't tell 
// without looking at the definition of func()
func(mySprite);

// func2 passes "by pointer" - no need to look up function definition
func2(&mySprite);

نصائح أخرى

المرور بالمؤشر

  • يجب على المتصل أن يأخذ العنوان -> غير شفاف
  • يمكن توفير قيمة 0 للمعنى nothing.يمكن استخدام هذا لتوفير الوسائط الاختيارية.

تمر بالرجوع إليها

  • يقوم المتصل فقط بتمرير الكائن -> شفاف.يجب استخدامه للتحميل الزائد للمشغل، نظرًا لأن التحميل الزائد لأنواع المؤشرات غير ممكن (المؤشرات هي أنواع مدمجة).لذلك لا يمكنك أن تفعل string s = &str1 + &str2; باستخدام المؤشرات.
  • لا توجد قيم 0 ممكنة -> لا يتعين على الوظيفة التي يتم الاتصال بها التحقق منها
  • الإشارة إلى const تقبل أيضًا المؤقتات: void f(const T& t); ... f(T(a, b, c));, ، لا يمكن استخدام المؤشرات بهذه الطريقة لأنه لا يمكنك أخذ عنوان مؤقت.
  • أخيرًا وليس آخرًا، المراجع أسهل في الاستخدام -> فرصة أقل للأخطاء.

يسرد كتاب Allen Holub "الحبل الكافي لإطلاق النار على قدمك" القاعدتين التاليتين:

120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers

يسرد عدة أسباب وراء إضافة المراجع إلى C++:

  • فهي ضرورية لتحديد منشئي النسخ
  • فهي ضرورية لأحمال المشغل الزائدة
  • const تسمح لك المراجع بالحصول على دلالات تمريرية مع تجنب النسخ

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

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

يعجبني المنطق في مقال من "cplusplus.com:"

  1. قم بالتمرير حسب القيمة عندما لا ترغب الوظيفة في تعديل المعلمة ويكون من السهل نسخ القيمة (ints، doubles، char، bool، إلخ...أنواع بسيطة.std::string وstd::vector وجميع حاويات STL الأخرى ليست أنواعًا بسيطة.)

  2. قم بالتمرير بواسطة مؤشر const عندما تكون القيمة مكلفة للنسخ ولا تريد الوظيفة تعديل القيمة المشار إليها وتكون NULL قيمة صالحة ومتوقعة تعالجها الوظيفة.

  3. قم بالتمرير بمؤشر غير ثابت عندما تكون القيمة مكلفة للنسخ وتريد الوظيفة تعديل القيمة المشار إليها وتكون NULL قيمة صالحة ومتوقعة تعالجها الوظيفة.

  4. قم بالتمرير بواسطة مرجع const عندما يكون نسخ القيمة مكلفًا ولا ترغب الدالة في تعديل القيمة المشار إليها ولن تكون NULL قيمة صالحة إذا تم استخدام مؤشر بدلاً من ذلك.

  5. قم بالتمرير بمرجع غير تابع عندما تكون القيمة مكلفة للنسخ وتريد الوظيفة تعديل القيمة المشار إليها ولن تكون NULL قيمة صالحة إذا تم استخدام مؤشر بدلاً من ذلك.

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

http://www.cplusplus.com/articles/z6vU7k9E/

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

ما إذا كانت القيمة مدخلة أو مخرجة أو قابلة للتعديل وما إلى ذلك.يجب أن يكون في الوثائق/التعليقات حول الوظيفة، بعد كل شيء.

توضيحات للمشاركات السابقة:


المراجع هي لا ضمان الحصول على مؤشر غير فارغ.(على الرغم من أننا غالبًا ما نعاملهم على هذا النحو).

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

bool test( int & a)
{
  return (&a) == (int *) NULL;
}

int
main()
{
  int * i = (int *)NULL;
  cout << ( test(*i) ) << endl;
};

المشكلة الحقيقية التي أواجهها مع المراجع تكمن في المبرمجين الآخرين، الذين يطلق عليهم من الآن فصاعدا البلهاء, ، الذي يخصص في المنشئ، يخصص في المدمر، وفشلت في توفير منشئ نسخة أو عامل التشغيل=().

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

ولحسن الحظ فإن المترجمين الحديثين سوف يلتقطون هذا التخصيص المزدوج لنفس المؤشر.قبل 15 عاماً، لم يفعلوا ذلك.(تحت gcc/g++، استخدم setenv MALLOC_CHECK_0 لإعادة النظر في الطرق القديمة.) مما يؤدي، في ظل DEC UNIX، إلى تخصيص نفس الذاكرة لكائنين مختلفين.الكثير من متعة التصحيح هناك ...


بشكل عملي أكثر:

  • تخفي المراجع أنك تقوم بتغيير البيانات المخزنة في مكان آخر.
  • من السهل الخلط بين المرجع والكائن المنسوخ.
  • المؤشرات تجعل الأمر واضحًا!

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

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

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