Haruskah program C ++ menangkap semua pengecualian dan mencegah pengecualian meluap melewati main ()?


29

Saya pernah disarankan bahwa program C ++ pada akhirnya harus menangkap semua pengecualian. Alasan yang diberikan pada saat itu pada dasarnya adalah bahwa program yang memungkinkan pengecualian muncul di luar main()memasuki keadaan zombie yang aneh. Saya diberitahu ini beberapa tahun yang lalu dan dalam retrospeksi saya percaya fenomena yang diamati adalah karena generasi panjang dump inti sangat besar dari proyek tersebut.

Pada saat itu ini tampak aneh tetapi meyakinkan. Itu benar-benar tidak masuk akal bahwa C ++ harus "menghukum" programmer karena tidak menangkap semua pengecualian tetapi bukti sebelum saya tampaknya mendukung hal ini. Untuk proyek yang dimaksud, program yang melemparkan pengecualian tanpa tertangkap tampaknya memasuki keadaan zombie yang aneh - atau seperti yang saya duga penyebabnya sekarang, proses di tengah-tengah pembuangan inti yang tidak diinginkan biasanya sulit untuk dihentikan.

(Bagi siapa pun yang bertanya-tanya mengapa hal ini tidak lebih jelas pada saat itu: Proyek ini menghasilkan sejumlah besar output dalam banyak file dari berbagai proses yang secara efektif mengaburkan semua jenis aborted (core dumped)pesan dan dalam kasus khusus ini, pemeriksaan post-mortem untuk dump inti tidak dilakukan. itu adalah teknik debugging yang penting sehingga dump inti tidak banyak dipikirkan. Masalah dengan suatu program biasanya tidak tergantung pada akumulasi dari banyak peristiwa dari waktu ke waktu oleh program yang berumur panjang melainkan input awal untuk program yang berumur pendek (< 1 jam) jadi lebih praktis untuk hanya menjalankan kembali program dengan input yang sama dari debug build atau di debugger untuk mendapatkan info lebih lanjut.)

Saat ini, saya tidak yakin apakah ada keuntungan atau kerugian besar dari menangkap pengecualian hanya untuk tujuan mencegah pengecualian pergi main().

Keuntungan kecil yang dapat saya pikirkan untuk memungkinkan pengecualian untuk muncul main()adalah bahwa hal itu menyebabkan hasil std::exception::what()untuk dicetak ke terminal (setidaknya dengan program yang dikompilasi gcc di Linux). Di sisi lain, ini sepele untuk dicapai dengan alih-alih menangkap semua pengecualian yang berasal dari std::exceptiondan mencetak hasil std::exception::what()dan jika diinginkan untuk mencetak pesan dari pengecualian yang tidak berasal dari std::exceptionmaka harus ditangkap sebelum pergi main()untuk mencetak pesan.

Kerugian sederhana yang dapat saya pikirkan untuk memungkinkan pengecualian untuk menggelembungkan masa lalu main()adalah bahwa dump inti yang tidak diinginkan dapat dihasilkan. Untuk proses yang menggunakan memori dalam jumlah besar, ini bisa menjadi gangguan dan mengendalikan perilaku dumping inti dari suatu program membutuhkan pemanggilan fungsi spesifik OS. Di sisi lain, jika sebuah core dump dan exit diinginkan maka ini dapat dicapai setiap saat dengan menelepon std::abort()dan keluar tanpa core dump dapat dicapai kapan saja dengan menelepon std::exit().

Secara anekdot, saya rasa saya tidak pernah melihat what(): ...pesan default dicetak oleh program yang didistribusikan secara luas saat mogok.

Apa, jika ada, argumen kuat untuk atau tidak memungkinkan pengecualian C ++ meluap melewati main()?

Sunting: Ada banyak pengecualian umum yang menangani pertanyaan di situs ini. Pertanyaan saya secara khusus tentang pengecualian C ++ yang tidak dapat ditangani dan berhasil sampai ke sana main()- mungkin pesan kesalahan dapat dicetak tetapi ini segera menunjukkan kesalahan berhenti.




@gnat Pertanyaan saya sedikit lebih spesifik. Ini tentang pengecualian C ++ yang tidak dapat ditangani daripada penanganan pengecualian dalam bahasa pemrograman apa pun dalam kondisi apa pun.
Praxeolitic

