batalkan, hentikan, atau keluar?


112

Apa perbedaan antara ketiganya, dan bagaimana saya harus mengakhiri program jika ada pengecualian yang tidak dapat saya tangani dengan benar?


3
Ini bukan duplikat, melainkan, subset dengan beberapa jawaban bagus stackoverflow.com/questions/397075/… dan juga diberi tag C ++!
Ellie Kesselman

std::abortwajar jika pengecualian tidak dapat diselesaikan di destruktor.
Daniel

1
Untuk informasi lebih lanjut tentang std::terminatelihat artikel ini di blog C ++ andrzej yang sangat baik: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate
Ohad Schneider

Jawaban:


3

Saran saya adalah jangan menggunakan salah satu dari mereka. Sebaliknya, catchpengecualian yang tidak dapat Anda tangani main()dan hanya returndari sana. Ini berarti Anda dijamin bahwa pelepasan tumpukan terjadi dengan benar dan semua destruktor dipanggil. Dengan kata lain:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}

8
@Neil: Pada dasarnya setuju, tetapi pengecualian yang tidak dapat ditangani oleh program harus dilaporkan & dicantumkan kembali. Biarkan aplikasi mogok.
John Dibling

13
Untuk memastikan tumpukan terlepas, Anda harus selalu menangkap di main. Tapi saya akan melempar kembali dari tangkapan. Karena beberapa OS memiliki kemampuan untuk secara otomatis menjalankan infrastruktur debugging jika Anda telah mengompilasi dalam debug.
Martin York

5
Pengecualian yang tidak tertangkap bahkan oleh penangan tingkat atas bisa memanggil fasilitas pelaporan sistem yang membuang proses dan mengunggah laporan pengecualian untuk perhatian pengembang, seperti Pelaporan Kesalahan Windows, laporan kesalahan Mac OS X dan log kesalahan aplikasi iPhone.
JBRWilkinson

6
@John Alasan mengapa hal ini mengejutkan bagi saya adalah bahwa, meskipun sangat masuk akal mengingat bagaimana pengecualian sebenarnya diimplementasikan, itu mematahkan abstraksi bahwa pengecualian "menyebarkan tumpukan" hingga penangan yang sesuai ditemukan (atau hentikan adalah dipanggil). Dan abstraksi yang bocor, meski seringkali tidak dapat dihindari, selalu mengejutkan saat ditemui.
Tyler McHenry

11
-1 karena ini tidak menjawab setengah pertanyaan. "Apa perbedaan antara [batalkan, hentikan, atau keluar?]" Ini adalah jawaban yang lebih baik: stackoverflow.com/a/397081/353094 Juga stackoverflow.com/a/2820407/353094 adalah jawaban yang bagus.
leetNightshade

149
  • abort menunjukkan akhir yang "tidak normal" pada program, dan memunculkan sinyal POSIX SIGABRT, yang berarti bahwa setiap penangan yang telah Anda daftarkan untuk sinyal itu akan dipanggil, meskipun program akan tetap menghentikan kata penutup dalam kedua kasus tersebut. Biasanya Anda akan menggunakan abortprogram C untuk keluar dari kasus kesalahan tak terduga di mana kesalahan tersebut kemungkinan besar merupakan bug dalam program, daripada sesuatu seperti input yang buruk atau kegagalan jaringan. Misalnya, Anda mungkin abortjika struktur data ditemukan memiliki penunjuk NULL di dalamnya, padahal seharusnya hal itu tidak pernah terjadi.

  • exit menunjukkan akhir "normal" untuk program, meskipun ini mungkin masih menunjukkan kegagalan (tapi bukan bug). Dengan kata lain, Anda mungkin exitmendapatkan kode kesalahan jika pengguna memberikan input yang tidak dapat diuraikan, atau file tidak dapat dibaca. Kode keluar 0 menunjukkan keberhasilan. exitjuga secara opsional memanggil penangan sebelum mengakhiri program. Ini terdaftar dengan fungsi atexitdan on_exit.

  • std :: terminate adalah apa yang dipanggil secara otomatis dalam program C ++ ketika ada pengecualian yang tidak tertangani. Ini pada dasarnya setara dengan C ++ abort, dengan asumsi bahwa Anda melaporkan semua kesalahan luar biasa Anda dengan cara melempar pengecualian. Ini memanggil penangan yang diatur oleh std::set_terminatefungsi, yang secara default memanggil abort.

Dalam C ++, Anda biasanya ingin menghindari panggilan abortatau exitkesalahan, karena Anda lebih baik membuat pengecualian dan membiarkan kode lebih jauh tumpukan panggilan memutuskan apakah mengakhiri program sesuai atau tidak. Apakah Anda menggunakan exituntuk sukses atau tidak adalah masalah keadaan - apakah masuk akal untuk mengakhiri program di tempat lain selain pernyataan return di main.

std::terminateharus dianggap sebagai alat pelaporan kesalahan terakhir, bahkan di C ++. Masalahnya std::terminateadalah bahwa penangan terminate tidak memiliki akses ke pengecualian yang tidak tertangani, jadi tidak ada cara untuk mengetahui apa itu. Anda biasanya jauh lebih baik membungkus keseluruhan bagian utama dalam satu try { } catch (std::exception& ex) { }blok. Setidaknya Anda dapat melaporkan lebih banyak informasi tentang pengecualian yang berasal dari std::exception(meskipun tentu saja pengecualian yang tidak berasal dari std::exceptionakan tetap tidak tertangani).

