تسريح الاستثناء - هل هذا بسبب إنشاء منشئ نسخة؟

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

  •  11-09-2019
  •  | 
  •  

سؤال

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

إليك فئة الاستثناءات الأساسية لدينا، فئة مشتقة، ووظائف ذات صلة:

class Exception
{
public:
  // construction
  Exception(int code, const char* format="", ...);
  virtual ~Exception(void);

  <snip - get/set routines and print function>

protected:
private:
  int mCode;                // thrower sets this
  char mMessage[Exception::MessageLen]; // thrower says this FIXME: use String
};

class Derived : public Exception {
public:
  Derived (const char* throwerSays) : Exception(1, throwerSays) {};
};

void innercall {
  <do stuff>
  throw Derived("Bad things happened!");
}

void outercall {
  try {
    innercall();
  }
  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw e;
  }
}

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

الآن، أريد فقط التأكد من أنني أفهم - أعتقد أنه في خط "رمي E"، يتم إنشاء كائن استثناء جديد، باستخدام منشئ نسخ افتراضي. هل هذا ما يحدث حقا؟

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

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

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

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

المحلول

عند رمي كائن، فأنت في الواقع رمي نسخة من الكائن، وليس الأصلي. فكر في الأمر - الكائن الأصلي موجود على المكدس، ولكن المكدس غير مناسب وإبطال.

أعتقد أن هذا جزء من المعيار، لكن ليس لدي نسخة للرجوع إليها.

نوع الاستثناء الذي يتم إلقاؤه في كتلة الصيد هو النوع الأساسي للقبض، وليس نوع الكائن الذي ألقيته. الطريق حول هذه المشكلة هو throw; بدلا من throw e; والتي سوف ترمي استثناء القبض الأصلي.

نصائح أخرى

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

على أي حال، فقط استخدام throw دون تحديد كائن الاستثناء، إلى Rethrow ما تم القبض عليه في catch. وبعد لا ينبغي أن يحل المشكلة؟

  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw;
  }

نعم.

throw e;

يلقي استثناء من ثابتة نوع من e, ، بغض النظر عن أي شيء e في الواقع. في هذه الحالة، Derived يتم نسخ الاستثناء إلى Exception باستخدام منشئ نسخة.

في هذه الحالة يمكنك فقط

throw;

للحصول على Derived فقاعة الاستثناء بشكل صحيح.

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

C ++ لا تتوقف أبدا عن تدهش لي. كنت قد فقدت الكثير من المال كان هذا يراهن على ما كان السلوك!

يتم نسخ كائن الاستثناء لأول مرة إلى مؤقت ويجب أن تستخدم throw. وبعد إلى اقتباس المعيار 15.1 / 3:

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

أعتقد أن هذا يؤدي إلى قاعدة قياسية مفيدة للغاية:

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

أو

يجب حماية منشئ النسخ للفئة الأساسية في التسلسل الهرمي استثناء.

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

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