Có nên lấy / thừa kế từ std :: ngoại lệ không?


15

Trong khi thiết kế thư viện C ++ 'nghiêm túc' đầu tiên của mình, tôi tự hỏi:

Đó có phải là phong cách tốt để rút ra những ngoại lệ từ std::exceptionđó và đó là kết quả?!

Ngay cả sau khi đọc

Tôi vẫn không chắc chắn. Bởi vì, bên cạnh thực tiễn thông thường (nhưng có thể không tốt), tôi cho rằng, với tư cách là người sử dụng thư viện, chức năng thư viện sẽ chỉ ném std::exceptionkhi chức năng thư viện tiêu chuẩn thất bại trong việc triển khai thư viện và nó không thể làm gì được. Tuy nhiên, khi viết mã ứng dụng, đối với tôi, nó rất thuận tiện và IMHO cũng rất đẹp mắt std::runtime_error. Ngoài ra người dùng của tôi cũng có thể dựa vào giao diện tối thiểu được xác định, như what()hoặc mã.

Và ví dụ, người dùng của tôi cung cấp các đối số bị lỗi, điều gì sẽ thuận tiện hơn là ném một cái std::invalid_argument, phải không? Vì vậy, kết hợp với việc sử dụng phổ biến của std :: ngoại lệ tôi thấy trong mã khác: Tại sao không đi xa hơn và xuất phát từ lớp ngoại lệ tùy chỉnh của bạn (ví dụ lib_foo_exception) và cũng từ std::exception.

Suy nghĩ?


Tôi không chắc là tôi làm theo. Chỉ vì bạn thừa kế std::exceptionkhông có nghĩa là bạn ném a std::exception. Ngoài ra, std::runtime_errorkhông kế thừa từ std::exceptionnơi đầu tiên, và what()phương pháp đến từ std::exception, không std::runtime_error. Và bạn chắc chắn nên tạo các lớp ngoại lệ của riêng bạn thay vì ném các ngoại lệ chung chung như std::runtime_error.
Vincent Savard

3
Sự khác biệt là, khi lib_foo_exceptionlớp của tôi xuất phát std::exception, người dùng thư viện sẽ bắt lib_foo_exceptionbằng cách chỉ bắt std::exception, ngoài ra khi anh ta chỉ bắt được người duy nhất. Vì vậy, tôi cũng có thể hỏi lớp thư viện ngoại lệ thư viện của tôi có nên kế thừa từ std :: ngoại lệ không .
Superlokkus

3
@LightnessRacesinOrbit Ý tôi là "... ngoài", như "Có bao nhiêu cách để bắt lib_foo_exception?" Với kế thừa từ std::exceptionbạn có thể làm điều đó bằng cách catch(std::exception)OR catch(lib_foo_exception). Không xuất phát từ std::exception, bạn sẽ bắt nó nếu và chỉ khi , bởi catch(lib_foo_exception).
Superlokkus

2
@Superlokkus: Chúng tôi loại bỏ qua catch(...). Đó là bởi vì ngôn ngữ cho phép trong trường hợp bạn đang xem xét (và cho các thư viện "hoạt động sai"), nhưng đó không phải là cách thực hành tốt nhất hiện đại.
Cuộc đua nhẹ nhàng với Monica

1
Rất nhiều thiết kế xử lý ngoại lệ trong C ++ có xu hướng khuyến khích catchcác trang web thô hơn, tổng quát hơn và các giao dịch thô hơn tương tự mô hình hóa hoạt động của người dùng. Nếu bạn so sánh nó với các ngôn ngữ không thúc đẩy ý tưởng bắt chung chung std::exception&, ví dụ, chúng thường có nhiều mã hơn với các try/catchkhối trung gian liên quan đến các lỗi rất cụ thể, điều này phần nào làm giảm tính tổng quát của việc xử lý ngoại lệ khi nó bắt đầu diễn ra nhấn mạnh hơn nhiều vào việc xử lý lỗi thủ công và cả các lỗi khác nhau có thể xảy ra.

Câu trả lời:


29

Tất cả các ngoại lệ nên kế thừa từ std::exception.

Giả sử, ví dụ, tôi cần gọi ComplexOperationThatCouldFailABunchOfWays()và tôi muốn xử lý mọi trường hợp ngoại lệ mà nó có thể ném ra. Nếu mọi thứ kế thừa từ std::exception, điều này là dễ dàng. Tôi chỉ cần một catchkhối duy nhất và tôi có một giao diện chuẩn ( what()) để nhận thông tin chi tiết.