Untuk program multi-utas, banyak hal dapat menjadi lebih kompleks atau lebih eksotis (pengecualian wrt)
Basile Starynkevitch

1
Orang-orang perangkat lunak untuk Ariane 5 tentu berharap mereka telah menangkap semua pengecualian selama peluncuran pertama ...
Eric Towers

Jawaban:


25

Satu masalah dengan membiarkan pengecualian melewati main adalah bahwa program akan diakhiri dengan panggilan std::terminateyang merupakan perilaku default untuk dipanggil std::abort. Ini hanya implementasi yang ditentukan jika stack unwinding dilakukan sebelum memanggil terminatesehingga program Anda dapat berakhir tanpa memanggil satu destruktor! Jika Anda memiliki beberapa sumber daya yang benar-benar perlu dipulihkan oleh panggilan destruktor, Anda berada dalam ...


12
Jika Anda memiliki beberapa sumber daya yang benar-benar perlu dipulihkan oleh panggilan destruktor, Anda berada di acar ... => Mengingat bahwa (sayangnya) C ++ cukup rawan kecelakaan, dan itu tanpa menyebutkan bahwa OS mungkin memutuskan untuk membunuh program Anda kapan saja (misalnya pembunuh OOM di Unix), program Anda (dan lingkungannya) perlu disesuaikan untuk mendukung kerusakan.
Matthieu M.

@ MatthieuM. Apakah Anda mengatakan bahwa destruktor bukan tempat yang baik untuk pembersihan sumber daya yang seharusnya terjadi bahkan saat terjadi kerusakan?
Praxeolitic

6
@Praxeolitic: Saya mengatakan bahwa tidak semua crash melepaskan stack, misalnya std::aborttidak dan dengan demikian pernyataan yang gagal juga tidak. Penting juga untuk dicatat bahwa pada OS modern, OS itu sendiri akan membersihkan banyak sumber daya (memori, file handle, ...) yang terkait dengan proses ID. Akhirnya, saya juga mengisyaratkan "Pertahanan dalam Kedalaman": tidak dapat diandalkan untuk mengharapkan bahwa semua proses lain adalah anti-bug dan akan selalu merilis sumber daya yang mereka peroleh (sesi, selesai menulis file, ...) Anda harus merencanakan untuk itu ...
Matthieu M.

3
@Praxeolitic Tidak, perangkat lunak Anda tidak perlu merusak data selama listrik mati . Dan jika Anda bisa mengelolanya, Anda juga bisa mengatur untuk tidak merusak data setelah pengecualian yang tidak ditangani.
user253751

1
@Praxeolitic: Sebenarnya, ini memang ada. Anda ingin mendengarkan WM_POWERBROADCASTpesannya. Ini hanya berfungsi jika komputer Anda bertenaga baterai (jika Anda menggunakan laptop atau UPS).
Brian

28

Alasan utama untuk tidak membiarkan pengecualian lolos mainadalah karena jika tidak, Anda kehilangan semua kemungkinan untuk mengontrol bagaimana masalah dilaporkan kepada pengguna Anda.

Untuk program yang tidak dimaksudkan untuk digunakan dalam waktu lama atau didistribusikan secara luas, dapat diterima bahwa kesalahan tak terduga dilaporkan dengan cara apa pun yang diputuskan oleh OS untuk melakukannya (misalnya, menampilkan dialog kesalahan di wajah Anda pada Windows ).

Untuk program yang Anda jual, atau yang disediakan untuk masyarakat umum oleh organisasi yang memiliki reputasi untuk ditegakkan, umumnya merupakan ide yang lebih baik untuk melaporkan dengan cara yang baik bahwa Anda menghadapi masalah yang tidak terduga dan mencoba untuk menyimpan sebanyak mungkin data pengguna mungkin. Tidak kehilangan pekerjaan setengah hari dari pengguna Anda dan tidak crash secara tiba-tiba, tetapi mematikan secara semi-anggun umumnya jauh lebih baik untuk reputasi bisnis Anda daripada alternatifnya.


