Câu hỏi về ngoại lệ trong C ++ về việc lặp lại ngoại lệ ban đầu


117

Liệu append () sau đây có gây ra ngoại lệ rethrown để xem hiệu ứng của append () được gọi không?

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

Tương tự, nếu tôi viết lại nó theo cách này, liệu việc cắt bit có xảy ra không nếu ngoại lệ thực sự được dẫn xuất bởi 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?
}

Câu trả lời:


150

Trong cả hai trường hợp, vì bạn nắm bắt bằng cách tham chiếu, bạn đang thay đổi hiệu quả trạng thái của đối tượng ngoại lệ ban đầu (mà bạn có thể coi là nằm trong một vị trí bộ nhớ ma thuật sẽ vẫn có giá trị trong lần mở tua tiếp theo - 0x98e7058trong ví dụ bên dưới). Tuy nhiên,

  1. Trong trường hợp đầu tiên, vì bạn rethrow với throw;(mà, không giống như throw err;, giữ nguyên đối tượng ngoại lệ ban đầu, với các sửa đổi của bạn, trong "vị trí huyền diệu" tại 0x98e7058) sẽ phản ánh lệnh gọi append ()
  2. Trong trường hợp thứ hai, vì bạn ném một thứ gì đó một cách rõ ràng, một bản sao của errsẽ được tạo sau đó được ném lại (tại một "vị trí phép thuật" khác 0x98e70b0- bởi vì đối với tất cả những gì mà trình biên dịch biết errcó thể là một đối tượng trên ngăn xếp sắp được giải nén, như elà tại 0xbfbce430, không phải ở "vị trí huyền diệu" tại 0x98e7058), vì vậy bạn sẽ mất dữ liệu dành riêng cho lớp dẫn xuất trong quá trình sao chép xây dựng một cá thể lớp cơ sở.

Chương trình đơn giản để minh họa những gì đang xảy ra:

#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;
}

Kết quả:

  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ũng thấy:


24

Câu hỏi này khá cũ và có câu trả lời phù hợp với thời điểm nó được hỏi. Tuy nhiên, tôi chỉ muốn thêm một lưu ý về cách xử lý ngoại lệ thích hợp kể từ C ++ 11 và tôi tin rằng điều này rất phù hợp với những gì bạn đang cố gắng đạt được với hàm append của mình:

Sử dụng std::nested_exceptionstd::throw_with_nested

Nó được mô tả trên StackOverflow tại đâytại đây , cách bạn có thể nhận được dấu vết về các trường hợp ngoại lệ của mình bên trong mã của mình mà không cần trình gỡ lỗi hoặc ghi nhật ký rườm rà, bằng cách viết một trình xử lý ngoại lệ thích hợp sẽ tạo lại các ngoại lệ lồng nhau.

Vì bạn có thể làm điều này với bất kỳ lớp ngoại lệ dẫn xuất nào, bạn có thể thêm nhiều thông tin vào một backtrace như vậy! Bạn cũng có thể xem qua MWE của tôi trên GitHub , nơi mà backtrace sẽ trông giống như sau:

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"

8

Có, thao tác ném lại sẽ ném lại đối tượng ngoại lệ ban đầu mà bạn đã sửa đổi bằng một tham chiếu. Bạn cũng có thể bắt một tham chiếu lớp cơ sở, sửa đổi nó và vẫn có thể ném lại kiểu ngoại lệ dẫn xuất ban đầu bởi throw;.


1

cho câu hỏi đầu tiên, có.

nhưng thứ hai, hãy tham khảo câu trả lời của Vlad. bạn sẽ cần thiết kế cẩn thận đối tượng ngoại lệ của mình để xử lý ctor sao chép. theo quy ước, lớp cơ sở không nhận ra con của nó, vì vậy bạn rất có thể sẽ mất dữ liệu bổ sung do lớp dẫn xuất mang theo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.