في لغة C++، ما هي فوائد استخدام الاستثناءات والمحاولة/الالتقاط بدلاً من مجرد إرجاع رمز الخطأ؟

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

سؤال

لقد قمت ببرمجة C و C++ لفترة طويلة وحتى الآن لم أستخدم الاستثناءات مطلقًا وأحاول/أمسك.ما هي فوائد استخدام ذلك بدلاً من مجرد قيام الوظائف بإرجاع رموز الخطأ؟

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

المحلول

وربما نقطة واضحة - مطور يمكن تجاهل (أو لا تكون على علم) حالة عودتك وتستمر يجهل بسعادة أن شيئا ما فشل

.

واستثناء يجب أن يتم الاعتراف في بعض الطريق - لا يمكن تجاهلها بصمت دون وضع بنشاط شيء في المكان للقيام بذلك

.

نصائح أخرى

وميزة الاستثناءات ذات شقين:

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

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

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

ومع رسائل الخطأ:

int DoSomeThings()
{
    int error = 0;
    HandleA hA;
    error = CreateAObject(&ha);
    if (error)
       goto cleanUpFailedA;

    HandleB hB;
    error = CreateBObjectWithA(hA, &hB);
    if (error)
       goto cleanUpFailedB;

    HandleC hC;
    error = CreateCObjectWithA(hB, &hC);
    if (error)
       goto cleanUpFailedC;

    ...

    cleanUpFailedC:
       DeleteCObject(hC);
    cleanUpFailedB:
       DeleteBObject(hB);
    cleanUpFailedA:
       DeleteAObject(hA);

    return error;
}

ومع استثناءات وRAII

void DoSomeThings()
{
    RAIIHandleA hA = CreateAObject();
    RAIIHandleB hB = CreateBObjectWithA(hA);
    RAIIHandleC hC = CreateCObjectWithB(hB);
    ...
}

struct RAIIHandleA
{
    HandleA Handle;
    RAIIHandleA(HandleA handle) : Handle(handle) {}
    ~RAIIHandleA() { DeleteAObject(Handle); }
}
...

في أول وهلة، وإصدار RAII / الاستثناءات يبدو لفترة أطول، حتى كنت أدرك أن رمز تنظيف يحتاج إلى أن يكتب مرة واحدة فقط (وهناك طرق لتبسيط ذلك). ولكن الإصدار الثاني من DoSomeThings هو أكثر وضوحا للصيانة.

لا تحاول استخدام الاستثناءات في C ++ بدون لغة RAII، وسوف تسرب الموارد والذاكرة. يحتاج كل ما تبذلونه من تنظيف ينبغي القيام به في تالفة من الكائنات المخصصة المكدس.

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

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

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

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

  • قم بإرجاع رمز خطأ عندما تكون هناك حالة خطأ مُتوقع في بعض الحالات
  • رمي استثناء عندما يكون هناك شرط خطأ لا المتوقع في كل الأحوال

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

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

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

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

.

واستخدام تالفة في C ++ ويتخلصون منها في .NET لضمان أن الموارد يتم اطلاق سراح بشكل صحيح في وجود استثناءات يمكن أيضا تبسيط كبير التعليمات البرمجية. من أجل الحصول على نفس المستوى من الحماية مع رموز الخطأ تحتاج إما الكثير من البيانات if، والكثير من رمز تنظيف تكرار، أو المكالمات goto إلى كتلة المشترك لتنظيف في نهاية وظيفة. أي من هذه الخيارات ممتعة.

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

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

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

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

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

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

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

int divide(int a, int b)
{
    if( b == 0 )
        // then what?  no integer can be used for an error flag!
    else
        return a / b;
}

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

ومجرد استدعاء IsOk على الكائن الخطأ دون رد فعل على ذلك، وعندئذ يكون ما يعادل كتابة الصيد (...) {} أن كلا بيان عرض نفسه عدم وجود إرادة مبرمج جيد.

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

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

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