2009-10-22 51 views
17

Tôi đã cố gắng tạo một số lớp ngoại lệ tùy chỉnh cho thư viện C++ mà tôi đang làm việc. Các ngoại lệ tùy chỉnh này nắm bắt thông tin bổ sung, chẳng hạn như tệp, số dòng, v.v., cần thiết để gỡ lỗi, nếu vì một lý do nào đó trong khi kiểm tra ngoại lệ không bị bắt ở đúng vị trí. Tuy nhiên hầu hết mọi người dường như đề nghị kế thừa từ std :: exception class trong STL, mà tôi đồng ý với, nhưng tôi đã tự hỏi có lẽ nó sẽ là tốt hơn để sử dụng nhiều thừa kế để kế thừa từ mỗi derived std::exception classes (ví dụ: std :: runtime_error) và một lớp ngoại lệ tùy chỉnh, như trong đoạn mã dưới đây?Ngoại lệ tùy chỉnh trong C++

Một điều khác, làm thế nào để một nhà thầu sao chép và các toán tử gán trong các lớp ngoại lệ? Họ có bị vô hiệu hóa không?

class Exception{ 
    public: 
     explicit Exception(const char *origin, const char *file, 
          const int line, const char *reason="", 
          const int errno=0) throw(); 

     virtual ~Exception() throw(); 

     virtual const char* PrintException(void) throw(); 

     virtual int GetErrno(void); 

    protected: 
     std::string m_origin; 
     std::string m_file; 
     int m_line; 
     std::string m_reason; 
     int m_errno; 
}; 

class RuntimeError: public virtual std::runtime_error, public Exception{ 
    public: 
       explicit RuntimeError(const char *origin, const char *file, 
            const int line, const char *reason="", 
            const int errno=0) throw(); 
     virtual ~RuntimeError() throw(); 
}; 
+0

Bạn sẽ đạt được điều gì từ điều này? Tại sao không sử dụng đơn thừa kế? Tại sao bạn cần tùy chỉnh * cả hai ngoại lệ ngoại lệ và ngoại lệ RuntimeError? – jalf

+0

Tôi muốn sử dụng chức năng từ lớp Ngoại lệ tùy chỉnh của mình và duy trì hệ thống phân cấp ngoại lệ STL bằng cách kế thừa từ các lớp ngoại lệ std :: ngoại lệ khác nhau. Bằng cách này, hãy bắt đầu với: thử { ném RunTimeException (...); } bắt (std :: runtime_error & e) { ... } bắt (std :: exception & e) {...} cũng sẽ bắt được lớp tùy chỉnh của tôi. Nó có thể là một sự lãng phí thời gian mặc dù. Tôi cũng có thể chỉ cần thử { ném RunTimeException (...); } bắt (std :: exception & e) {...} nhưng có thể khó xác định ngoại lệ sau đó. Tôi không biết thực hành điển hình là gì khi các lớp ngoại lệ tùy chỉnh? – Tom

+0

Tôi sẽ nói chỉ kế thừa từ std :: runtime_error (hoặc std :: exception). Nếu bạn muốn một điều trị cụ thể, bạn có thể có nhiều đánh bắt sau khi tất cả. –

Trả lời

11

Bạn nên cố gắng boost::exception

Mục đích của Boost ngoại lệ là dễ dàng thiết kế các lớp ngoại lệ phân cấp và để giúp viết xử lý ngoại lệ và báo cáo lỗi mã.

Nó hỗ trợ vận chuyển tùy ý dữ liệu đến các trang web bắt, đó là khác khó khăn do sự yêu cầu không ném (15.5.1) cho ngoại lệ loại. Dữ liệu có thể được thêm vào bất kỳ đối tượng ngoại lệ ngoại lệ nào, trực tiếp trong biểu thức ném (15.1) hoặc tại thời gian sau khi đối tượng ngoại lệ truyền bá ngăn xếp cuộc gọi.

Khả năng thêm dữ liệu vào ngoại lệ đối tượng sau khi họ đã được thông qua để ném là rất quan trọng, bởi vì thường một số các thông tin cần thiết để xử lý một ngoại lệ không có sẵn trong bối cảnh nơi sự thất bại được phát hiện.

Boost ngoại lệ cũng hỗ trợ sao chép N2179 kiểu của ngoại lệ đối tượng, thực hiện phi intrusively và tự động bằng chức năng boost :: throw_exception.

+0

Tôi chưa bao giờ sử dụng tăng trước đây. Tôi sẽ xem xét nó. Cảm ơn vì bài đăng. – Tom

+1

