Saya telah melihat setidaknya satu sumber yang dapat diandalkan (kelas C ++ yang saya ambil) merekomendasikan bahwa kelas pengecualian khusus aplikasi di C ++ harus mewarisi std::exception
. Saya tidak mengerti manfaat dari pendekatan ini.
Di C # alasan untuk mewarisi dari ApplicationException
sudah jelas: Anda mendapatkan beberapa metode, properti, dan konstruktor yang berguna dan hanya perlu menambahkan atau mengganti apa yang Anda butuhkan. Dengan std::exception
tampaknya semua yang Anda dapatkan adalah what()
metode untuk menimpa, yang Anda bisa juga buat sendiri.
Jadi apa keuntungan, jika ada, menggunakan std::exception
sebagai kelas dasar untuk kelas pengecualian khusus aplikasi saya? Apakah ada alasan bagus untuk tidak mewarisi std::exception
?
sumber
Jawaban:
Manfaat utama adalah bahwa kode menggunakan kelas Anda tidak harus tahu tepat jenis apa yang Anda
throw
itu, tapi bisa sajacatch
itustd::exception
.Edit: seperti yang dicatat Martin dan yang lainnya, Anda sebenarnya ingin mendapatkan dari salah satu sub-kelas yang
std::exception
dideklarasikan di<stdexcept>
header.sumber
std::exception
adalah didefinisikan dalam<exception>
header ( bukti ).-1
Masalahnya
std::exception
adalah bahwa tidak ada konstruktor (dalam versi standar yang sesuai) yang menerima pesan.Alhasil saya lebih suka berasal dari
std::runtime_error
. Ini diturunkan daristd::exception
tetapi konstruktornya memungkinkan Anda untuk melewatkan C-String atau astd::string
ke konstruktor yang akan dikembalikan (sebagai achar const*
) saatwhat()
dipanggil.sumber
Alasan untuk mewarisi dari
std::exception
adalah kelas dasar "standar" untuk pengecualian, jadi wajar bagi orang lain dalam tim, misalnya, untuk mengharapkan itu dan menangkap dasarstd::exception
.Jika Anda mencari kenyamanan, Anda dapat mewarisi dari
std::runtime_error
yang menyediakanstd::string
konstruktor.sumber
std::exception
adalah ide yang bagus . Apa yang tidak boleh Anda lakukan adalah mewarisi lebih dari satu.Saya pernah berpartisipasi dalam pembersihan basis kode besar di mana penulis sebelumnya telah melemparkan int, HRESULTS, std :: string, char *, kelas acak ... hal-hal berbeda di mana-mana; sebutkan saja jenisnya dan itu mungkin terlempar ke suatu tempat. Dan tidak ada kelas dasar sama sekali. Percayalah, segala sesuatunya jauh lebih rapi setelah kita sampai pada titik bahwa semua jenis yang dilempar memiliki basis yang sama yang dapat kita tangkap dan tahu tidak ada yang akan lewat. Jadi tolong bantulah diri Anda sendiri (dan mereka yang harus mempertahankan kode Anda di masa depan) dan lakukan seperti itu dari awal.
sumber
Anda harus mewarisi dari boost :: exception . Ini menyediakan lebih banyak fitur dan cara yang dipahami dengan baik untuk membawa data tambahan ... tentu saja, jika Anda tidak menggunakan Boost , abaikan saran ini.
sumber
Ya, Anda harus berasal dari
std::exception
.Orang lain telah menjawab bahwa
std::exception
memiliki masalah bahwa Anda tidak dapat menyampaikan pesan teks ke sana, namun umumnya bukan ide yang baik untuk mencoba memformat pesan pengguna pada titik lemparan. Sebaliknya, gunakan objek pengecualian untuk mengangkut semua informasi yang relevan ke situs tangkapan yang kemudian dapat memformat pesan yang ramah pengguna.sumber
std::exception
dan membawa informasi pengecualian. Tetapi siapa yang akan bertanggung jawab untuk membangun / memformat pesan kesalahan? The::what()
fungsi atau sesuatu yang lain?Alasan mengapa Anda mungkin ingin mewarisi dari
std::exception
adalah karena memungkinkan Anda untuk melempar pengecualian yang ditangkap menurut kelas itu, yaitu:sumber
Ada satu masalah dengan warisan yang harus Anda ketahui adalah pemotongan objek. Saat Anda menulis
throw e;
ekspresi-lemparan menginisialisasi objek sementara, yang disebut objek pengecualian, yang tipenya ditentukan dengan menghapus kualifikasi cv tingkat atas apa pun dari jenis statis operan darithrow
. Itu mungkin bukan yang Anda harapkan. Contoh masalah dapat Anda temukan di sini .Ini bukan argumen yang menentang warisan, itu hanya info 'harus tahu'.
sumber
throw;
tidak apa-apa, tetapi tidak jelas apakah Anda harus menulis sesuatu seperti ini.raise()
, seperti:virtual void raise() { throw *this; }
. Ingatlah untuk menimpanya di setiap pengecualian turunan.Perbedaan: std :: runtime_error vs std :: exception ()
Apakah Anda harus mewarisinya atau tidak, itu terserah Anda. Standar
std::exception
dan keturunan standarnya mengusulkan satu kemungkinan struktur hierarki pengecualian (pembagian menjadilogic_error
subhierarki danruntime_error
subhierarki) dan satu kemungkinan antarmuka objek pengecualian. Jika Anda suka - gunakanlah. Jika karena alasan tertentu Anda memerlukan sesuatu yang berbeda - tentukan kerangka pengecualian Anda sendiri.sumber
Karena bahasa sudah menampilkan std :: exception, Anda tetap perlu menangkapnya untuk menyediakan pelaporan kesalahan yang layak. Anda mungkin juga menggunakan tangkapan yang sama untuk semua pengecualian tak terduga milik Anda sendiri. Selain itu, hampir semua pustaka yang melontarkan pengecualian akan memperolehnya dari std :: exception.
Dengan kata lain, itu baik
atau
Dan pilihan kedua pasti lebih baik.
sumber
Jika semua kemungkinan pengecualian Anda berasal dari
std::exception
, blok tangkapan Anda bisa dengan mudahcatch(std::exception & e)
dan terjamin untuk menangkap semuanya.Setelah Anda menangkap pengecualian, Anda dapat menggunakan
what
metode itu untuk mendapatkan lebih banyak informasi. C ++ tidak mendukung pengetikan bebek, jadi kelas lain denganwhat
metode akan memerlukan tangkapan yang berbeda dan kode yang berbeda untuk menggunakannya.sumber
Apakah akan berasal dari tipe pengecualian standar atau tidak adalah pertanyaan pertama. Melakukan hal itu memungkinkan satu penangan pengecualian untuk semua pengecualian pustaka standar dan milik Anda sendiri, tetapi hal itu juga mendorong penangan penampung semuanya. Masalahnya adalah bahwa seseorang seharusnya hanya menangkap pengecualian yang diketahui bagaimana menanganinya. Dalam main (), misalnya, menangkap semua std :: pengecualian kemungkinan merupakan hal yang baik jika string what () akan dicatat sebagai pilihan terakhir sebelum keluar. Di tempat lain, bagaimanapun, itu tidak mungkin menjadi ide yang bagus.
Setelah Anda memutuskan apakah akan diturunkan dari tipe pengecualian standar atau tidak, maka pertanyaannya adalah mana yang harus menjadi dasar. Jika aplikasi Anda tidak memerlukan i18n, Anda mungkin berpikir bahwa memformat pesan di situs panggilan sama baiknya dengan menyimpan informasi dan membuat pesan di situs panggilan. Masalahnya adalah pesan yang diformat mungkin tidak diperlukan. Lebih baik menggunakan skema pembuatan pesan malas - mungkin dengan memori yang dialokasikan sebelumnya. Kemudian, jika pesan diperlukan, pesan itu akan dibuat saat diakses (dan, mungkin, di-cache dalam objek pengecualian). Jadi, jika pesan dibuat saat dilempar, maka turunan std :: exception, seperti std :: runtime_error diperlukan sebagai kelas dasar. Jika pesan dibuat secara malas, maka std :: exception adalah basis yang sesuai.
sumber
Alasan lain untuk pengecualian subkelas adalah aspek desain yang lebih baik saat bekerja pada sistem enkapsulasi besar. Anda dapat menggunakannya kembali untuk hal-hal seperti pesan validasi, kueri pengguna, kesalahan pengontrol fatal, dan sebagainya. Daripada menulis ulang atau mengulang semua validasi Anda seperti pesan, Anda cukup "menangkapnya" di file sumber utama, tetapi membuang kesalahan di mana saja di seluruh rangkaian kelas Anda.
misalnya Pengecualian fatal akan menghentikan program, kesalahan validasi hanya akan menghapus tumpukan dan kueri pengguna akan mengajukan pertanyaan kepada pengguna akhir.
Melakukannya dengan cara ini juga berarti Anda dapat menggunakan kembali kelas yang sama tetapi pada antarmuka yang berbeda. Misalnya, aplikasi windows dapat menggunakan kotak pesan, layanan web akan menampilkan html dan sistem pelaporan akan mencatatnya dan seterusnya.
sumber
Meskipun pertanyaan ini agak tua dan sudah banyak dijawab, saya hanya ingin menambahkan catatan tentang bagaimana melakukan penanganan pengecualian yang tepat di C ++ 11, karena saya terus melewatkan ini dalam diskusi tentang pengecualian:
Gunakan
std::nested_exception
danstd::throw_with_nested
Ini dijelaskan di StackOverflow di sini dan di sini , bagaimana Anda bisa mendapatkan pelacakan balik pada pengecualian Anda di dalam kode Anda tanpa perlu debugger atau logging rumit, cukup dengan menulis penangan pengecualian yang tepat yang akan menampilkan kembali pengecualian bertingkat.
Karena Anda dapat melakukan ini dengan kelas pengecualian apa pun, Anda dapat menambahkan banyak informasi ke penelusuran mundur seperti itu! Anda juga dapat melihat MWE saya di GitHub , di mana backtrace akan terlihat seperti ini:
Anda bahkan tidak perlu membuat subkelas
std::runtime_error
untuk mendapatkan banyak informasi saat pengecualian dilempar.Satu-satunya keuntungan yang saya lihat di subclassing (daripada hanya menggunakan
std::runtime_error
) adalah bahwa penangan pengecualian Anda dapat menangkap pengecualian khusus Anda dan melakukan sesuatu yang istimewa. Sebagai contoh:sumber