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.
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
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
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ả. –