Thư viện tuyệt vời. Bây giờ tôi ước tôi đã đọc nó trước đây! –

17

tôi đã tự hỏi có lẽ nó sẽ là tốt hơn để sử dụng đa kế thừa kế thừa từ mỗi std :: nguồn gốc lớp ngoại lệ

Lưu ý rằng đây là một vấn đề, do thực tế rằng các ngoại lệ trong thư viện chuẩn bắt nguồn từ thực tế không phải từ nhau. Nếu bạn giới thiệu nhiều thừa kế, bạn có được phân cấp ngoại lệ kim cương đáng sợ mà không cần thừa kế ảo và sẽ không thể bắt được ngoại lệ có nguồn gốc từ std::exception&, vì lớp ngoại lệ có nguồn gốc của bạn mang hai subobject của std::exception, làm cho một lớp cơ sở không rõ ràng "#".

dụ bê tông:

class my_exception : virtual public std::exception { 
    // ... 
}; 

class my_runtime_error : virtual public my_exception 
         , virtual public std::runtime_error { 
    // ... 
}; 

Bây giờ my_runtime_error Xuất phát (gián tiếp) từ std::exception hai lần, một lần qua std::run_time_error và một lần qua my_exception.Vì trước đây không xuất phát từ std::exception hầu như, điều này

try { 
    throw my_runtime_error(/*...*/); 
} catch(const std::exception& x) { 
    // ... 
} 

sẽ không hoạt động.

Edit:

Tôi nghĩ rằng tôi đã nhìn thấy ví dụ đầu tiên của một hệ thống phân cấp ngoại lệ liên quan đến lớp MI trong một cuốn sách Stroustrup của, vì vậy tôi kết luận rằng, nói chung, đó là một ý tưởng tốt. Đó là ngoại lệ của std lib không lấy được hầu như từ mỗi khác tôi coi là một thất bại.

Khi tôi thiết kế lần cuối một hệ thống phân cấp ngoại lệ, tôi đã sử dụng MI rất rộng rãi, nhưng không lấy được từ các lớp ngoại lệ của std lib. Trong phân cấp đó, có các lớp ngoại lệ trừu tượng mà bạn đã định nghĩa để người dùng của bạn có thể bắt chúng và các lớp triển khai tương ứng, bắt nguồn từ các lớp trừu tượng này và từ lớp cơ sở triển khai mà bạn thực sự sẽ ném. Để thực hiện dễ dàng hơn này, tôi xác định một số mẫu mà sẽ làm tất cả những công việc khó khăn:

// something.h 
class some_class { 
private: 
    DEFINE_TAG(my_error1); // these basically define empty structs that are needed to 
    DEFINE_TAG(my_error2); // distinguish otherwise identical instances of the exception 
    DEFINE_TAG(my_error3); // templates from each other (see below) 
public: 
    typedef exc_interface<my_error1> exc_my_error1; 
    typedef exc_interface<my_error2> exc_my_error2; 
    typedef exc_interface<my_error3,my_error2> // derives from the latter 
            exc_my_error3; 

    some_class(int i); 
    // ... 
}; 

//something.cpp 
namespace { 
    typedef exc_impl<exc_my_error1> exc_impl_my_error1; 
    typedef exc_impl<exc_my_error2> exc_impl_my_error2; 
    typedef exc_impl<exc_my_error3> exc_impl_my_error3; 
    typedef exc_impl<exc_my_error1,exc_my_error2> // implements both 
            exc_impl_my_error12; 
} 
some_class::some_class(int i) 
{ 
    if(i < 0) 
    throw exc_impl_my_error3(EXC_INFO // passes '__FILE__', '__LINE__' etc. 
          , /* ... */ // more info on error 
          ); 
} 

Nhìn lại này ngay bây giờ, tôi nghĩ rằng tôi có thể thực hiện mà exc_impl lớp mẫu lấy từ std::exception (hoặc bất kỳ lớp khác trong std lib exception hierarchy, được chuyển thành tham số mẫu tùy chọn), vì nó không bao giờ xuất phát từ bất kỳ cá thể exc_impl nào khác. Nhưng sau đó điều này là không cần thiết, vì vậy nó không bao giờ xảy ra với tôi.

+0

+1: khá giải thích – fmuecke

+0

Cảm ơn bài đăng sbi. Làm cho các lớp học ngoại lệ với nhiều thừa kế là một thực hành tốt? Hoặc là nó tốt hơn chỉ để kế thừa từ std :: lớp ngoại lệ? – Tom

+0

@Tom: Tôi đã thêm suy nghĩ của mình làm bản chỉnh sửa. – sbi

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