Penanganan pengecualian di C ++ terbatas untuk mencoba / melempar / menangkap. Tidak seperti Object Pascal, Java, C # dan Python, bahkan dalam C ++ 11, finally
konstruksinya belum diimplementasikan.
Saya telah melihat banyak sekali literatur C ++ yang membahas "pengecualian kode aman". Lippman menulis bahwa pengecualian kode aman adalah topik yang penting namun canggih, sulit, di luar cakupan Primer-nya - yang tampaknya menyiratkan bahwa kode aman tidak mendasar bagi C ++. Herb Sutter menyediakan 10 bab untuk topik ini dalam C ++ Luar Biasa!
Namun bagi saya tampaknya banyak masalah yang dihadapi ketika mencoba untuk menulis "pengecualian kode aman" dapat diselesaikan dengan baik jika finally
konstruksinya diimplementasikan, yang memungkinkan programmer untuk memastikan bahwa bahkan jika ada pengecualian, program dapat dipulihkan. ke keadaan aman, stabil, bebas kebocoran, dekat dengan titik alokasi sumber daya dan kode berpotensi bermasalah. Sebagai programmer Delphi dan C # yang sangat berpengalaman saya menggunakan try .. akhirnya memblokir kode saya secara ekstensif, seperti halnya kebanyakan programmer dalam bahasa ini.
Mempertimbangkan semua 'lonceng dan peluit' yang diimplementasikan dalam C ++ 11, saya terkejut menemukan bahwa 'akhirnya' masih belum ada.
Jadi, mengapa finally
konstruksinya tidak pernah diimplementasikan dalam C ++? Ini benar-benar bukan konsep yang sangat sulit atau lanjutan untuk dipahami dan berjalan jauh ke arah membantu programmer untuk menulis 'pengecualian kode aman'.
sumber
finally
dalam C ++, dan teknik apa untuk penanganan pengecualian yang digunakan sebagai gantinya?" valid dan sesuai topik untuk situs ini. Jawaban yang ada mencakup ini dengan baik, saya pikir. Mengubahnya menjadi diskusi tentang "Apakah alasan desainer C ++ untuk tidak termasukfinally
berharga?" dan "Harusfinally
ditambahkan ke C ++?" dan melanjutkan diskusi lintas komentar pada pertanyaan dan setiap jawaban tidak cocok dengan model situs tanya jawab ini.Jawaban:
Seperti beberapa komentar tambahan pada jawaban @ Nemanja (yang, karena mengutip Stroustrup, benar-benar sama baiknya dengan jawaban yang Anda dapatkan):
Ini benar-benar hanya masalah memahami filosofi dan idiom C ++. Ambil contoh operasi yang membuka koneksi database pada kelas persisten dan harus memastikan bahwa itu menutup koneksi jika pengecualian dilemparkan. Ini adalah masalah keamanan pengecualian dan berlaku untuk bahasa apa pun dengan pengecualian (C ++, C #, Delphi ...).
Dalam bahasa yang menggunakan
try
/finally
, kode tersebut mungkin terlihat seperti ini:Sederhana dan mudah. Namun, ada beberapa kelemahan:
finally
blok, kalau tidak saya membocorkan sumber daya.DoRiskyOperation
lebih dari satu pemanggilan metode tunggal - jika saya memiliki beberapa pemrosesan yang harus dilakukan ditry
blok - makaClose
operasi dapat berakhir menjadi agak jauh dariOpen
operasi. Saya tidak bisa menulis pembersihan di sebelah akuisisi saya.try
/finally
blok.Pendekatan C ++ akan terlihat seperti ini:
Ini sepenuhnya menyelesaikan semua kelemahan dari
finally
pendekatan. Ini memiliki beberapa kelemahan sendiri, tetapi mereka relatif kecil:ScopedDatabaseConnection
sendiri kelas itu. Namun, ini adalah implementasi yang sangat sederhana - hanya 4 atau 5 baris kode.Secara pribadi, mengingat kelebihan dan kekurangan ini, saya menemukan teknik RAII yang lebih disukai
finally
. Jarak tempuh Anda mungkin beragam.Akhirnya, karena RAII adalah idiom mapan dalam C ++, dan untuk meringankan pengembang dari beberapa beban menulis banyak
Scoped...
kelas, ada perpustakaan seperti ScopeGuard dan Boost.ScopeExit yang memfasilitasi semacam pembersihan deterministik ini.sumber
using
pernyataan, yang secara otomatis membersihkan objek yang mengimplementasikanIDisposable
antarmuka. Jadi, meskipun mungkin salah, sangat mudah melakukannya dengan benar.try/finally
konstruk karena kompiler tidak memaparkantry/finally
konstruk dan satu-satunya cara untuk mengaksesnya adalah melalui kelas berbasis idiom desain, bukan merupakan "keuntungan;" itu adalah definisi inversi abstraksi.finally
. Seperti yang saya katakan, jarak tempuh Anda mungkin berbeda.Dari Mengapa C ++ tidak menyediakan konstruksi "akhirnya"? di FAQ Gaya dan Teknik C ++ Bjarne Stroustrup :
sumber
finally
konstruksi selalu tidak berguna selamanya, terlepas dari apa yang dikatakan Strousup. Fakta bahwa menulis "pengecualian kode aman" adalah masalah besar dalam C ++ adalah buktinya. Heck, C # memiliki kedua destructor danfinally
, dan keduanya digunakan.Alasan yang tidak dimiliki C ++
finally
adalah karena tidak diperlukan dalam C ++.finally
digunakan untuk mengeksekusi beberapa kode terlepas dari apakah pengecualian telah terjadi atau tidak, yang hampir selalu merupakan semacam kode pembersihan. Dalam C ++, kode pembersihan ini harus dalam destruktor dari kelas yang relevan dan destruktor akan selalu dipanggil, seperti halnya sebuahfinally
blok. Ungkapan menggunakan destructor untuk pembersihan Anda disebut RAII .Di dalam komunitas C ++ mungkin ada lebih banyak pembicaraan tentang kode 'pengecualian aman', tetapi hampir sama pentingnya dalam bahasa lain yang memiliki pengecualian. Inti dari kode 'pengecualian aman' adalah bahwa Anda memikirkan dalam keadaan apa kode Anda dibiarkan jika pengecualian terjadi di salah satu fungsi / metode yang Anda panggil.
Dalam C ++, kode 'exception safe' sedikit lebih penting, karena C ++ tidak memiliki pengumpulan sampah otomatis yang menangani objek yang ditinggalkan yatim karena pengecualian.
Alasan keamanan pengecualian lebih banyak dibahas di komunitas C ++ mungkin juga berasal dari kenyataan bahwa di C ++ Anda harus lebih waspada terhadap apa yang bisa salah, karena ada lebih sedikit jaring pengaman default dalam bahasa tersebut.
sumber
finally
standar C ++, saya pikir aman untuk menyimpulkan bahwa komunitas C ++ tidak menganggapthe absence of finally
masalah. Sebagian besar bahasa memilikifinally
kekurangan deterministik konsisten yang dimiliki oleh C ++. Saya melihat bahwa Delphi memiliki keduanya, tetapi saya tidak tahu sejarahnya dengan cukup baik untuk mengetahui yang mana yang ada terlebih dahulu.finally
". Saya tidak dapat mengingat tugas apa pun yang akan menjadi lebih mudah, seandainya saya memiliki akses ke sana.Yang lain telah membahas RAII sebagai solusinya. Ini solusi yang sangat bagus. Tetapi itu tidak benar-benar membahas mengapa mereka tidak menambahkan
finally
juga karena itu adalah hal yang diinginkan secara luas. Jawabannya lebih mendasar pada desain dan pengembangan C ++: selama pengembangan C ++ mereka yang terlibat telah sangat menentang pengenalan fitur desain yang dapat dicapai dengan menggunakan fitur lain tanpa banyak keributan dan terutama di mana ini membutuhkan pengenalan kata kunci baru yang dapat membuat kode lama tidak kompatibel. Karena RAII menyediakan alternatif yang sangat fungsionalfinally
dan Anda sebenarnya dapat memutar sendirifinally
di C ++ 11, ada sedikit panggilan untuk itu.Yang perlu Anda lakukan adalah membuat kelas
Finally
yang memanggil fungsi yang diteruskan ke konstruktor di destruktor itu. Maka Anda dapat melakukan ini:Kebanyakan programmer C ++ asli, secara umum, lebih suka objek RAII yang dirancang dengan rapi.
sumber
Finally atEnd([&] () { database.close(); });
juga, saya membayangkan yang berikut ini lebih baik:{ Finally atEnd(...); try {...} catch(e) {...} }
(Saya mengangkat finalizer dari blok uji coba sehingga dieksekusi setelah blok tangkapan.)Anda dapat menggunakan pola "perangkap" - bahkan jika Anda tidak ingin menggunakan blok coba / tangkap.
Letakkan objek sederhana dalam cakupan yang diperlukan. Dalam destruktor objek ini masukkan logika "akhirnya" Anda. Tidak peduli apa, ketika tumpukan dibatalkan, destruktor objek akan dipanggil dan Anda akan mendapatkan permen Anda.
sumber
Nah, Anda bisa mengurutkan roll Anda sendiri
finally
, menggunakan Lambdas, yang akan mendapatkan yang berikut untuk dikompilasi dengan baik (menggunakan contoh tanpa RAII tentu saja, bukan potongan kode terbaik):Lihat artikel ini .
sumber
Saya tidak yakin saya setuju dengan pernyataan di sini bahwa RAII adalah superset dari
finally
. Tumit achilles dari RAII sederhana: pengecualian. RAII diimplementasikan dengan destruktor, dan selalu salah dalam C ++ untuk membuang destruktor. Itu berarti bahwa Anda tidak dapat menggunakan RAII ketika Anda perlu membuang kode pembersihan Anda. Jikafinally
diterapkan, di sisi lain, tidak ada alasan untuk percaya bahwa itu tidak sah untuk dilempar darifinally
blok.Pertimbangkan jalur kode seperti ini:
Jika kita punya
finally
kita bisa menulis:Tapi tidak ada cara, yang bisa saya temukan, untuk mendapatkan perilaku yang setara menggunakan RAII.
Jika seseorang tahu bagaimana melakukan ini di C ++, saya sangat tertarik dengan jawabannya. Saya bahkan akan senang dengan sesuatu yang mengandalkan, misalnya, menegakkan bahwa semua pengecualian diwarisi dari satu kelas dengan beberapa kemampuan khusus atau sesuatu.
sumber
complex_cleanup
bisa melempar, maka Anda dapat memiliki kasus di mana dua pengecualian tanpa tertangkap sedang terbang sekaligus, sama seperti yang Anda lakukan dengan RAII / destruktor, dan C ++ menolak untuk mengizinkan ini. Jika Anda ingin pengecualian asli dilihat, makacomplex_cleanup
harus mencegah pengecualian, seperti halnya dengan RAII / destruktor. Jika Anda ingincomplex_cleanup
pengecualian terlihat, maka saya pikir Anda dapat menggunakan blok coba / tangkap bersarang - meskipun ini bersinggungan dan sulit untuk dimasukkan ke dalam komentar, jadi itu layak untuk pertanyaan terpisah.finally
blok putatif jelas akan bekerja sama dengan lemparan dicatch
blok pengecualian dalam penerbangan WRT - tidak meneleponstd::terminate
. Pertanyaannya adalah "mengapa tidak adafinally
di C ++?" dan semua jawaban mengatakan "Anda tidak membutuhkannya ... RAII FTW!" Maksud saya adalah ya, RAII baik-baik saja untuk kasus-kasus sederhana seperti manajemen memori, tetapi sampai masalah pengecualian diselesaikan, memerlukan terlalu banyak pemikiran / overhead / perhatian / desain ulang untuk menjadi solusi tujuan umum.