Saya cenderung menambahkan banyak pernyataan ke kode C ++ saya untuk membuat debugging lebih mudah tanpa mempengaruhi kinerja rilis build. Sekarang, assert
adalah makro C murni yang dirancang tanpa memikirkan mekanisme C ++.
C ++ di sisi lain mendefinisikan std::logic_error
, yang dimaksudkan untuk dilemparkan dalam kasus di mana ada kesalahan dalam logika program (karena itu namanya). Melempar sebuah instance mungkin saja merupakan alternatif yang sempurna dan lebih C ++ untuk assert
.
Masalahnya adalah assert
dan abort
keduanya segera menghentikan program tanpa memanggil destruktor, oleh karena itu melewatkan pembersihan, sedangkan melempar pengecualian secara manual menambah biaya runtime yang tidak perlu. Salah satu cara untuk mengatasinya akan membuat makro pernyataan sendiri SAFE_ASSERT
, yang berfungsi seperti mitra C, tetapi memberikan pengecualian jika gagal.
Saya dapat memikirkan tiga pendapat tentang masalah ini:
- Tetap berpegang pada pernyataan C. Karena program segera dihentikan, tidak masalah apakah perubahan dibuka gulungannya dengan benar. Selain itu, menggunakan
#define
s di C ++ sama buruknya. - Lemparkan pengecualian dan tangkap di main () . Mengizinkan kode untuk melewati destruktor dalam keadaan program apa pun adalah praktik yang buruk dan harus dihindari dengan cara apa pun, begitu juga panggilan ke terminate (). Jika pengecualian dilemparkan, mereka harus ditangkap.
- Lempar pengecualian dan biarkan menghentikan program. Pengecualian untuk menghentikan program tidak apa-apa, dan karena itu
NDEBUG
, ini tidak akan pernah terjadi dalam build rilis. Penangkapan tidak diperlukan dan mengekspos detail implementasi kode internal kemain()
.
Apakah ada jawaban pasti untuk masalah ini? Ada referensi profesional?
Diedit: Melewati destruktor, tentu saja, bukanlah perilaku yang tidak ditentukan.
sumber
logic_error
adalah kesalahan logika. Kesalahan dalam logika program disebut bug. Anda tidak memecahkan bug dengan melemparkan pengecualian.static_assert
tempat yang sesuai jika Anda memilikinya.std::bug
?std::abort()
; itu hanya akan menaikkan sinyal yang menyebabkan proses dihentikan.Jawaban:
Pernyataan sepenuhnya sesuai dalam kode C ++. Pengecualian dan mekanisme penanganan kesalahan lainnya tidak benar-benar dimaksudkan untuk hal yang sama seperti pernyataan.
Penanganan kesalahan adalah ketika ada potensi untuk memulihkan atau melaporkan kesalahan dengan baik kepada pengguna. Misalnya jika ada kesalahan saat mencoba membaca file input, Anda mungkin ingin melakukan sesuatu tentang itu. Kesalahan bisa disebabkan oleh bug, tetapi bisa juga hanya menjadi keluaran yang sesuai untuk masukan yang diberikan.
Pernyataan adalah untuk hal-hal seperti memeriksa bahwa persyaratan API terpenuhi ketika API biasanya tidak diperiksa, atau untuk memeriksa hal-hal yang menurut pengembang dijamin oleh konstruksi. Misalnya, jika suatu algoritme memerlukan masukan yang diurutkan, Anda biasanya tidak akan memeriksanya, tetapi Anda mungkin memiliki pernyataan untuk memeriksanya sehingga debug membangun menandai bug semacam itu. Pernyataan harus selalu menunjukkan program operasi yang salah.
Jika Anda sedang menulis program di mana pematian yang tidak bersih dapat menyebabkan masalah, Anda mungkin ingin menghindari pernyataan. Perilaku tidak terdefinisi secara ketat dalam istilah bahasa C ++ tidak termasuk sebagai masalah di sini, karena membuat pernyataan mungkin sudah merupakan hasil dari perilaku tidak terdefinisi, atau pelanggaran beberapa persyaratan lain yang dapat mencegah beberapa pembersihan berfungsi dengan benar.
Selain itu, jika Anda menerapkan pernyataan dalam istilah pengecualian, maka pernyataan itu berpotensi ditangkap dan 'ditangani' meskipun hal ini bertentangan dengan tujuan pernyataan tersebut.
sumber
3
alih-alih1
ke kode Anda, secara umum itu tidak akan memicu pernyataan. Pernyataan hanya kesalahan programmer, bukan kesalahan pengguna perpustakaan atau aplikasi.Pernyataan ditujukan untuk debugging . Pengguna kode yang Anda kirimkan seharusnya tidak pernah melihatnya. Jika pernyataan dipukul, kode Anda harus diperbaiki.
CWE-617: Reachable Assertion
Pengecualian berlaku untuk keadaan luar biasa . Jika ditemukan, pengguna tidak akan dapat melakukan apa yang dia inginkan, tetapi mungkin dapat melanjutkan di tempat lain.
Penanganan kesalahan adalah untuk aliran program normal. Misalnya, jika Anda meminta nomor pengguna dan mendapatkan sesuatu yang tidak dapat diuraikan, itu normal , karena input pengguna tidak di bawah kendali Anda dan Anda harus selalu menangani semua kemungkinan situasi sebagai hal yang biasa. (Misalnya, putar ulang hingga Anda memiliki masukan yang valid, dengan mengatakan "Maaf, coba lagi" di antaranya.)
sumber
Assertion dapat digunakan untuk memverifikasi invarian implementasi internal, seperti status internal sebelum atau setelah eksekusi beberapa metode, dll. Jika pernyataan gagal, ini berarti logika program rusak dan Anda tidak dapat memulihkannya. Dalam hal ini, hal terbaik yang dapat Anda lakukan adalah menghentikannya sesegera mungkin tanpa memberikan pengecualian kepada pengguna. Apa yang benar-benar bagus tentang pernyataan (setidaknya di Linux) adalah bahwa dump inti dihasilkan sebagai hasil dari penghentian proses dan dengan demikian Anda dapat dengan mudah menyelidiki pelacakan tumpukan dan variabel. Ini jauh lebih berguna untuk memahami kegagalan logika daripada pesan pengecualian.
sumber
Tidak menjalankan destruktor karena alling abort () bukanlah perilaku yang tidak ditentukan!
Jika ya, maka akan menjadi perilaku yang tidak terdefinisi untuk memanggil
std::terminate()
juga, jadi apa gunanya menyediakannya?assert()
berguna di C ++ seperti di C. Assertion bukan untuk penanganan error, melainkan untuk membatalkan program dengan segera.sumber
abort()
untuk membatalkan program segera. Anda benar bahwa assertion bukan untuk penanganan error, namun assert mencoba menangani error tersebut dengan membatalkan. Tidakkah seharusnya Anda membuat pengecualian dan membiarkan pemanggil menangani kesalahan tersebut jika bisa? Bagaimanapun, pemanggil berada dalam posisi yang lebih baik untuk menentukan apakah kegagalan satu fungsi membuatnya tidak layak untuk melakukan hal lain. Mungkin si penelepon mencoba melakukan tiga hal yang tidak berhubungan dan masih bisa menyelesaikan dua pekerjaan lainnya dan membuang yang satu ini.assert
didefinisikan untuk memanggilabort
(bila kondisinya salah). Adapun untuk melempar pengecualian, tidak, itu tidak selalu tepat. Beberapa hal tidak dapat ditangani oleh penelepon. Penelepon tidak dapat menentukan apakah bug logika dalam fungsi perpustakaan pihak ketiga dapat dipulihkan, atau jika data yang rusak dapat diperbaiki.IMHO, pernyataan untuk memeriksa kondisi yang jika dilanggar, membuat segala sesuatu menjadi tidak masuk akal. Dan oleh karena itu Anda tidak dapat pulih dari mereka atau lebih tepatnya, pemulihan tidak relevan.
Saya akan mengelompokkannya menjadi 2 kategori:
Keduanya adalah contoh yang sepele tetapi tidak terlalu jauh dari kenyataan. Misalnya, pikirkan tentang algoritme naif yang mengembalikan indeks negatif untuk digunakan dengan vektor. Atau program tertanam di perangkat keras khusus. Atau lebih tepatnya karena omong kosong terjadi .
Dan jika ada kesalahan pengembangan seperti itu, Anda tidak boleh yakin tentang mekanisme pemulihan atau penanganan kesalahan yang diterapkan. Hal yang sama berlaku untuk kesalahan perangkat keras.
sumber