Saat mendesain pustaka C ++ 'serius' pertama saya, saya bertanya pada diri sendiri:
Apakah gaya yang baik untuk mendapatkan pengecualian dari std::exception
dan keturunannya?
Bahkan setelah membaca
- Merancang kelas pengecualian
- Apa yang dimaksud dengan 'jumlah yang baik' untuk diterapkan untuk perpustakaan saya?
Aku masih tidak yakin. Karena, selain praktik umum (tapi mungkin tidak baik), saya akan berasumsi, sebagai pengguna perpustakaan, bahwa fungsi perpustakaan akan melempar std::exception
hanya 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?
sumber
std::exception
tidak berarti Anda membuang sebuahstd::exception
. Juga,std::runtime_error
apakah mewarisi daristd::exception
sejak awal, danwhat()
metode itu berasal daristd::exception
, bukanstd::runtime_error
. Dan Anda harus membuat kelas pengecualian sendiri alih-alih melemparkan pengecualian umum sepertistd::runtime_error
.lib_foo_exception
kelas saya berasalstd::exception
, pengguna perpustakaan akan menangkaplib_foo_exception
hanya dengan menangkapstd::exception
, selain ketika ia hanya menangkap perpustakaan. Jadi saya juga bisa bertanya Haruskah perpustakaan saya pengecualian kelas root mewarisi dari std :: exception .lib_foo_exception
?" Dengan mewarisi daristd::exception
Anda dapat melakukannya dengancatch(std::exception)
ATAU dengancatch(lib_foo_exception)
. Tanpa berasal daristd::exception
, Anda akan menangkapnya jika dan hanya jika , olehcatch(lib_foo_exception)
.catch(...)
. Itu ada di sana karena bahasa tersebut memungkinkan untuk kasus yang Anda pertimbangkan (dan untuk perpustakaan "nakal"), tapi itu bukan praktik terbaik modern.catch
, dan juga transaksi yang lebih kasar yang memodelkan operasi pengguna akhir. Jika Anda membandingkannya dengan bahasa yang tidak mempromosikan gagasan penangkapan secara umumstd::exception&
, misalnya, mereka sering memiliki lebih banyak kode dengantry/catch
blok 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:
Semua pengecualian harus diwarisi dari
std::exception
.Misalkan, misalnya, saya perlu menelepon
ComplexOperationThatCouldFailABunchOfWays()
, dan saya ingin menangani pengecualian yang dapat dilemparkan. Jika semuanya mewarisistd::exception
, ini mudah. Saya hanya perlu satucatch
blok, dan saya memiliki antarmuka standar (what()
) untuk mendapatkan detail.Jika pengecualian TIDAK mewarisi dari
std::exception
, ini menjadi jauh lebih buruk:Mengenai apakah akan melempar
runtime_error
atauinvalid_argument
menentang membuatstd::exception
subclass 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 membutuhkancatch
blok terpisah ).runtime_error
dilemparkan di sini berarti sesuatu yang berbeda dari kesalahan runtime generik), maka saya menjalankan risiko konflik dengan penggunaan lain dari subclass yang ada.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.
sumber
catch (...)
(dengan elipsis literal) untuk apa?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.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 daristd::runtime_error
ataustd::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_error
dan selesai dengan itu. Tapi itu masuk ke diskusi tentang bagaimana granular membuat kelas pengecualian Anda, yang bukan itu yang Anda tanyakan.sumber