سؤال

سمعت أنك لا ينبغي أبدا رمي سلسلة لأن هناك نقص في المعلومات و عليك التقاط استثناءات لا نتوقع أن قبض.ما هي الممارسات الجيدة من أجل رمي استثناءات ؟ هل ترث قاعدة استثناء الصف ؟ هل لديك العديد من الاستثناءات أو قليلة ؟ تفعل MyExceptionClass& const أو MyExceptionClass& ?الخ.أيضا أعرف الاستثناءات يجب أن يسبق طرح في destructors

سأضيف أن أفهم التصميم قبل العقد عند رمي استثناء.أنا أسأل كيف لي أن رمي استثناءات.

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

المحلول

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

بالنظر إلى هاتين الوظيفتين:

const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;

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

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

لاحظ أنه في حالة findapple., ، الأمر متروك للمتصل لتحديد كيفية التعامل مع حالة "عدم العثور على التفاحة الصحيحة" لأنه لم يعد حالة استثنائية.

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

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

ديف

نصائح أخرى

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

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

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

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

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

class DeviceException {
    ;
}

class DeviceIOException: public DeviceException {
    DeviceIOException(std::string msg, int errorCode);
}

إلخ.

ثم يمكنني الحكم / التصرف على الاستثناء حسب النوع والمعلومات الواردة في الداخل.

أنا دائما رمي استثناء مع رسالة من حيث حدثت وما سبب حدوثه:

throw NException("Foo::Bar", "Mungulator cause a stack overflow!");

يمكنك بعد ذلك استخدام هذه السلاسل في صناديق الرسائل وما إلى ذلك.

أنا دائما الصيد عن طريق

catch (NException& ex) { ... }

إذا قمت بتشغيل Windows، فيمكنك اجتياز قيمة الخطأ ولدي وظيفة تستمد رسالة الخطأ. أفضل مثال على ذلك في ويندوز عبر C / C ++ بواسطة جيفري ريختر.

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

في استخدام فئة أو فئة التسلسل الهرمي هناك بعض النقاط التي يجب عليك أن تنظر:

  1. كل من منشئ نسخة ، المدمر من وجوه استثناء يجب أن أبدا بطرح استثناء.إذا فإنها تفعل أنت البرنامج تنهي على الفور.(ISO 15.5/1)

  2. إذا كان الاستثناء كائنات قاعدة دروس, ثم استخدام الوراثة العامة.
    معالج فقط سيتم اختيارهم المستمدة من قاعدة الطبقة إذا كانت قاعدة الفئة يمكن الوصول إليها.(ISO 15.3/3)

  3. وأخيرا (لجميع أنواع الاستثناءات) ضمان أن التعبير التي القيت لا في حد ذاته يؤدي إلى استثناء التي القيت.

على سبيل المثال:

class Ex {
public:
  Ex(int i) 
  : m_i (i)
  {
    if (i > 10) {
      throw "Exception value out of range";
    }
  }

  int m_i;
};


void foo (bool b) {
  if (! b) {
     // 'b' is false this is bad - throw an exception
     throw Ex(20);    // Ooops - throw's a string, not an Ex
  }
}

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

catch(std::exception& e)
كتلة وينبغي القيام به معها.(على الرغم من أن في كثير من الأحيان أنك لن تكون قادرة على الحصول على بعيدا مع أنه إذا كنت لا السيطرة على جميع التعليمات البرمجية التي يمكن رمي).

أنا أميل إلى رمي الاستثناءات الوحيدة التي يقدمها معيار (أيالأمراض المنقولة جنسيا::runtime_error) ولكن إذا كنت ترغب في توفير مزيد من تحبب إلى معالجات, يجب أن لا تتردد في اشتقاق الخاصة بك من الأمراض المنقولة جنسيا::الاستثناء.انظر C++ التعليمات لايت.

أيضا, يجب عليك رمي مؤقت وقبض عليه من قبل المرجعية (لتجنب نسخ المنشئ الاحتجاج في اللحاق بك في الموقع).رمي المؤشرات أيضا مكروها لأنه من غير الواضح من الذي يجب تنظيف الذاكرة. C++ التعليمات لايت يتناول هذا أيضا.

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

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

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

إذا كنت تقوم بإلقاء الاستثناءات من مكون من مكونات، فسيتم استخدام المطورين الآخرين من النهر، فالرجاء القيام بها بشكل كبير ودائما دائما فصول الاستثناءات الخاصة بك (إذا كنت بحاجة فعلا إلى CF باستخدام الاستثناءات القياسية) من STD :: استثناء. تجنب كل تكاليف التكاليف المطلقة مثل رمي Ints، Hresults، Char *، STD :: سلسلة ...

كما كان قد قال بالفعل استخدامها لحالات استثنائية فقط.

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

public void DoSomethingWithFile() {
    if(!File.Exists(..))
        throw new FileNotFoundException();
}

تقديم طريقة أخرى للمستخدم للاتصال:

public bool CanDoSomething() {
    return File.Exists(..);
}

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

احتفظ أيضا باستثناء التسلسل الهرمي للفئة المسطحة وإلقاء نظرة على الاستثناءات القياسية مثل InvalidStateException و ArgumentNlessCpetion.

إليك مثال بسيط على إلقاء استثناء يأت بالكاد أي موارد:

class DivisionError {};
class Division
{
public:
    float Divide(float x, float y) throw(DivisionError)
    {
        float result = 0;
        if(y != 0)
            result = x/y;
        else
            throw DivisionError();
        return result;
    }
};

int main()
{
    Division d;
    try
    {
        d.Divide(10,0);
    }
    catch(DivisionError)
    {
        /*...error handling...*/
    }
}

الفئة الفارغة التي يتم طرحها لا تأخذ أي مورد أو عدد قليل جدا ...

من الأسئلة الشائعة C ++، 17.12] ماذا ألقي رمي؟:

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

...و

الممارسة الأكثر شيوعا هي رمي مؤقت: (انظر المثال التالي)

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