Membungkus badan mainin try { } catch(...) { }tidak jauh lebih baik daripada mengatur pengendali terminate, karena sekali lagi Anda tidak memiliki akses ke pengecualian yang dimaksud. Sunting: Menurut jawaban Neil Butterworth, ada keuntungan bahwa tumpukan dibatalkan dalam kasus ini, yang (agak mengejutkan) tidak benar untuk pengecualian yang tidak tertangani.


10
Bisakah Anda memperbarui jawaban ini dengan info C ++ 11? Tampaknya sekarang ada cara untuk mendapatkan pengecualian di catch (...) dan di handler terminate.
Klaim

1
Di C ++ penangan terminate memang memiliki akses ke pengecualian melalui std::current_exception(). Lihat contoh di sini: akrzemi1.wordpress.com/2011/10/05/using-stdterminate
anorm

Tidak masalah jika Anda bisa mendapatkan pengecualian saat ini, karena Anda tidak dapat memeriksanya. Yang bisa Anda lakukan hanyalah membuangnya.
seattlecpp

2
@seattlecpp Anda dapat menampilkannya kembali dan mendapatkan referensi ke sana, yang kemudian dapat Anda periksa
gpeche

16

std :: abort dan std :: exit (dan lainnya: std :: _ Exit, std :: quick_exit) hanyalah fungsi tingkat yang lebih rendah. Anda menggunakannya untuk memberi tahu program apa yang Anda ingin lakukan dengan tepat: destruktor apa (dan jika) harus dipanggil, fungsi pembersihan apa yang dipanggil, nilai apa yang dikembalikan, dll.

std :: terminate adalah abstraksi tingkat yang lebih tinggi: ini dipanggil (oleh run-time atau Anda) untuk menunjukkan bahwa kesalahan dalam program telah terjadi dan karena alasan tertentu tidak mungkin ditangani dengan melemparkan pengecualian. Keharusan untuk itu biasanya terjadi ketika kesalahan terjadi dalam mekanisme pengecualian itu sendiri, tetapi Anda dapat menggunakannya kapan saja ketika Anda tidak ingin program Anda melanjutkan melewati kesalahan yang diberikan. Saya mengumpulkan daftar lengkap situasi ketika std :: terminate dipanggil di posting saya. Tidak ditentukan apa yang dilakukan std :: terminate, karena Anda yang mengontrolnya. Anda dapat mengonfigurasi perilaku dengan mendaftarkan fungsi apa pun. Batasan yang Anda miliki adalah bahwa fungsi tersebut tidak dapat kembali ke situs kesalahan dan tidak dapat keluar melalui pengecualian, tetapi secara teknis Anda bahkan dapat memulai pompa pesan Anda di dalam. Untuk daftar hal-hal berguna yang dapat Anda lakukan di dalam, lihat posting saya yang lain .

Secara khusus, perhatikan bahwa std :: terminate dianggap sebagai penangan pengecualian dalam konteks di mana std :: terminate dipanggil karena pengecualian yang dilemparkan yang tidak dapat ditangani, dan Anda dapat memeriksa apa pengecualian itu dan memeriksanya dengan menggunakan C ++ 11 menggunakan std :: rethrow_exception dan std :: current_exception. Semuanya ada di postingan saya .


Apakah disarankan untuk memiliki penangan pembersihan jika program berhenti karena sinyal sistem? Misalnya, akses memori yang tidak valid menyebabkan pembangkitan sinyal SIGSEGV. Dalam hal ini, apakah baik membiarkan program untuk menghentikan dan memiliki file inti atau mendaftarkan penangan sinyal untuk melakukan pembersihan ?? Apakah ada kekhawatiran melakukan pembersihan saat menangani sinyal sistem dibandingkan melakukan pembersihan saat menangani std :: terminate?
kartik trivikram

12

quick_exit () !

Jika program Anda multi-thread, maka pemanggilan exit()kemungkinan besar akan mengakibatkan crash karena objek global / statis std::threadakan berusaha untuk dihancurkan tanpa keluar dari utasnya.

Jika Anda ingin mengembalikan kode kesalahan dan keluar dari program (lebih atau kurang) secara normal, panggil quick_exit()program multi-utas. Untuk penghentian abnormal (tanpa kemungkinan bagi Anda untuk menentukan kode kesalahan), abort()atau std::terminate()bisa disebut.

Catatan: quick_exit () belum didukung oleh MSVC ++ hingga versi 2015.


4
  • terminate memberi Anda kemungkinan untuk mendaftar apa yang akan terjadi ketika dipanggil. Harus salah satu dari dua lainnya.
  • exit adalah jalan keluar normal yang memungkinkan untuk menentukan status keluar. Penangan yang didaftarkan oleh at_exit () dijalankan
  • membatalkan adalah jalan keluar yang tidak normal. Satu-satunya yang dijalankan adalah penangan sinyal untuk SIGABRT.

4
  • terminate () secara otomatis dipanggil ketika terjadi pengecualian yang tidak dapat ditangani. Secara default, panggilan terminate () abort (). Anda dapat mengatur pegangan khusus dengan fungsi set_terminate ().

    abort () mengirimkan sinyal SIGABRT.

    exit () tidak selalu berarti buruk. Itu berhasil keluar dari aplikasi, dan memanggil fungsi atexit () dalam urutan LIFO. Saya biasanya tidak melihat ini di aplikasi C ++, namun, saya melihatnya di banyak aplikasi berbasis unix di mana ia mengirimkan kode keluar di bagian akhir. Biasanya keluar (0) menunjukkan aplikasi berhasil dijalankan.


8
Gagal! Di Unix dan DOS, exit (0) menunjukkan sukses dan nilai lain yang diteruskan ke exit () menunjukkan kegagalan, bukan sebaliknya!
Richard Barrell
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.