Perbedaan: std :: runtime_error vs std :: exception ()

128

Apa perbedaan antara std::runtime_errordan std::exception? Apa gunanya masing-masing? Mengapa mereka berbeda sejak awal?

sivabudh
sumber

Jawaban:

153

std::exceptionadalah kelas yang tujuannya hanya untuk melayani sebagai kelas dasar dalam hierarki pengecualian. Tidak memiliki kegunaan lain. Dengan kata lain, secara konseptual itu abstrak kelas (meskipun tidak didefinisikan sebagai kelas abstrak dalam makna C ++ dari istilah tersebut).

std::runtime_erroradalah kelas yang lebih khusus, turun dari std::exception, yang dimaksudkan untuk dilemparkan jika terjadi berbagai kesalahan runtime . Ini memiliki tujuan ganda. Itu dapat dibuang dengan sendirinya, atau dapat berfungsi sebagai kelas dasar untuk berbagai jenis pengecualian runtime error yang lebih khusus, seperti std::range_error, std::overflow_errordll. Anda dapat menentukan kelas pengecualian Anda sendiri yang diturunkan std::runtime_error, serta Anda dapat menentukan pengecualian Anda sendiri kelas turun dari std::exception.

Sama seperti std::runtime_error, pustaka standar berisi std::logic_error, juga turun dari std::exception.

Inti dari hierarki ini adalah memberi pengguna kesempatan untuk menggunakan kekuatan penuh mekanisme penanganan pengecualian C ++. Karena klausa 'catch' dapat menangkap pengecualian polimorfik, pengguna dapat menulis klausa 'catch' yang dapat menangkap tipe pengecualian dari subtree tertentu dari hierarki pengecualian. Misalnya, catch (std::runtime_error& e)akan menangkap semua pengecualian dari std::runtime_errorsubtree, membiarkan yang lain melewati (dan terbang lebih jauh ke tumpukan panggilan).

PS Merancang hierarki kelas pengecualian yang berguna (yang akan membiarkan Anda menangkap hanya tipe pengecualian yang Anda minati di setiap titik kode Anda) adalah tugas yang tidak sepele. Apa yang Anda lihat di pustaka C ++ standar adalah salah satu pendekatan yang mungkin, ditawarkan kepada Anda oleh penulis bahasa. Seperti yang Anda lihat, mereka memutuskan untuk membagi semua jenis pengecualian menjadi "kesalahan runtime" dan "kesalahan logika" dan membiarkan Anda melanjutkan dari sana dengan jenis pengecualian Anda sendiri. Tentu saja ada cara-cara alternatif untuk menyusun hierarki itu, yang mungkin lebih sesuai dalam desain Anda.

Pembaruan: Portabilitas Linux vs Windows

Seperti yang Loki Astari dan unixman83 catat dalam jawaban dan komentar mereka di bawah ini, konstruktor exceptionkelas tidak mengambil argumen apa pun sesuai dengan standar C ++. Microsoft C ++ memiliki konstruktor yang mengambil argumen di exceptionkelas, tetapi ini bukan standar. The runtime_errorkelas memiliki konstruktor taking argumen ( char*) pada kedua platform, Windows dan Linux. Agar portabel, lebih baik digunakan runtime_error.

(Dan ingat, hanya karena spesifikasi proyek Anda mengatakan kode Anda tidak harus berjalan di Linux, itu tidak berarti itu tidak harus dijalankan di Linux.)

Semut
sumber
1
Terima kasih. jawaban yang bagus meskipun saya bertanya-tanya apakah ada kebutuhan untuk memiliki jenis pengecualian yang berbeda ... hanya sebuah pemikiran sekalipun.
sivabudh
1
Jika ada kemungkinan bahwa pengecualian dapat ditutup kembali dari, maka jenis pengecualian yang berbeda dapat berguna karena kita dapat menggunakan mekanisme penanganan pengecualian untuk mengarahkan pengecualian ke penangan yang akan mencoba dan memperbaiki masalah. Jika tidak ada peluang pemulihan, salah satu pengecualian standar baik-baik saja.
Martin York
1
Sama seperti kata sampingan: tidak ada aturan, di mana pun, yang memaksa Anda untuk berasal std::exception. Tentu, semua stdbenda melempar kelas turunannya, tetapi sama sekali tidak ada alasan untuk hanya melempar std::exceptionobjek turunan.
rubenvb
1
@rubenvb Saya tidak tahu tentang itu, tapi saya pikir itu akan menjelaskan kode untuk pemeliharaan di masa depan jika hanya objek kelas yang berasal dari pengecualian dilemparkan. Contoh: Saya ingin mengetahui pengecualian khusus apa yang diterapkan di basis kode saya dan mencari kelas yang berasal dari pengecualian.
Aku sendiri
21

std::exceptionharus dianggap (perhatikan yang dipertimbangkan) basis abstrak dari hierarki pengecualian standar. Ini karena tidak ada mekanisme untuk menyampaikan pesan tertentu (untuk melakukan ini Anda harus menurunkan dan mengkhususkan what()). Tidak ada yang menghentikan Anda dari menggunakan std :: exception dan untuk aplikasi sederhana mungkin itu yang Anda butuhkan.

std::runtime_errordi sisi lain memiliki konstruktor yang valid yang menerima string sebagai pesan. Ketika what()disebut pointer char const dikembalikan bahwa menunjuk pada string C yang memiliki string yang sama seperti yang diteruskan ke konstruktor.

try
{
    if (badThingHappened)
    {
         throw std::runtime_error("Something Bad happened here");
    }
}
catch(std::exception const& e)
{
    std::cout << "Exception: " << e.what() << "\n";
} 
Martin York
sumber
1
Terima kasih untuk jawabannya, martin. Namun, saya menggunakan std :: exception () dengan cara yang sama seperti yang Anda jelaskan di atas. yaitu std :: exception () constructor juga dapat mengambil std :: string () atau const char *.
sivabudh
14
Tidak sesuai standar. std :: exception memiliki satu konstruktor yang tidak menggunakan argumen. Menggunakan versi yang menerima std :: string atau C-String adalah non portable.
Martin York
10
Karena Microsoft, saya terbiasa melempar std::exception(std::string). Sekarang saya menyadari bahwa saya harus membuang std::runtime_errorjika saya ingin kode saya berfungsi di Linux (GCC).
unixman83