Yakalamak (…) {atmak; } `kötü bir uygulama mı?


74

... Tekrar yorumlamadan yakalamanın gerçekten yanlış olduğu konusunda hemfikir olmama rağmen, bunun gibi yapıları kullanmanın inanıyorum:

try
{
  // Stuff
}
catch (...)
{
  // Some cleanup
  throw;
}

RAII'nin geçerli olmadığı durumlarda kabul edilebilir . (Lütfen sormayın ... şirketimdeki herkes nesne yönelimli programlamayı sevmiyor ve RAII genellikle “işe yaramaz okul eşyaları” olarak görülüyor ...)

İş arkadaşlarım, hangi istisnaların atılacağını her zaman bilmeniz gerektiğini ve her zaman aşağıdaki gibi yapıları kullanabileceğinizi söylüyor:

try
{
  // Stuff
}
catch (exception_type1&)
{
  // Some cleanup
  throw;
}
catch (exception_type2&)
{
  // Some cleanup
  throw;
}
catch (exception_type3&)
{
  // Some cleanup
  throw;
}

Bu durumlarla ilgili kabul edilmiş iyi bir uygulama var mı?


3
@Pubby: Aynı sorunun bu olduğundan emin değil. Bağlantılı soru hakkında daha fazla "Ben yakalamak mı olduğunu ...ise benim soru odak" I daha iyi yakalamak Should " ...ya <specific exception>yeniden atma önce"
ereOn

53
Bunu söylediğim için üzgünüm, ancak RAII olmadan C ++, C ++ değil.
fredoverflow

46
Öyleyse, inek işçileriniz belirli bir problemle başa çıkmak için icat edilmiş ve daha sonra hangi alternatiflerin kullanılması gerektiğine dair tereddüt etmemiş midir? Bunu söylediğim için üzgünüm, ama aptalca görünüyor , hangi şekilde bakarsam bak.
sbi

11
“yeniden yakalamadan ... yakalamak gerçekten yanlış” - yanılıyorsun. In main, catch(...) { return EXIT_FAILURE; }bir hata ayıklayıcı altında çalışan kodda haklı olabilir. Yakalamazsanız, yığın açılmamış olabilir. Sadece hata ayıklayıcınız, bırakmalarını istediğiniz yakalanmamış istisnalar tespit ettiğinde main.
Steve Jessop,

3
... bu yüzden bir "programlama hatası" olsa bile, mutlaka bilmek istemediğinizi takip etmez. Her neyse, meslektaşlarınız iyi bir yazılım uzmanı değil, sbi'nin dediği gibi, başlangıçta kronik olarak zayıf olan bir durumla nasıl başa çıkılacağı hakkında konuşmanın çok zor olduğunu söylüyor.
Steve Jessop,

Yanıtlar:


196

Arkadaşlarım her zaman hangi istisnaların atılacağını bilmeniz gerektiğini söylüyor [...]

İş arkadaşınız, söylemekten nefret ediyorum, kesinlikle genel amaçlı kütüphanelerde hiç çalışmadı.

Nasıl dünyada olduğu gibi bir sınıf olabilir std::vectorhatta taklit hala istisna güvenliğini garanti ederken, kopya kurucular atmak olacağını bilmek?

Callee'nin derleme zamanında ne yapacağını her zaman bilirseniz, polimorfizm işe yaramaz! Bazen bütün amaç daha düşük bir seviyede olur, bu nedenle özellikle ne uzak soyut etmektir yok neler olduğunu bilmek istiyorum!


32
Aslında, istisnaların atılacağını bilseler bile . Bu kod çoğaltmanın amacı nedir? Kullanım farklı olmadıkça, bilginizi göstermek için istisnaları numaralandırmanın bir anlamı yok.
Michael Krelin - Hacker

3
@ MichaelKrelin-hacker: Bu da. Ayrıca, istisna spesifikasyonlarını kullanım dışı bıraktıklarını da ekleyelim, çünkü koddaki tüm olası istisnaları listelemek daha sonra böceklere neden olma eğilimindeydi ... şimdiye kadarki en kötü fikir.
Mehrdad

4
Ve beni rahatsız eden şey, “işe yaramaz okul eşyaları” olarak kullanışlı ve kullanışlı bir teknik görmekle birleştiğinde böyle bir tutumun kaynağı olabilir. Ama iyi ...
Michael Krelin - Hacker

1
+1, tüm olası seçeneklerin numaralandırılması gelecekteki bir başarısızlık için mükemmel bir reçetedir, neden birileri bunu yapmayı seçti ...?
littleadv

2
Güzel cevap Eğer birinin desteklemesi gereken bir derleyicinin X alanında bir hata varsa, X alanından işlevsellik kullanmanın akıllı olmadığını, en azından doğrudan kullanmayacağını söylemekten faydalanabilir. Örneğin, şirket hakkında bilgi verildiğinde, bu alanda bazı aptal böcekleri olan Visual C ++ 6.0 kullanıyorlarsa şaşırmam. (İstisna nesne yıkıcılarının iki kez çağrıldığı istisna nesne yıkıcıları gibi) bu gün, ancak tezahür ettirmek için dikkatli düzenlemeler yapılması gerekiyor.
Alf P. Steinbach

44

Yakalandığın şey, birinin kekini almaya ve onu da yemeye çalışan birinin cehennemi.

RAII ve istisnalar el ele gitmek üzere tasarlanmıştır. RAII sen yok ediliş biçimidir sahip bir sürü yazmaya catch(...)temizlik yapmak için ifadeleri. Elbette, otomatik olarak gerçekleşecek. İstisnalar, RAII nesnelerle çalışmanın tek yoludur, çünkü yapıcılar yalnızca başarılı olabilir veya atabilir (veya nesneyi bir hata durumuna geçirebilir, ancak bunu kim ister?).

Bir catchaçıklama iki şeyden birini yapabilir: bir hatayı veya istisnai durumu ele alır veya temizleme işi yapar. Bazen her ikisini de yapar, ancak her catchifade bunlardan en az birini yapmak için vardır.

catch(...)uygun istisna işlemleri yapamaz. Özel durumun ne olduğunu bilmiyorsunuz; istisna hakkında bilgi edemezsiniz. Bir istisnanın belirli bir kod bloğundaki bir şey tarafından atıldığı gerçeğinden başka hiçbir bilginiz yok . Böyle bir blokta yapabileceğiniz tek meşru şey, temizlik yapmaktır. Ve bu, temizliğin sonunda istisnayı tekrar atmak anlamına gelir.

RAII size istisnai durumlarla ilgili olarak size ücretsiz temizlik sağlar. Her şey RAII uygun şekilde kapsüllenmişse, her şey uygun şekilde temizlenir. Artık catchifadeleri temizleme işlemi yapmanız gerekmez. Bu durumda, bir catch(...)ifade yazmak için hiçbir sebep yoktur .

Bu yüzden geçici olarak bunun catch(...)kötü bir şey olduğuna katılıyorum .

Bu hüküm RAII'nin doğru kullanımıdır. Onsuz olduğundan, gerek bazı temizlik yapmak mümkün. Etrafında dolaşmak yok; temizlik işini yapabilmek zorundasın. Bir istisna atmanın kodu makul bir durumda bırakacağından emin olmanız gerekir. Ve catch(...)bir olan hayati bunu yaparken de aracı.

Biri olmadan diğeri olamaz. Hem RAII hem catch(...) de kötü olduğunu söyleyemezsiniz . Bunlardan en az birine ihtiyacınız var; Aksi takdirde, istisna güvende değilsindir.

Tabii ki, catch(...)RAII'nin bile yasaklayamayacağı, nadiren geçerli bir kullanımı var : exception_ptrbaşkasına iletmek. Tipik olarak bir promise/futureveya benzer bir arayüz aracılığıyla .

İş arkadaşlarım, hangi istisnaların atılacağını her zaman bilmeniz gerektiğini ve her zaman aşağıdaki gibi yapıları kullanabileceğinizi söylüyor:

İş arkadaşınız bir aptal (veya sadece korkunç derecede cahil). Bu, ne kadar kopyala ve yapıştır kodu yazmanızı önerdiği nedeniyle hemen anlaşılmalıdır. Bu açıklama ifadelerinin her biri için temizleme tamamen aynı olacaktır . Okunabilirlikten bahsetmiyorum bile, bu bir bakım kabusu.

Kısacası: Bu, RAII'nin çözmek için yarattığı problemdir (diğer problemleri çözmemesi değil).

Beni bu fikre karıştıran şey, genel olarak çoğu insanın RAII'nin kötü olduğunu iddia ettiği duruma ters yönde olmasıdır. Genel olarak, argüman "RAII kötüdür, çünkü yapıcı başarısızlığını işaret etmek için istisnalar kullanmak zorundasınız. Ancak istisnalar atamazsınız çünkü güvenli değil ve catchher şeyi temizlemek için çok fazla ifadeniz olması gerekir ." Bu, bozuk bir argümandır çünkü RAII , RAII eksikliğinin yarattığı sorunu çözmektedir .

Muhtemelen, RAII'e karşı olduğu için ayrıntıları gizliyor. Yıkıcı çağrıları otomatik değişkenlerde hemen görünmez. Yani dolaylı olarak adlandırılan kodu alırsınız. Bazı programcılar bundan nefret ediyor. Görünüşe göre, catchhepsi kopyala ve yapıştır koduyla aynı şeyi yapan 3 ifadeye sahip olduklarını düşündükleri noktaya göre daha iyi bir fikirdir.


2
Size sağlayan kod yazmayın görünüyor güçlü istisna güvenlik garantisi. RAII temel garantiyi sağlamada yardımcı olur . Ancak, güçlü bir güvence sağlamak için, sistemi işlevsellikten önce aldığı duruma geri döndürmek için bazı eylemleri geri almalısınız. Temel garanti temizlik , güçlü garanti geri almadır . Geri alma işlemi işleve özgüdür. Yani onu "RAII" ye koyamazsın. Ve bu, hepsini yakala bloğunun kullanışlı hale geldiği zamandır. Kodunuzu güçlü bir garanti ile yazıyorsanız, hepsini çok kullanırsınız.
anton_rh

@anton_rh: Belki, ama bu durumlarda bile, hepsini yakala ifadeleri son çare aracıdır . Tercih edilen araç, istisnaya geri dönmeniz gereken herhangi bir durumu değiştirmeden önce atılan her şeyi yapmaktır . Açıkçası, her durumda bu şekilde her şeyi uygulayamazsınız, ancak güçlü istisna garantisini almanın ideal yolu.
Nicol Bolas

14

Gerçekten iki yorum. Birincisi, ideal bir dünyada, üçüncü taraf kütüphaneleriyle uğraşıyorsanız veya bir Microsoft derleyicisi ile derliyorsanız, pratikte ne gibi istisnalar olabileceğini her zaman bilmelisiniz. Ancak, noktadan daha fazlası; olası istisnaların tamamını tam olarak bilseniz bile, bu burada geçerli mi? catch (...)niyetini çok daha iyi ifade eder catch ( std::exception const& ), hatta tüm olası istisnaların kaynaklandığı varsayılarak std::exception(ideal dünyada böyle olurdu). Birkaç istisna bloğu kullanmaya gelince, tüm istisnalar için ortak bir temel yoksa: bu tamamen düpedüz şaşırtma ve bakım kabusu. Tüm davranışların aynı olduğunu nasıl anlarsınız? Ve amacın bu muydu? Davranışı değiştirmek zorunda kalırsanız ne olur (örneğin, hata düzeltme)? Birini kaçırmak çok kolay.


3
Aslında, benim iş arkadaşınız gelmez kendi istisna sınıfı tasarlanmış değil kaynaklanıyor std::exceptionve bizim kod temeli arasında kullanımını zorlamak için her gün çalışır. Tahminim, kendisinin yazmadığı kod ve dış kütüphaneleri kullandığım için beni cezalandırmaya çalıştığı.
26'da

17
@ereOn İş arkadaşınız çok ciddi bir eğitime ihtiyaç duyuyor gibi geliyor. Her durumda, muhtemelen onun tarafından yazılan kütüphaneleri kullanmaktan kaçınırdım.

2
Şablonlar ve hangi istisnaların atılacağını bilmek, fıstık ezmesi ve ölü kertenkeleler gibi bir araya gelir. Kadar basit bir şey, std::vector<>herhangi bir nedenden dolayı herhangi bir istisna oluşturabilir.
David Thornley

3
Lütfen bize söyleyin, tam olarak çağrı ağacının aşağısındaki yarınların hata düzeltmesinden ne gibi bir istisna atılacağını nasıl biliyorsunuz?
mattnz

11

Bence meslektaşınız iyi bir tavsiyede bulundu - bildiğiniz istisnaları bir catchblokta tekrar atmadığınızda kullanmalısınız.

Bu şu anlama gelir:

try
{
  // Stuff
}
catch (...)
{
  // General stuff
}

Kötü çünkü sessizce herhangi bir hatayı gizleyecektir .

Ancak:

try
{
  // Stuff
}
catch (exception_type_we_can_handle&)
{
  // Deal with the known exception
}

Sorun değil - neyle uğraştığımızı biliyoruz ve onu arama koduna maruz bırakmak zorunda değiliz.

Aynı şekilde:

try
{
  // Stuff
}
catch (...)
{
  // Rollback transactions, log errors, etc
  throw;
}

İyi, en iyi uygulama bile, genel hatalarla başa çıkacak kodun kendilerine neden olan kodda olması gerekir. Bir işlemin geri ya da neyse geri alınması gerektiğini bilmek, callee'ye güvenmekten iyidir.


9

Herhangi bir evet veya hayır cevabına bunun neden böyle olduğuna dair bir mantık eşlik etmelidir.

Bunun yanlış olduğunu söylemek, bu şekilde öğretildiğim için fanatizm körlüğüdür.

Aynı şekilde //Some cleanup; throwbirkaç kez yazmak, örneğinizde olduğu gibi yanlış çünkü kod çoğaltması ve bu bir bakım yükü. Sadece bir kez yazmak daha iyidir.

Bir yazma catch(...)tüm istisnalar susturmak için yalnızca nasıl işleneceğini biliyor istisnalar işlemesi gerektiğini, çünkü yanlış olduğunu ve joker ile size beklediğinizden daha fazla cath edebilir ve bunu yaparken önemli hatalar tamamen kesilir.

Ancak, bir denemeden sonra yeniden düşünürseniz catch(...), o zaman gerekçeyi istisnayı ele almadığınızdan sonraki mantık geçerli olmaz, bu yüzden bunun önerilmemesi için hiçbir neden yoktur.

Aslında bunu hassas fonksiyonlarda oturum açmak için yaptım.

void DoSomethingImportant()
{
    try
    {
        Log("Going to do something important");
        DoIt();
    }
    catch (std::exception &e)
    {
        Log("Error doing something important: %s", e.what());
        throw;
    }
    catch (...)
    {
        Log("Unexpected error doing something important");
        throw;
    }
    Log("Success doing something important");
}

2
Umalım Log(...)da atamazsın.
Deduplicator

2

Buradaki yayınların havasına genel olarak katılıyorum, özel istisnaları yakalama modelini gerçekten beğenmedim - bunun sözdiziminin hala başlangıç ​​aşamasında olduğunu ve fazlalık koduyla başa çıkamadığını düşünüyorum.

Ama herkes bunu söylediğinden, onları sık sık kullanmama rağmen sık sık "catch (Exception e)" ifadelerimden birine baktım ve "Lanet olsun, keşke aradım" dedi. O zamanki özel istisnalar dışında "çünkü daha sonra geldiğinizde, niyenin ne olduğunu ve müşterinin bir bakışta neye atması muhtemel olduğunu bilmek genellikle güzeldir.

"Daima x'i kullan" tutumunu haklı çıkarmıyorum, sadece zaman zaman listelenenleri görmek gerçekten güzel olur ve bazılarının bu şekilde "doğru" yol olduğunu düşündüğünden eminim.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.