Untuk beberapa bahasa (yaitu C ++) Kebocoran sumber daya seharusnya tidak menjadi alasan
C ++ didasarkan pada RAII.
Jika Anda memiliki kode yang bisa gagal, dikembalikan atau dibuang (yaitu, sebagian besar kode normal), maka Anda harus membungkus penunjuk Anda di dalam penunjuk cerdas (dengan asumsi Anda memiliki alasan yang sangat bagus untuk tidak membuat objek Anda di tumpukan).
Kode pengembalian lebih bertele-tele
Mereka bertele-tele, dan cenderung berkembang menjadi seperti:
if(doSomething())
{
if(doSomethingElse())
{
if(doSomethingElseAgain())
{
// etc.
}
else
{
// react to failure of doSomethingElseAgain
}
}
else
{
// react to failure of doSomethingElse
}
}
else
{
// react to failure of doSomething
}
Pada akhirnya, kode Anda adalah kumpulan instruksi yang teridentifikasi (saya melihat kode semacam ini dalam kode produksi).
Kode ini dapat dengan baik diterjemahkan menjadi:
try
{
doSomething() ;
doSomethingElse() ;
doSomethingElseAgain() ;
}
catch(const SomethingException & e)
{
// react to failure of doSomething
}
catch(const SomethingElseException & e)
{
// react to failure of doSomethingElse
}
catch(const SomethingElseAgainException & e)
{
// react to failure of doSomethingElseAgain
}
Yang memisahkan kode dan pemrosesan kesalahan dengan rapi, yang bisa menjadi hal yang baik .
Kode pengembalian lebih rapuh
Jika tidak ada peringatan yang tidak jelas dari satu kompilator (lihat komentar "phjr"), mereka dapat dengan mudah diabaikan.
Dengan contoh di atas, asumsikan daripada seseorang lupa menangani kemungkinan kesalahannya (ini terjadi ...). Kesalahan diabaikan saat "dikembalikan", dan mungkin akan meledak nanti (yaitu penunjuk NULL). Masalah yang sama tidak akan terjadi dengan pengecualian.
Kesalahan tidak akan diabaikan. Terkadang, Anda ingin agar tidak meledak ... Jadi, Anda harus memilih dengan hati-hati.
Kode Pengembalian terkadang harus diterjemahkan
Katakanlah kita memiliki fungsi berikut:
- doSomething, yang bisa mengembalikan int yang disebut NOT_FOUND_ERROR
- doSomethingElse, yang dapat mengembalikan bool "false" (untuk gagal)
- doSomethingElseAgain, yang dapat mengembalikan objek Error (dengan __LINE__, __FILE__ dan setengah variabel stack.
- doTryToDoSomethingWithAllThisMess yang, yah ... Gunakan fungsi di atas, dan kembalikan kode kesalahan jenis ...
Apa jenis kembalinya doTryToDoSomethingWithAllThisMess jika salah satu fungsi yang dipanggilnya gagal?
Kode Pengembalian bukanlah solusi universal
Operator tidak dapat mengembalikan kode kesalahan. Konstruktor C ++ juga tidak bisa.
Return Codes berarti Anda tidak dapat merangkai ekspresi
Akibat wajar dari poin di atas. Bagaimana jika saya ingin menulis:
CMyType o = add(a, multiply(b, c)) ;
Saya tidak bisa, karena nilai pengembalian sudah digunakan (dan terkadang, tidak bisa diubah). Jadi nilai kembali menjadi parameter pertama, dikirim sebagai referensi ... Atau tidak.
Pengecualian diketik
Anda dapat mengirim kelas yang berbeda untuk setiap jenis pengecualian. Pengecualian sumber daya (mis. Kehabisan memori) harus ringan, tetapi hal lain bisa seberat yang diperlukan (saya suka Java Exception memberi saya seluruh tumpukan).
Setiap tangkapan kemudian dapat dikhususkan.
Jangan pernah menggunakan tangkapan (...) tanpa melempar ulang
Biasanya, Anda tidak boleh menyembunyikan kesalahan. Jika Anda tidak membuang kembali, setidaknya, catat kesalahan dalam file, buka kotak pesan, apa pun ...
Pengecualiannya adalah ... NUKE
Masalah dengan pengecualian adalah bahwa menggunakannya secara berlebihan akan menghasilkan kode yang penuh dengan percobaan / tangkapan. Tapi masalahnya ada di tempat lain: Siapa yang mencoba / menangkap kodenya menggunakan kontainer STL? Namun, kontainer tersebut dapat mengirimkan pengecualian.
Tentu saja, di C ++, jangan pernah membiarkan pengecualian keluar dari destruktor.
Pengecualian adalah ... sinkron
Pastikan untuk menangkapnya sebelum mereka mengeluarkan utas Anda, atau menyebar di dalam loop pesan Windows Anda.
Solusinya bisa mencampurkannya?
Jadi saya kira solusinya adalah membuang ketika sesuatu tidak seharusnya terjadi. Dan ketika sesuatu bisa terjadi, maka gunakan kode kembali atau parameter untuk memungkinkan pengguna bereaksi terhadapnya.
Jadi, satu-satunya pertanyaan adalah "apa yang seharusnya tidak terjadi?"
Itu tergantung pada kontrak fungsi Anda. Jika fungsi menerima pointer, tetapi menentukan pointer harus non-NULL, maka boleh saja untuk membuat pengecualian saat pengguna mengirim pointer NULL (pertanyaannya adalah, dalam C ++, ketika pembuat fungsi tidak menggunakan referensi sebagai gantinya petunjuk, tapi ...)
Solusi lain adalah menunjukkan kesalahan
Terkadang, masalah Anda adalah Anda tidak menginginkan kesalahan. Menggunakan pengecualian atau kode pengembalian kesalahan itu keren, tapi ... Anda ingin mengetahuinya.
Dalam pekerjaan saya, kami menggunakan semacam "Assert". Ini akan, tergantung pada nilai file konfigurasi, tidak peduli opsi kompilasi debug / rilis:
- catat kesalahannya
- buka kotak pesan dengan "Hei, kamu punya masalah"
- buka kotak pesan dengan "Hai, Anda punya masalah, apakah Anda ingin men-debug"
Dalam pengembangan dan pengujian, ini memungkinkan pengguna untuk menunjukkan masalah dengan tepat ketika terdeteksi, dan bukan setelahnya (ketika beberapa kode peduli tentang nilai yang dikembalikan, atau di dalam tangkapan).
Mudah untuk ditambahkan ke kode lama. Sebagai contoh:
void doSomething(CMyObject * p, int iRandomData)
{
// etc.
}
mengarahkan semacam kode yang mirip dengan:
void doSomething(CMyObject * p, int iRandomData)
{
if(iRandomData < 32)
{
MY_RAISE_ERROR("Hey, iRandomData " << iRandomData << " is lesser than 32. Aborting processing") ;
return ;
}
if(p == NULL)
{
MY_RAISE_ERROR("Hey, p is NULL !\niRandomData is equal to " << iRandomData << ". Will throw.") ;
throw std::some_exception() ;
}
if(! p.is Ok())
{
MY_RAISE_ERROR("Hey, p is NOT Ok!\np is equal to " << p->toString() << ". Will try to continue anyway") ;
}
// etc.
}
(Saya memiliki makro serupa yang hanya aktif di debug).
Perhatikan bahwa pada produksi, file konfigurasi tidak ada, jadi klien tidak pernah melihat hasil makro ini ... Tetapi mudah untuk mengaktifkannya bila diperlukan.
Kesimpulan
Ketika Anda membuat kode menggunakan kode kembali, Anda mempersiapkan diri untuk kegagalan, dan berharap benteng pengujian Anda cukup aman.
Ketika Anda membuat kode menggunakan pengecualian, Anda tahu bahwa kode Anda bisa gagal, dan biasanya menempatkan tangkapan balasan di posisi strategis yang dipilih dalam kode Anda. Tapi biasanya, kode Anda lebih banyak tentang "apa yang harus dilakukan" lalu "apa yang saya khawatirkan akan terjadi".
Tetapi ketika Anda membuat kode sama sekali, Anda harus menggunakan alat terbaik yang Anda inginkan, dan terkadang, itu adalah "Jangan pernah menyembunyikan kesalahan, dan tunjukkan secepat mungkin". Makro yang saya bicarakan di atas mengikuti filosofi ini.