melempar std :: exception baru vs membuang std :: exception

113

sambil melihat beberapa kode yang saya temukan:

throw /*-->*/new std::exception ("//...

dan saya selalu berpikir bahwa Anda tidak perlu / Anda tidak boleh menggunakan di newsini.
Bagaimana cara yang benar, keduanya OK, jika demikian apakah ada perbedaan?

BTW dari apa yang bisa saya lihat saat "grepping" dengan PowerShell boost libs tidak pernah digunakan throw new.

PS juga saya temukan beberapa kode CLI yang digunakan throw gcnew. Apakah itu oke?

NoSenseEtAl
sumber
1
Saya pikir throw gcnewakan berguna misalnya. jika Anda ingin kode terkelola menangkap pengecualian Anda. Bisakah seseorang mengoreksi saya tentang itu?
jpalecek
1
.Net berurusan dengan pengecualian oleh pointer, jadi membuang gcnew adalah hal yang benar untuk dilakukan di sana.
Sebastian Redl
1
@ SebastianRedl. Net "pointer" mungkin rancu? Padahal tentu saja tidak. System::Exceptionumumnya mengacu pada objek yang dikelola di heap yang dikumpulkan sampah. Saya selalu dilempar gcnewdan tertangkap System::Exception ^. Tentu saja saya menggunakan finallysepanjang waktu di C ++ / CLI juga, meskipun tidak sering mencampur dengan pengecualian C ++ di tryblok yang sama , saya tidak yakin mengapa.

Jawaban:

89

Cara konvensional untuk melempar dan menangkap pengecualian adalah dengan melempar objek pengecualian dan menangkapnya dengan referensi (biasanya constreferensi). Bahasa C ++ memerlukan kompilator untuk menghasilkan kode yang sesuai untuk membangun objek pengecualian dan membersihkannya dengan benar pada waktu yang tepat.

Melempar pointer ke objek yang dialokasikan secara dinamis bukanlah ide yang bagus. Pengecualian seharusnya memungkinkan Anda menulis kode yang lebih kuat saat menghadapi kondisi kesalahan. Jika Anda melempar objek pengecualian dengan cara konvensional, Anda dapat yakin bahwa objek tersebut ditangkap oleh klausa catch yang menamai tipe yang benar, oleh a catch (...), apakah objek tersebut kemudian dilempar kembali atau tidak, objek tersebut akan dihancurkan dengan benar pada waktu yang tepat. (Satu-satunya pengecualian adalah jika tidak pernah tertangkap sama sekali tetapi ini adalah situasi yang tidak dapat dipulihkan, bagaimanapun cara Anda melihatnya.)

Jika Anda melempar pointer ke objek yang dialokasikan secara dinamis, Anda harus memastikan bahwa apa pun tampilan stack panggilan pada titik yang Anda inginkan untuk menampilkan pengecualian, ada blok catch yang menamai tipe pointer yang benar dan memiliki deletepanggilan yang sesuai . Exception Anda tidak boleh ditangkap oleh catch (...)kecuali blok itu melempar kembali pengecualian yang kemudian ditangkap oleh blok penangkap lain yang menangani pengecualian dengan benar.

Secara efektif, ini berarti Anda telah menggunakan fitur penanganan pengecualian yang akan mempermudah penulisan kode yang kuat dan membuatnya sangat sulit untuk menulis kode yang benar dalam semua situasi. Ini mengesampingkan masalah bahwa hampir tidak mungkin bertindak sebagai kode pustaka untuk kode klien yang tidak mengharapkan fitur ini.

CB Bailey
sumber
1
"melempar objek pengecualian" tumpukan atau tumpukan teman saya? Stack atau heap? (Mungkin saya sedang melihat contoh global yang buruk di suatu tempat) oh dan jika tumpukan, lalu apa cakupan yang sesuai?
@ebyrob: Saya tidak begitu yakin dengan apa yang Anda tanyakan tetapi sepertinya Anda ingin tahu tentang penyimpanan dan / atau masa pakai objek pengecualian yang mungkin dijawab di sini . Jika tidak, Anda mungkin lebih baik mengajukan pertanyaan terpisah.
CB Bailey
31

Tidak perlu digunakan newsaat melempar pengecualian.

Tulis saja:

throw yourexception(yourmessage);

dan tangkap sebagai:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

Perhatikan bahwa yourexceptionharus berasal dari std::exceptionsecara langsung atau tidak langsung.

Nawaz
sumber
7
Mengapa? kenapa tidak digunakan new? mengapa berasal yourexceptiondari std::exception?
Walter
Ketika saya malas (yang sering terjadi) mengapa tidak throw std::exception;berhasil? g ++ tampaknya tidak akan mengkompilasinya ...
7
@ebyrob: std::exceptionadalah tipe, dan Anda tidak bisa melempar tipe , Anda sudah melempar objek . Jadi sintaksnya harus seperti ini: throw std::exception();Itu akan dikompilasi. Sekarang betapa bagusnya itu, adalah pertanyaan yang sama sekali berbeda.
Nawaz
22

Melempar new std::exceptionbenar jika situs panggilan mengharapkan untuk menangkap std::exception*. Tapi tidak ada yang akan mengharapkan untuk menangkap pointer ke sebuah pengecualian. Bahkan jika Anda mendokumentasikan itulah yang dilakukan fungsi Anda dan orang-orang membaca dokumentasinya, mereka masih cenderung lupa dan mencoba menangkap referensi ke suatu std::exceptionobjek.


sumber
27
Melempar new std::exceptionhanya benar jika situs panggilan mengharapkan untuk menangkap pointer DAN mengharapkan untuk mengambil alih pengelolaan pengecualian alokasi DAN tidak akan pernah ada kasus di mana fungsi Anda akan dipanggil oleh sesuatu yang tidak secara eksplisit menangkap pointer yang benar ( catch(...)atau tidak ada penanganan sama sekali) jika tidak maka akan ada kebocoran objek. Singkatnya, ini dapat diperkirakan sebagai "tidak pernah".
CB Bailey
Sungguh penasaran bagaimana jawaban ini diterima, padahal sebenarnya komentar @ CharlesBailey itulah jawaban yang benar.
John Dibling
@ John: Itu juga terlintas dalam pikiran saya. Tapi saya pikir pukulan satu-dua memiliki efek yang baik dengan saya memberikan ringkasan kering dan Charles dengan lucu mengembangkan berbagai cara orang cenderung lupa menanganinya dengan benar. Sayangnya, Anda tidak mendapatkan reputasi dari komentar yang dipilih secara positif.
Charles tidak memberikan jawabannya, dan A ini (tidak seperti yang lainnya) memiliki penjelasan di A dan komentar.
NoSenseEtAl
9

FAQ C ++ memiliki diskusi yang bagus tentang ini:

  1. https://isocpp.org/wiki/faq/exceptions#what-to-catch
  2. https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

Pada dasarnya "kecuali ada alasan bagus untuk tidak melakukannya, tangkap dengan referensi. Hindari menangkap dengan nilai, karena itu menyebabkan salinan dibuat dan salinan dapat memiliki perilaku yang berbeda dari apa yang dilemparkan. Hanya dalam keadaan yang sangat khusus Anda dapat menangkap dengan pointer. "

pengguna1202136
sumber
2
Seperti biasa, FAQ berisi kata-kata yang buruk. Anda bisa menangkap dengan nilai atau referensi. Sebuah pointer kebetulan menjadi nilai (yang Anda tangkap dengan nilai atau referensi). Ingat jenisnya Aberbeda dari jenisnya A*jadi jika saya melakukannya throw A()saya TIDAK bisa menangkapnya catch(A* e)karena jenisnya sama sekali berbeda.
Martin York
Tautan ini sekarang rusak.
stephenspann
1
Saya memperbaiki tautan @spanndemic
user1202136
1

Operator baru tidak dapat menjamin bahwa itu tidak akan pernah menimbulkan pengecualian. Karena alasan ini, menggunakannya untuk menampilkan pengecualian "valid" (dimaksudkan) akan menghasilkan kode yang tidak dijamin tidak akan mogok. Karena mungkin hanya ada satu pengecualian pada satu waktu, dan program Anda mencoba membuang dua pengecualian sebelum salah satu dari mereka dapat ditangkap, hal terbaik yang dapat dilakukan implementasi adalah segera membatalkan program Anda, misalnya dengan memanggil std :: terminate.

zkoza
sumber