Apakah Anda yakin perilaku default pengecualian C ++ yang meninggalkan main() banyak hubungannya dengan OS? OS mungkin tidak tahu apa-apa tentang C ++. Dugaan saya adalah bahwa itu ditentukan oleh kode penyusun sisipan di suatu tempat ke dalam program.
Praxeolitic

5
@Praxeolitic: Lebih dari itu OS menetapkan konvensi dan kompiler menghasilkan kode untuk memenuhi konvensi tersebut ketika sebuah program berakhir tanpa terduga. Intinya adalah bahwa penghentian program yang tidak terduga harus dihindari bila memungkinkan.
Bart van Ingen Schenau

Anda hanya kehilangan kendali dalam ruang lingkup std C ++. Cara khusus implementasi untuk menangani "gangguan" seperti itu harus tersedia. C ++ biasanya tidak dijalankan dalam ruang hampa.
Martin Ba

Dialog kesalahan in-your-face Anda dapat dikonfigurasi on / off di registri untuk nilai apa itu - tetapi Anda akan memerlukan kontrol registri untuk melakukan ini - dan sebagian besar program tidak berjalan dalam mode kios (setelah mengambil alih OS).
PerryC

11

TL; DR : Apa spesifikasinya katakan?


Jalan memutar teknis ...

Ketika pengecualian dilemparkan dan tidak ada pawang yang siap untuk itu:

  • itu adalah implementasi yang didefinisikan apakah stack dibatalkan atau tidak
  • std::terminate disebut, yang secara default dibatalkan
  • tergantung pada pengaturan lingkungan Anda, batal mungkin atau tidak meninggalkan laporan kerusakan

Yang terakhir ini dapat berguna untuk bug yang sangat jarang (karena mereproduksi mereka adalah proses yang menghabiskan waktu).


Apakah untuk menangkap semua pengecualian atau tidak, pada akhirnya, adalah masalah spesifikasi:

  • apakah ditentukan cara melaporkan kesalahan pengguna? (nilai tidak valid, ...)
  • apakah ditentukan cara melaporkan kesalahan fungsional? (direktori target hilang, ...)
  • apakah ditentukan cara melaporkan kesalahan teknis? (Pernyataan menyala, ...)

Untuk setiap program produksi, ini harus ditentukan, dan Anda harus mengikuti spesifikasi (dan mungkin berpendapat bahwa itu diubah).

Untuk dengan cepat menggabungkan program yang hanya dapat digunakan oleh orang-orang teknis (Anda, rekan tim Anda), keduanya baik-baik saja. Saya sarankan membiarkannya macet dan mengatur lingkungan untuk mendapatkan laporan atau tidak tergantung pada kebutuhan Anda.


1
Ini. Faktor penentu wrt. Pada dasarnya di luar lingkup C ++.
Martin Ba

4

Pengecualian yang Anda tangkap memberi Anda kesempatan untuk mencetak pesan kesalahan yang bagus atau bahkan mencoba memulihkan dari kesalahan (mungkin dengan hanya meluncurkan ulang aplikasi).

Namun, dalam C ++ pengecualian tidak menyimpan informasi tentang status program saat dilempar. Jika Anda menangkapnya, semua status seperti itu dilupakan, sedangkan jika Anda membiarkan program macet, keadaan biasanya masih ada dan dapat dibaca dari dump program, sehingga lebih mudah untuk debug.

Jadi itu adalah trade-off.


4

Saat Anda tahu Anda harus membatalkan, silakan dan telepon std::terminatesudah untuk mengurangi kerusakan lebih lanjut.

Jika Anda tahu Anda dapat beristirahat dengan aman, lakukan itu. Ingat bahwa tumpukan-gulungan tidak dijamin ketika pengecualian tidak pernah ditangkap, jadi tangkap dan rethrow.

Jika Anda dapat dengan aman melaporkan / mencatat kesalahan lebih baik daripada sistem akan melakukannya sendiri, silakan.
Tetapi pastikan benar-benar untuk tidak secara tidak sengaja memperburuk keadaan saat melakukannya.

Seringkali sudah terlambat untuk menyimpan data ketika Anda mendeteksi kesalahan yang tidak dapat dipulihkan, meskipun itu tergantung pada kesalahan spesifik.
Lagi pula, jika program Anda ditulis untuk pemulihan cepat, mematikannya mungkin merupakan cara terbaik untuk mengakhirinya bahkan jika itu hanya shutdown biasa.


