2011-07-05 56 views
10

Khi làm việc với C# hoặc Java, tôi đã sử dụng để tạo các lớp ngoại lệ bao gồm các ngoại lệ khác với tư cách thành viên lớp. Ví dụ:Ngoại lệ trong các trường hợp ngoại lệ trong C++

public class MyException : Exception { 
    private MyException ex; 
    private String message; 

    public String what() { 
     return this.message; 
    } 

    public String stack() { 
     if(ex != null) { 
      StringBuilder s; 
      s.append(this.what()); 
      s.append(this.ex.stack()); 
      return s.toString(); 
     } 

     return this.what(); 
    } 
} 

Tôi đang tìm các ví dụ về cùng một chủ đề nhưng đối với C++ và tôi không thể tìm thấy (có thể tôi không tìm kiếm đúng cụm từ, bạn có thể thấy tiêu đề của câu hỏi này không phải là rất ưa thích).

Dù sao, cách chính xác để thực hiện điều đó trong C++ là gì? Là nó để lưu trữ các ngoại lệ bên trong như một con trỏ hoặc một tài liệu tham khảo? (Tôi nghĩ rằng tôi có thể cần một con trỏ để nó có thể là null khi nó là ngoại lệ đầu tiên). Khi tôi đưa ra ngoại lệ, nó có phải là con trỏ được tạo với new không?

EDIT: Có thể những gì tôi đã viết là một chút bối rối hoặc không phải là một thực hành lập trình nổi tiếng (được chấp nhận). Vì vậy, tôi sẽ chỉ định làm thế nào tôi có ý định sử dụng lớp này với một đoạn:

try { 
    // Code that throws an exception of type MyException 
} catch(MyException ex) { 
    MyException another = new MyException(); 
    another.setEx(ex); 
    another.setMessage("A message that explains where the other exception was caught and on what conditions"); 

    throw another; 
} 
+3

bạn không thể có nhiều ngoại lệ trong C++. Nếu một ngoại lệ mới được ném trước khi ngoại lệ hiện tại bị bắt, chương trình được chấm dứt (chính xác hơn là 'std :: terminate' được gọi) –

+0

@Gene: Đây là một biến thể về rethrow, nơi ngoại lệ hiện tại được nhồi trong một ngoại lệ mới . Có lẽ không khả thi nếu không thu gom rác thải. –

+0

@Ben Voigt: Có thể khả thi nếu không có bộ sưu tập rác nếu không khó nhớ chính xác khi nào các trường hợp ngoại lệ được gọi là destructors. –

Trả lời

0

Tôi không nghĩ rằng chúng ta cần một tài sản lớp dụ (ví dụ ex) trong Java/C#/C++ nếu lớp học của bạn không được thiết kế trong mẫu đơn. Ngoài ra, here là một hướng dẫn mà bạn có thể muốn xem.

+0

Thành viên lớp 'MyException' được sử dụng như sau: 1. Tôi bắt ngoại lệ được ném 2. Tôi khởi tạo ** ngoại lệ ** khác với thông báo cụ thể hơn và đặt thuộc tính' ex' thành ngoại lệ bị bắt trong 1 3. Ném ngoại lệ mới được tạo ra. Đó là một hướng dẫn tốt đẹp mặc dù, một trong những bạn đã gửi cho tôi. – Renan

+0

Có vẻ như bạn muốn giữ nó để tham khảo lại, phải không? Tại sao nó không phải là một ví dụ 'Exception'? Bên cạnh đó, nếu bạn chỉ muốn một tham chiếu trở lại để giữ ngoại lệ ban đầu, tại sao bạn không chỉ đơn giản là ngăn xếp các thông điệp dấu vết và ném ngoại lệ ban đầu đi? Có lẽ trong constructor? Nó có vẻ lý do hơn với tôi. 2 xu của tôi – shinkou

21

