استثناءات C ++ أسئلة حول إعادة صياغة الاستثناء الأصلي

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

  •  23-09-2019
  •  | 
  •  

سؤال

هل سيتسبب الإلحاق التالي () في الصيد في استثناء إعادة التثبيت لمعرفة تأثير الإلحاح () الذي يتم استدعاؤه؟

try {
  mayThrowMyErr();
} catch (myErr &err) {
  err.append("Add to my message here");
  throw; // Does the rethrow exception reflect the call to append()?
}

وبالمثل ، إذا قمت بإعادة كتابتها بهذه الطريقة ، فهل سيحدث تقطيع بت إذا كان الاستثناء الفعلي مشتقًا من قبل Myerr؟

try {
  mayThrowObjectDerivedFromMyErr();
} catch (myErr &err) {
  err.append("Add to my message's base class here");
  throw err; // Do I lose the derived class exception and only get myErr?
}
هل كانت مفيدة؟

المحلول

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

  1. في الحالة الأولى ، بما أنك تتجه إلى throw; (الذي ، على عكس throw err;, يحافظ على كائن الاستثناء الأصلي ، مع تعديلاتك ، في "الموقع السحري" في 0x98e7058) إرادة تعكس الدعوة إلى إلحاق ()
  2. في الحالة الثانية ، نظرًا لأنك ترمي شيئًا صريحًا ، أ ينسخ من err سيتم إنشاء ثم رمي من جديد (في "موقع سحري" مختلف 0x98e70b0 - لأنه بالنسبة لجميع المترجم يعرف err يمكن أن يكون كائنًا على المكدس على وشك أن يكون غير معروف ، مثل e كان في 0xbfbce430, ، وليس في "الموقع السحري" في 0x98e7058)، لذا سوف تفقد بيانات خاصة بالفئة المشتقة أثناء عملية بناء نسخة من الفئة الأساسية.

برنامج بسيط لتوضيح ما يحدث:

#include <stdio.h>

struct MyErr {
  MyErr() {
    printf("  Base default constructor, this=%p\n", this);
  }
  MyErr(const MyErr& other) {
    printf("  Base copy-constructor, this=%p from that=%p\n", this, &other);
  }
  virtual ~MyErr() {
    printf("  Base destructor, this=%p\n", this);
  }
};

struct MyErrDerived : public MyErr {
  MyErrDerived() {
    printf("  Derived default constructor, this=%p\n", this);
  }
  MyErrDerived(const MyErrDerived& other) {
    printf("  Derived copy-constructor, this=%p from that=%p\n", this, &other);
  }
  virtual ~MyErrDerived() {
    printf("  Derived destructor, this=%p\n", this);
  }
};

int main() {
  try {
    try {
      MyErrDerived e;
      throw e;
    } catch (MyErr& err) {
      printf("A Inner catch, &err=%p\n", &err);
      throw;
    }
  } catch (MyErr& err) {
    printf("A Outer catch, &err=%p\n", &err);
  }
  printf("---\n");
  try {
    try {
      MyErrDerived e;
      throw e;
    } catch (MyErr& err) {
      printf("B Inner catch, &err=%p\n", &err);
      throw err;
    }
  } catch (MyErr& err) {
    printf("B Outer catch, &err=%p\n", &err);
  }
  return 0;
}

نتيجة:

  Base default constructor, this=0xbfbce430
  Derived default constructor, this=0xbfbce430
  Base default constructor, this=0x98e7058
  Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
  Derived destructor, this=0xbfbce430
  Base destructor, this=0xbfbce430
A Inner catch, &err=0x98e7058
A Outer catch, &err=0x98e7058
  Derived destructor, this=0x98e7058
  Base destructor, this=0x98e7058
---
  Base default constructor, this=0xbfbce430
  Derived default constructor, this=0xbfbce430
  Base default constructor, this=0x98e7058
  Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
  Derived destructor, this=0xbfbce430
  Base destructor, this=0xbfbce430
B Inner catch, &err=0x98e7058
  Base copy-constructor, this=0x98e70b0 from that=0x98e7058
  Derived destructor, this=0x98e7058
  Base destructor, this=0x98e7058
B Outer catch, &err=0x98e70b0
  Base destructor, this=0x98e70b0

انظر أيضا:

نصائح أخرى

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

يستخدم std::nested_exception و std::throw_with_nested

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

نظرًا لأنه يمكنك القيام بذلك مع أي فئة استثناء مشتقة ، يمكنك إضافة الكثير من المعلومات إلى مثل هذا الخلفية! يمكنك أيضًا إلقاء نظرة على بلدي MWE على جيثب, ، حيث يبدو أن الخلفية يبدو مثل هذا:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

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

للسؤال الأول ، نعم.

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

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