Haruskah seseorang memperoleh / mewarisi dari std :: exception?

15

Saat mendesain pustaka C ++ 'serius' pertama saya, saya bertanya pada diri sendiri:

Apakah gaya yang baik untuk mendapatkan pengecualian dari std::exceptiondan keturunannya?

Bahkan setelah membaca

Aku masih tidak yakin. Karena, selain praktik umum (tapi mungkin tidak baik), saya akan berasumsi, sebagai pengguna perpustakaan, bahwa fungsi perpustakaan akan melempar std::exceptionhanya ketika fungsi perpustakaan standar gagal dalam implementasi perpustakaan, dan tidak dapat berbuat apa-apa. Tapi tetap saja, ketika menulis kode aplikasi, bagi saya itu sangat nyaman, dan juga IMHO tampan hanya melempar std::runtime_error. Selain itu, pengguna saya juga dapat mengandalkan antarmuka minimum yang ditentukan, seperti what()atau kode.

Dan misalnya, pengguna saya memberikan argumen yang salah, apa yang lebih nyaman, daripada melempar std::invalid_argument, bukan? Jadi dikombinasikan dengan penggunaan std :: exception yang umum saya lihat di kode lain: Mengapa tidak melangkah lebih jauh dan berasal dari kelas pengecualian khusus Anda (mis. Lib_foo_exception) dan juga dari std::exception.

Pikiran?

Superlokkus
sumber
Saya tidak yakin saya mengikuti. Hanya karena Anda mewarisi dari std::exceptiontidak berarti Anda membuang sebuah std::exception. Juga, std::runtime_errorapakah mewarisi dari std::exceptionsejak awal, dan what()metode itu berasal dari std::exception, bukan std::runtime_error. Dan Anda harus membuat kelas pengecualian sendiri alih-alih melemparkan pengecualian umum seperti std::runtime_error.
Vincent Savard
3
Perbedaannya adalah, bahwa ketika lib_foo_exceptionkelas saya berasal std::exception, pengguna perpustakaan akan menangkap lib_foo_exceptionhanya dengan menangkap std::exception, selain ketika ia hanya menangkap perpustakaan. Jadi saya juga bisa bertanya Haruskah perpustakaan saya pengecualian kelas root mewarisi dari std :: exception .
Superlokkus
3
@LightnessRacesinOrbit yang saya maksud "... selain", seperti "Berapa banyak cara yang ada untuk menangkap lib_foo_exception?" Dengan mewarisi dari std::exceptionAnda dapat melakukannya dengan catch(std::exception)ATAU dengan catch(lib_foo_exception). Tanpa berasal dari std::exception, Anda akan menangkapnya jika dan hanya jika , oleh catch(lib_foo_exception).
Superlokkus
2
@Superlokkus: Kami agak mengabaikan catch(...). Itu ada di sana karena bahasa tersebut memungkinkan untuk kasus yang Anda pertimbangkan (dan untuk perpustakaan "nakal"), tapi itu bukan praktik terbaik modern.
Lightness Races with Monica
1
Banyak desain penanganan pengecualian di C ++ cenderung mendorong situs yang lebih kasar, lebih umum catch, dan juga transaksi yang lebih kasar yang memodelkan operasi pengguna akhir. Jika Anda membandingkannya dengan bahasa yang tidak mempromosikan gagasan penangkapan secara umum std::exception&, misalnya, mereka sering memiliki lebih banyak kode dengan try/catchblok perantara yang berkaitan dengan kesalahan yang sangat spesifik, yang agak mengurangi generalisasi penanganan pengecualian karena mulai terjadi. penekanan yang jauh lebih kuat pada penanganan kesalahan manual, dan juga pada semua kesalahan berbeda yang mungkin terjadi.

Jawaban:

29

Semua pengecualian harus diwarisi dari std::exception.