1

Menabrak dengan anggun adalah hal yang baik sebagian besar waktu - tetapi ada trade-off. Terkadang bagus untuk crash. Saya harus menyebutkan bahwa saya kebanyakan berpikir tentang debugging dalam ukuran yang sangat besar. Untuk program yang sederhana - walaupun ini mungkin masih berguna, ini sama sekali tidak membantu dengan program yang sangat kompleks (atau beberapa program kompleks yang berinteraksi).

Anda tidak ingin crash di depan umum (walaupun ini benar-benar tidak dapat dihindari - program crash, dan program non-crash yang benar-benar dapat diverifikasi secara matematis bukanlah yang sedang kita bicarakan di sini). Pikirkan Bill Gates BSODing di tengah-tengah demo - buruk, bukan? Meskipun demikian, kita dapat mencoba mencari tahu mengapa kita crash dan tidak crash lagi dengan cara yang sama.

Saya menyalakan fitur Windows Error Reporting yang membuat crash crashs lokal pada pengecualian yang tidak tertangani. Ini berfungsi dengan baik jika Anda memiliki file simbol yang terkait dengan build (macet) Anda. Cukup menarik, karena saya mengatur alat ini, saya ingin crash lebih banyak - karena saya belajar dari setiap crash. Lalu saya bisa memperbaiki bug dan mengurangi crash.

Jadi untuk sekarang, saya ingin program saya membuang semua jalan ke atas dan crash. Di masa depan, saya mungkin ingin memakan semua pengecualian saya dengan anggun - tetapi tidak jika saya bisa membuatnya bekerja untuk saya.

Anda dapat membaca lebih lanjut tentang Tumpukan Kecelakaan Lokal di sini jika Anda tertarik: Mengumpulkan Dump Mode Pengguna


-6

Pada akhirnya, jika pengecualian muncul di atas main (), itu akan merusak aplikasi Anda, dan dalam pikiran saya sebuah aplikasi seharusnya tidak pernah crash. Jika OK untuk crash aplikasi di satu tempat, lalu mengapa tidak di mana saja? Mengapa repot dengan penanganan pengecualian? (Sarkasme, tidak benar-benar menyarankan ini ...)

Anda mungkin memiliki global mencoba / menangkap yang mencetak pesan yang elegan memberitahu pengguna bahwa telah terjadi kesalahan yang tidak dapat dipulihkan dan bahwa mereka perlu mengirim email kepada bob tentang hal itu atau sesuatu yang serupa, tetapi menabrak benar-benar tidak profesional dan tidak dapat diterima. Sepele untuk dicegah dan Anda dapat memberi tahu pengguna tentang apa yang baru saja terjadi dan bagaimana cara memperbaikinya agar tidak terjadi lagi.

Kecelakaan adalah jam yang sangat amatir.


8
Jadi, lebih baik berpura-pura semuanya bekerja dengan benar dan menimpa semua penyimpanan permanen dengan omong kosong sebagai gantinya?
Deduplicator

1
@Deduplicator: Tidak: Anda selalu dapat menangkap pengecualian dan menanganinya.
Giorgio

5
Jika Anda tahu cara menanganinya, Anda mungkin akan menanganinya jauh sebelum bangun main. Jika Anda tidak tahu cara menanganinya, berpura-pura melakukannya jelas merupakan bug yang sangat mengerikan.
Deduplicator

8
tetapi menerjang sama sekali tidak profesional dan tidak dapat diterima => menghabiskan waktu mengerjakan fitur yang tidak berguna tidak profesional: menabrak hanya penting jika penting bagi pengguna akhir, jika tidak, lalu membiarkannya macet (dan mendapatkan laporan kerusakan, dengan memori dump) lebih ekonomis.
Matthieu M.

Matthieu M. jika itu benar-benar membawa Anda banyak upaya untuk mencatat kesalahan, menyimpan file yang terbuka, dan melukis pesan ramah di layar, saya tidak tahu harus berkata apa. Jika Anda berpikir bahwa sia-sia gagal secara anggun, Anda mungkin harus berbicara dengan siapa pun yang melakukan dukungan teknis untuk kode Anda.
Roger Hill
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.