Không có cách nào đúng để làm điều đó với các ngoại lệ tiêu chuẩn trong C++ 03 vì chúng được thiết kế để sử dụng đa hình nhưng không thể nhân bản. Vì vậy, nếu bạn bắt giữ std::exception const& e bạn có thể lưu trữ một bản sao nhưng điều này sẽ dẫn đến việc cắt, mất tất cả thông tin hữu ích. Bạn nên không lưu trữ một con trỏ hoặc tham chiếu đến ngoại lệ vì thời gian tồn tại của nó sẽ kết thúc ngay sau khi rời khỏi mệnh đề catch (giả sử bạn không trả lại ngoại lệ ban đầu).

Bạn có thể vượt qua giới hạn này nếu bạn biết từng loại có thể được ném và thử nghiệm tất cả nhưng đó không phải là thiết kế đẹp (tức là nó chuyển đổi đa hình). Nó có ý nghĩa hơn để viết một lớp ngoại lệ cơ bản của riêng bạn mà có thể được nhân bản và bắt nó. Tuy nhiên, bạn vẫn gặp sự cố nếu bạn bắt gặp một số std::exception đến từ mã của người khác.

Tại thời điểm này, tôi cảm thấy bắt buộc phải đề cập đến Boost.Exception. Nó làm cho nó dễ dàng để viết hệ thống phân cấp ngoại lệ của riêng bạn và cung cấp các tiện ích khác nhau, trong số đó boost::exception_ptr. Sau đó bạn có thể làm:

typedef boost::error_info<struct tag_nested_exception, boost::exception_ptr> 
    nested_exception; 

// ... 
catch(...) { 
    // better: use BOOST_THROW_EXCEPTION 
    throw your_exception_type() << nested_exception(boost::current_exception()); 
} 

này rất hữu ích mà boost::diagnostic_info hỗ trợ nó và sẽ hiển thị ngoại trừ lồng nhau cho bạn (đó là không có giấy tờ). Thậm chí còn được đề xuất rằng nested_exceptiontypedef này cũng phải là một phần của thư viện; trong khi đó, thật dễ dàng để tự viết nó.

Đừng mong đợi phép thuật: boost::current_exception 'chụp' ngoại lệ hoạt động (chữ in hoa: hoặc bản sao của nó) chỉ phạt nếu trang web ném sử dụng boost::enable_current_exception. (Về mặt chức năng, đây là tương đương về mặt đạo đức của việc sử dụng một lớp ngoại lệ cơ bản có thể được nhân bản). Nếu không, nó sẽ không thất bại nhưng một số thông tin có thể bị mất.


Lưu ý cuối cùng, biết rằng thiết kế Boost.Exception được chấp nhận cho C++ 0x.Do đó, sau một cách chính xác lưu trữ các ngoại lệ hoạt động, với không có boost::current_exception hãy cẩn thận vì nó có hỗ trợ ngôn ngữ:

// you can still use Boost.Exception: 
typedef boost::error_info<struct tag_nested_exception, std::exception_ptr> 
    nested_exception; 

// ... 
catch(...) { 
    // e has type std::exception_ptr 
    auto e = std::current_exception(); 
    // internally store the std::exception_ptr 
    throw your_exception_type(e); 

    // or with Boost 
    BOOST_THROW_EXCEPTION(your_exception_type() << nested_exception(e)); 
} 

Ngoài ra còn có một loại std::nested_exception rằng có thể dễ dàng được sử dụng như sau:

catch(...) { 
    // throws an unspecified type derived from your_exception_type 
    // and std::nested_exception 
    std::throw_with_nested(your_exception_type()); 
} 
+0

Cảm ơn bạn đã đứng đầu, C++ 0x giữ tôi ngạc nhiên :) –

1

Tôi đoán bạn có thể ném unique_ptr<some_exception>, sau đó thực hiện inner_exception một unique_ptr khác có quyền sở hữu.

+0

Và bạn phải đảm bảo rằng chỉ con trỏ đến cùng một lớp cơ sở được ném bởi vì điều này không hoạt động đa hình. –

+0

@Luc: Đó chắc chắn là một nhược điểm, phải không? –

+0

Vâng, cho rằng 'std :: unique_ptr' là C++ 0x, tôi nghĩ rằng có lựa chọn thay thế tốt hơn. –

Các vấn đề liên quan