Misalkan, misalnya, saya perlu menelepon ComplexOperationThatCouldFailABunchOfWays(), dan saya ingin menangani pengecualian yang dapat dilemparkan. Jika semuanya mewarisi std::exception, ini mudah. Saya hanya perlu satu catchblok, dan saya memiliki antarmuka standar ( what()) untuk mendapatkan detail.

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

Jika pengecualian TIDAK mewarisi dari std::exception, ini menjadi jauh lebih buruk:

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

Mengenai apakah akan melempar runtime_erroratau invalid_argumentmenentang membuat std::exceptionsubclass Anda sendiri untuk dilempar: Aturan praktis saya adalah memperkenalkan subclass baru setiap kali saya perlu menangani jenis kesalahan tertentu secara berbeda dari kesalahan lainnya (yaitu, setiap kali saya membutuhkan catchblok terpisah ).

  • Jika saya memperkenalkan subkelas pengecualian baru untuk setiap jenis kesalahan yang mungkin, bahkan jika saya tidak perlu menanganinya secara terpisah, maka itu menambah banyak perkembangbiakan kelas.
  • Jika saya menggunakan kembali subclass yang ada untuk mengartikan sesuatu yang spesifik (yaitu, jika yang runtime_errordilemparkan di sini berarti sesuatu yang berbeda dari kesalahan runtime generik), maka saya menjalankan risiko konflik dengan penggunaan lain dari subclass yang ada.
  • Jika saya tidak perlu menangani kesalahan secara khusus, dan jika kesalahan yang saya lemparkan persis sama dengan salah satu kesalahan pustaka standar yang ada (seperti invalid_argument), maka saya menggunakan kembali kelas yang ada. Saya hanya tidak melihat banyak manfaat dari menambahkan kelas baru dalam kasus ini. (Pedoman Inti C ++ tidak setuju dengan saya di sini - mereka merekomendasikan untuk selalu menggunakan kelas Anda sendiri.)

The C ++ Pedoman Inti memiliki diskusi lebih lanjut dan contoh.

Josh Kelley
sumber
Semua ampersand itu! C ++ aneh.
SuperJedi224
2
@ SuperJedi224 dan semua surat yang berbeda itu! Bahasa Inggris itu aneh.
johannes
Dasar pemikiran ini tidak masuk akal bagi saya. Bukankah ini untuk catch (...)(dengan elipsis literal) untuk apa?
Maks.
1
@ Maxm catch (...)hanya berguna jika Anda tidak perlu melakukan apa pun dengan apa yang dilemparkan. Jika Anda ingin melakukan sesuatu - misalnya, tampilkan atau catat pesan kesalahan tertentu, seperti dalam contoh saya - maka Anda perlu tahu apa itu.
Josh Kelley
9

Saya akan berasumsi, sebagai pengguna perpustakaan, bahwa fungsi perpustakaan akan melempar std :: pengecualian hanya ketika fungsi perpustakaan standar gagal dalam implementasi perpustakaan, dan itu tidak bisa berbuat apa-apa

Itu anggapan yang salah.

Jenis pengecualian standar disediakan untuk penggunaan "biasa". Mereka tidak dirancang untuk hanya digunakan oleh perpustakaan standar.

Ya, jadikan segalanya sebagai warisan std::exception. Seringkali, itu akan melibatkan pewarisan dari std::runtime_erroratau std::logic_error. Apa pun yang sesuai untuk kelas pengecualian yang Anda laksanakan.

Ini tentu saja subyektif - beberapa perpustakaan populer sepenuhnya mengabaikan jenis pengecualian standar, mungkin untuk memisahkan perpustakaan dari perpustakaan standar. Secara pribadi saya pikir ini sangat egois! Itu membuat pengecualian menangkap bahwa jauh lebih sulit untuk mendapatkan yang benar.

Berbicara secara pribadi, saya sering hanya melempar std::runtime_errordan selesai dengan itu. Tapi itu masuk ke diskusi tentang bagaimana granular membuat kelas pengecualian Anda, yang bukan itu yang Anda tanyakan.

Lightness Races with Monica
sumber