try {
    ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
    cerr << e.what() << endl;
}

Nếu ngoại lệ KHÔNG được thừa hưởng từ std::exception, điều này trở nên xấu hơn nhiều:

try {
    ComplexOperationThatCouldFailABunchOfWays();
} catch (std::exception& e) {
    cerr << e.what() << endl;
} catch (Exception& e) {
    cerr << e.Message << endl;
} catch (framework_exception& e) {
    cerr << e.Details() << endl;
}

Về việc ném runtime_errorhay invalid_argumentso với việc tạo các std::exceptionlớp con của riêng bạn để ném: Quy tắc ngón tay cái của tôi là giới thiệu một lớp con mới bất cứ khi nào tôi cần xử lý một loại lỗi cụ thể khác với các lỗi khác (nghĩa là, bất cứ khi nào tôi cần một catchkhối riêng ).

  • Nếu tôi giới thiệu một lớp con ngoại lệ mới cho mọi loại lỗi có thể hiểu được, ngay cả khi tôi không cần xử lý chúng một cách riêng biệt, thì điều đó sẽ thêm rất nhiều sự phổ biến của lớp.
  • Nếu tôi sử dụng lại các lớp con hiện tại có nghĩa là một cái gì đó cụ thể (nghĩa là, nếu runtime_errorném ở đây có nghĩa là một cái gì đó khác với lỗi thời gian chạy chung), thì tôi có nguy cơ xung đột với các cách sử dụng khác của lớp con hiện tại.
  • Nếu tôi không cần xử lý một lỗi cụ thể và nếu lỗi tôi ném chính xác khớp với một trong các lỗi của thư viện tiêu chuẩn hiện tại (chẳng hạn như invalid_argument), thì tôi sử dụng lại lớp hiện có. Tôi chỉ không thấy nhiều lợi ích khi thêm một lớp mới trong trường hợp này. (Nguyên tắc cốt lõi C ++ không đồng ý với tôi ở đây - họ khuyên bạn luôn luôn sử dụng các lớp của riêng bạn.)

Các ++ Hướng dẫn Lõi C có cuộc thảo luận và các ví dụ tiếp theo.


Tất cả những ký hiệu đó! C ++ thật kỳ lạ.
SuperJedi224

2
@ SuperJedi224 và tất cả những chữ cái khác nhau! Tiếng Anh thật kỳ lạ.
johannes

Lý do này không có ý nghĩa với tôi. Đây không phải là những gì catch (...)(với dấu chấm lửng theo nghĩa đen) là để làm gì?
Tối đa

1
@Maxpm catch (...)chỉ hữu ích nếu bạn không cần phải làm với những gì bị ném. Nếu bạn muốn làm một cái gì đó - ví dụ, hiển thị hoặc ghi nhật ký thông báo lỗi cụ thể, như trong ví dụ của tôi - thì bạn cần phải biết nó là gì.
Josh Kelley

9

Tôi cho rằng, với tư cách là người dùng thư viện, chức năng thư viện sẽ chỉ ném std :: ngoại lệ khi chức năng thư viện chuẩn bị lỗi khi triển khai thư viện và nó không thể làm gì được về nó

Đó là một giả định không chính xác.

Các loại ngoại lệ tiêu chuẩn được cung cấp để sử dụng "thường dân". Chúng không được thiết kế để chỉ được sử dụng bởi thư viện tiêu chuẩn.

Vâng, làm cho mọi thứ cuối cùng được thừa hưởng từ std::exception. Thông thường, điều đó sẽ liên quan đến việc thừa kế từ std::runtime_errorhoặc std::logic_error. Bất cứ điều gì phù hợp với lớp ngoại lệ bạn đang thực hiện.

Tất nhiên đây là chủ quan - một số thư viện phổ biến hoàn toàn bỏ qua các loại ngoại lệ tiêu chuẩn, có lẽ để tách các thư viện khỏi thư viện chuẩn. Cá nhân tôi nghĩ rằng điều này là vô cùng ích kỷ! Nó làm cho việc bắt ngoại lệ trở nên khó khăn hơn nhiều để có được quyền.

Nói cá nhân, tôi thường chỉ ném một std::runtime_errorvà được thực hiện với nó. Nhưng đó là đi vào một cuộc thảo luận về cách chi tiết để tạo các lớp ngoại lệ của bạn, đó không phải là điều bạn đang hỏi.

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.