سؤال

ممكن مكررة:
البرمجة الدفاعية

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

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

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

ما هي تجاربك حول هذا الموضوع؟ كيف تكتب الدفاعات الموجودة في التعليمات البرمجية الخاصة بك للمعلمات التي تم تمريرها إلى أساليب تابعة؟

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

المحلول

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

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

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

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

نصائح أخرى

أفضل طريقة لتكون دفاعية ليست للتحقق من المؤشرات ل NULL في وقت التشغيل، ولكن ل تجنب استخدام المؤشرات التي قد تكون فارغة

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

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

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

لا أستطيع التفكير في لغة سائدة أخرى تسمح لك بالقبض على أكبر عدد ممكن من الأخطاء في وقت Compile-A ++. استخدام هذه القدرة.

لا توجد طريقة للتحقق مما إذا كان المؤشر صالحا.

كل شيء خطير، يعتمد ذلك على عدد الأخطاء التي ترغب في إلحاق بها.

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

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

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

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

الآن، يمكنك استدعاء تفكيرك الدفاعي والوحدة التي تم اختبارها جيدا (التي هي التي تفتقر للأسف إلى فحص مؤشر داخلي NULL) و Boom! nullpointerException ذلك، دون فحص داخلي، ليس لديك أي طريقة للتعامل مع:

defensiveMethod(thirdPartySearch("Nothing matches me")); 
// You just passed a null to your own code.

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

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

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

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

بشكل عام، لا أميل إلى استخدام أنواع "الخام" مباشرة. دعونا توضح:

void myFunction(std::string const& foo, std::string const& bar);

ما هي القيم المحتملة لل foo و bar ب حسنا هذا محدود إلى حد كبير فقط بأي std::string قد تحتوي ... التي هي غامضة جدا.

على الجانب الآخر:

void myFunction(Foo const& foo, Bar const& bar);

هو أفضل بكثير!

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

لدي ميل لصالح كتابة قوية. إذا كان لدي إدخال يجب أن يتكون فقط من الأحرف الأبجدية ويكون ما يصل إلى 12 حرفا، فأفضل إنشاء تفاف فئة صغيرة std::string, مع بسيطة validate الطريقة المستخدمة داخليا للتحقق من المهام، وتمرير هذه الفئة حولها بدلا من ذلك. بهذه الطريقة أعرف أنه إذا قمت باختبار روتين التحقق من الصحة مرة واحدة، فلا يجب أن أقلق فعلا بشأن جميع المسارات التي يمكن من خلالها أن تصل إلي هذه القيمة إلي> سيتم التحقق من صحتها عندما تصل إلي.

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

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

"اختبارات الوحدة التحقق من التعليمات البرمجية تفعل ما يجب القيام به"> رمز الإنتاج الذي يحاول التحقق من عدم القيام بأي حال من المفترض أن يفعله ".

لن أتحقق حتى عن NULL لنفسي، ما لم يكن جزءا من API المنشور.

ذلك كثيرا يعتمد. هي الطريقة المعنية التي تم استدعاؤها بواسطة الرمز الخارجي إلى مجموعتك، أم أنها طريقة داخلية؟

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

للطرق المرئية من الخارج - إذا كان لديك أي - يجب عليك دائما التحقق من المدخلات الخاصة بك دائما. دائماً.

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

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

التحقق من مؤشر فارغة هو فقط نصف القصة, ، يجب عليك أيضا تعيين قيمة فارغة لكل مؤشر غير مخصص.
سوف API المسؤول سوف تفعل الشيء نفسه.
التحقق من مؤشر فارغ يأتي رخيصا جدا في دورات وحدة المعالجة المركزية، ولديه تطبيق يتعطل بمجرد تسليمه قد يكلفك وشركتك في المال والسمعة.

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

هناك بعض الأشياء في العمل هنا في هذا السؤال الذي أود معالجته:

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

يمكنك إنشاء مؤشر ذكي يقوم بالضبط الافتراضي Nullobject الذي يشبه هذا:

template <class Type, class NullTypeDefault>
struct possibly_null_ptr {
  possibly_null_ptr() : p(new NullTypeDefault) {}
  possibly_null_ptr(Type* p_) : p(p_) {}
  Type * operator->() { return p.get(); }
  ~possibly_null_ptr() {}
  private:
    shared_ptr<Type> p;
    friend template<class T, class N> Type & operator*(possibly_null_ptr<T,N>&);
};

template <class Type, class NullTypeDefault>
Type & operator*(possibly_null_ptr<Type,NullTypeDefault> & p) {
  return *p.p;
}

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

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

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

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

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

نحن نفعل برامج الطيران وتأثيرات خطأ البرنامج من إزعاج بسيط لفقدان الطائرات / الطاقم. نقوم بتصنيف أجزاء مختلفة من البرامج بناء على تأثيراتها الضارة المحتملة التي تؤثر على معايير الترميز، والاختبار، وما إلى ذلك. تحتاج إلى تقييم كيفية استخدام برنامجك وتأثيرات الأخطاء وتحديد مستوى الدفاع الذي تريده (ويمكنك تحمله). ال DO-178B القياسية يدعو هذا "مستوى ضمان التصميم".

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