Pertama, seperti orang lain telah menyatakan, hal-hal yang tidak yang jelas dipotong di C ++, IMHO terutama karena persyaratan dan pembatasan yang agak lebih bervariasi dalam C ++ dari bahasa lain, esp. C # dan Java, yang memiliki masalah pengecualian "mirip".
Saya akan mengungkap contoh std :: stof:
meneruskan string kosong ke std :: stof (akan membuang invalid_argument) bukan kesalahan pemrograman
Dasar kontrak , seperti yang saya lihat, dari fungsi ini adalah bahwa ia mencoba untuk mengkonversi argumen itu untuk pelampung, dan setiap kegagalan untuk melakukannya dilaporkan oleh pengecualian. Kedua pengecualian yang mungkin berasal dari logic_error
tetapi tidak dalam arti kesalahan programer tetapi dalam arti "input tidak dapat, pernah dikonversi menjadi float".
Di sini, dapat dikatakan bahwa a logic_error
digunakan untuk menunjukkan bahwa, mengingat bahwa (runtime) input, selalu merupakan kesalahan untuk mencoba mengubahnya - tetapi tugas fungsi untuk menentukan itu dan memberi tahu Anda (melalui pengecualian).
Catatan: Dalam pandangan itu, a runtime_error
dapat dilihat sebagai sesuatu yang, diberikan input yang sama ke suatu fungsi, secara teoritis dapat berhasil untuk berbagai proses. (mis. operasi file, akses DB, dll.)
Catatan Sisi Lebih Lanjut: Pustaka regex C ++ memilih untuk menurunkan kesalahan itu dari runtime_error
meskipun ada kasus di mana ia dapat diklasifikasikan sama seperti di sini (pola regex tidak valid).
Ini hanya menunjukkan, IMHO, bahwa pengelompokan ke dalam logic_
atau runtime_
kesalahan cukup kabur di C ++ dan tidak terlalu banyak membantu dalam kasus umum (*) - jika Anda perlu menangani kesalahan tertentu, Anda mungkin perlu menangkap lebih rendah dari keduanya.
(*): Itu bukan untuk mengatakan bahwa sepotong kode tidak boleh konsisten, tetapi apakah Anda melempar runtime_
atau logic_
atau custom_
sesuatu benar-benar tidak begitu penting, saya pikir.
Untuk mengomentari keduanya stof
dan bitset
:
Kedua fungsi mengambil string sebagai argumen, dan dalam kedua kasus itu adalah:
- non-sepele untuk memeriksa penelepon apakah string yang diberikan valid (mis. kasus terburuk Anda harus mereplikasi logika fungsi; dalam kasus bitset, tidak segera jelas apakah string kosong itu valid, jadi biarkan aktor memutuskan)
- Sudah merupakan tanggung jawab fungsi untuk "mem-parsing" string, sehingga harus memvalidasi string, sehingga masuk akal bahwa ia melaporkan kesalahan untuk "menggunakan" string secara seragam (dan dalam kedua kasus ini merupakan pengecualian) .
aturan yang sering muncul dengan pengecualian adalah "hanya gunakan pengecualian dalam keadaan luar biasa". Tapi bagaimana fungsi perpustakaan seharusnya tahu keadaan mana yang luar biasa?
Pernyataan ini memiliki, IMHO, dua akar:
Performa : Jika suatu fungsi dipanggil dalam jalur kritis, dan case "luar biasa" tidak luar biasa, yaitu sejumlah besar lintasan akan melibatkan melempar pengecualian, lalu membayar setiap waktu untuk pengecualian-mesin-gulungan tidak masuk akal , dan mungkin terlalu lambat.
Lokalitas penanganan kesalahan : Jika fungsi dipanggil dan pengecualian segera tertangkap dan diproses, maka ada gunanya melemparkan pengecualian, seperti penanganan kesalahan akan lebih verbose dengan yang catch
dari dengan if
.
Contoh:
float readOrDefault;
try {
readOrDefault = stof(...);
} catch(std::exception&) {
// discard execption, just use default value
readOrDefault = 3.14f; // 3.14 is the default value if cannot be read
}
Di sinilah fungsi seperti TryParse
vs. Parse
ikut bermain: Satu versi untuk saat kode lokal mengharapkan string yang diuraikan menjadi valid, satu versi ketika kode lokal mengasumsikan bahwa sebenarnya diharapkan (yaitu tidak luar biasa) bahwa parsing akan gagal.
Memang, stof
hanya (didefinisikan sebagai) pembungkus strtof
, jadi jika Anda tidak ingin pengecualian, gunakan yang itu.
Jadi, bagaimana saya harus memutuskan apakah saya harus menggunakan pengecualian atau tidak untuk fungsi tertentu?
IMHO, Anda memiliki dua kasus:
Fungsi "Perpustakaan" (sering digunakan kembali dalam konteks yang berbeda): Anda pada dasarnya tidak dapat memutuskan. Mungkin memberikan kedua versi, mungkin yang melaporkan kesalahan dan yang bungkus yang mengubah kesalahan yang dikembalikan menjadi pengecualian.
Fungsi "Aplikasi" (khusus untuk gumpalan kode aplikasi, dapat digunakan kembali beberapa, tetapi dibatasi oleh gaya penanganan kesalahan aplikasi, dll.): Di sini, harus sering dipotong cukup jelas. Jika jalur kode yang memanggil fungsi menangani pengecualian dengan cara yang waras dan bermanfaat, gunakan pengecualian untuk melaporkan kesalahan apa pun (tetapi lihat di bawah) . Jika kode aplikasi lebih mudah dibaca dan ditulis untuk gaya pengembalian kesalahan, tentu saja gunakan itu.
Tentu saja akan ada tempat di antara - gunakan saja apa yang perlu dan ingat YAGNI.
Terakhir, saya pikir saya harus kembali ke pernyataan FAQ,
Jangan gunakan lemparan untuk menunjukkan kesalahan pengkodean dalam penggunaan suatu fungsi. Gunakan penegasan atau mekanisme lain untuk mengirim proses ke debugger atau untuk menghentikan proses ...
Saya berlangganan ini untuk semua kesalahan yang merupakan indikasi jelas bahwa ada sesuatu yang sangat kacau atau kode panggilan jelas tidak tahu apa yang dilakukannya.
Tetapi ketika ini tepat sering aplikasi yang sangat spesifik, maka lihat domain perpustakaan di atas vs domain aplikasi.
Ini kembali pada pertanyaan apakah dan bagaimana memvalidasi prakondisi panggilan , tapi saya tidak akan membahasnya, jawabnya sudah terlalu lama :-)