C ++ 'da bir istisna tanımlayıcı kullanmalı mıyım?


123

C ++ 'da, bir işlevin bir istisna belirticisi kullanarak bir istisna atıp atamayacağını belirtebilirsiniz. Örneğin:

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

Aşağıdakilerden dolayı bunları gerçekten kullanmak konusunda şüpheliyim:

  1. Derleyici, istisna belirleyicileri herhangi bir titiz şekilde gerçekten zorlamaz, bu nedenle faydalar büyük değildir. İdeal olarak, bir derleme hatası almak istersiniz.
  2. Bir işlev bir istisna tanımlayıcısını ihlal ederse, standart davranışın programı sonlandırmak olduğunu düşünüyorum.
  3. VS.Net'te, throw (X) 'i fırlatma (...) olarak ele alır, bu nedenle standarda bağlılık güçlü değildir.

İstisna tanımlayıcıların kullanılması gerektiğini düşünüyor musunuz?
Lütfen "evet" veya "hayır" olarak yanıtlayın ve cevabınızı gerekçelendirmek için bazı nedenler belirtin.


7
"atmak (...)" standart C ++ değildir. Bazı derleyicilerde bir uzantı olduğuna ve genel olarak istisna belirtimi olmamasıyla aynı anlama sahip olduğuna inanıyorum.
Richard Corden

Yanıtlar:


97

Hayır.

İşte bunun birkaç örneği:

  1. İstisna özellikleriyle şablon kodu yazmak imkansızdır,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }

    Kopyalar atabilir, geçen parametre atabilir ve x()bazı bilinmeyen istisnalar atabilir.

  2. İstisna spesifikasyonlar, genişletilebilirliği yasaklama eğilimindedir.

    virtual void open() throw( FileNotFound );

    dönüşebilir

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );

    Bunu gerçekten şu şekilde yazabilirsin

    throw( ... )

    İlki genişletilebilir değil, ikincisi aşırı hırslı ve üçüncüsü, sanal işlevler yazarken gerçekten kastettiğin şey.

  3. Eski kod

    Başka bir kitaplığa dayanan bir kod yazdığınızda, bir şeyler korkunç bir şekilde ters gittiğinde bunun ne işe yarayacağını gerçekten bilemezsiniz.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }

    gatıldığında sona erecek lib_f(). Bu (çoğu durumda) gerçekten istediğiniz şey değildir. std::terminate()asla çağrılmamalıdır. Sessizce / şiddetle ölmektense, yığın izleme alabileceğiniz işlenmemiş bir istisna ile uygulamanın çökmesine izin vermek her zaman daha iyidir.

  4. Yaygın hataları döndüren ve istisnai durumlarda fırlatan kod yazın.

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }

Yine de, kitaplığınız kendi istisnalarınızı ortaya çıkardığında, amacınızı belirtmek için istisna özelliklerini kullanabilirsiniz.


1
3'te, teknik olarak std :: beklenmeyen olacaktır, std :: terminate değil. Ancak bu işlev çağrıldığında, varsayılan abort () işlevini çağırır. Bu bir çekirdek dökümü oluşturur. Bu, işlenmemiş bir istisnadan nasıl daha kötü? (temelde aynı şeyi yapar)
Greg Rogers

6
@Greg Rogers: Yakalanmamış bir istisna, yığın çözülmeye devam ediyor. Bu, yıkıcıların çağrılacağı anlamına gelir. Ve bu yıkıcılarda pek çok şey yapılabilir, örneğin: Kaynaklar doğru şekilde serbest bırakılır, günlükler doğru yazılır, diğer işlemlere mevcut sürecin çöktüğü söylenir, vb. Özetlemek gerekirse, RAII'dir.
paercebal

Dışarıda bıraktınız: Bu try {...} catch (<specified exceptions>) { <do whatever> } catch (...) { unexpected(); ], orada bir try bloğu isteyip istemeseniz de, her şeyi bir yapıda etkili bir şekilde sarar .
David Thornley

4
@paercebal Bu yanlış. Yıkıcıların yakalanmamış istisnalar için çalıştırılıp çalıştırılmayacağı tanımlanır. İstisna yakalanmazsa çoğu ortam yığın / çalıştırma yıkıcılarını çözmez. İmha edicilerinizin bir istisna atıldığında ve ele alınmadığında bile çalışmasını taşınabilir bir şekilde sağlamak istiyorsanız (bu şüpheli bir değerdir), kodunuzu şu şekilde yazmanız gerekirtry { <<...code...>> } catch(...) /* stack guaranteed to be unwound here and dtors run */ { throw; /* pass it on to the runtime */ }
Logan Capaldo

" Uygulamanın, sessizce / şiddetle ölmektense, yığın izini alabileceğiniz, işlenmemiş bir istisnayla çökmesine izin vermek her zaman daha iyidir. " Bir "kilitlenme" nasıl temiz bir çağrıdan daha iyi olabilir terminate()? Neden sadece aramıyorsun abort()?
meraklı adam

42

C ++ 'da istisna belirtimlerinden kaçının. Sorunuzda verdiğiniz nedenler, neden için oldukça iyi bir başlangıçtır.

Herb Sutter'ın "İstisna Özelliklerine Pragmatik Bir Bakış" sayfasına bakın .


3
@awoodland: 'dinamik istisna özellikleri' ( throw(optional-type-id-list)) kullanımı C ++ 11'de kaldırılmıştır. Hala standarttadırlar, ancak kullanımlarının dikkatlice düşünülmesi gerektiğine dair bir uyarı atışı gönderildiğini tahmin ediyorum. C ++ 11, noexceptspesifikasyonu ve operatörü ekler . noexceptYorum yapmak için yeterince ayrıntı bilmiyorum . Bu makale oldukça ayrıntılı görünüyor: akrzemi1.wordpress.com/2011/06/10/using-noexcept Ve Dietmar Kühl'ün Haziran 2011 Overload Journal'da bir makalesi var: accu.org/var/uploads/journals/overload103.pdf
Michael Burr

@MichaelBurr Yalnızca throw(something)yararsız ve kötü bir fikir olarak kabul edilir. throw()kullanışlı.
meraklı adam

" Pek çok insanın istisna belirtimlerinin yaptığını düşündüğü şu: bullet İşlevlerin yalnızca listelenen istisnaları (muhtemelen hiçbirini) atacağını garanti edin. Bullet Yalnızca listelenen istisnaların (muhtemelen hiçbirinin) atılmayacağı bilgisine dayalı derleyici optimizasyonlarını etkinleştirin. Yukarıdaki beklentiler, yine, aldatıcı bir şekilde doğru olmaya yakın "Hayır, yukarıdaki beklentiler kesinlikle doğrudur.
meraklı adam

14

Bence standart hariç kongre (C ++ için)
İstisna tanımlayıcıları, C ++ standardında çoğunlukla başarısız olan bir deneydi.
Bunun istisnası, no throw belirticisinin yararlı olmasıdır, ancak kodun belirticiyle eşleştiğinden emin olmak için dahili olarak uygun try catch bloğunu eklemeniz gerekir. Herb Sutter'ın konuyla ilgili bir sayfası var. Gotch 82

Ek olarak, İstisna Garantilerini açıklamaya değer olduğunu düşünüyorum.

Bunlar temelde, bir nesnenin durumunun, o nesne üzerindeki bir yöntemden kaçan istisnalardan nasıl etkilendiğine ilişkin belgelerdir. Maalesef derleyici tarafından zorunlu tutulmuyor veya başka bir şekilde bahsedilmiyor.
Güçlendirme ve İstisnalar

İstisna Garantileri

Garanti yok:

Bir özel durum bir yöntemden kaçtıktan sonra nesnenin durumu hakkında hiçbir garanti yoktur
. Bu durumlarda nesne artık kullanılmamalıdır.

Temel Garanti:

Neredeyse tüm durumlarda bu, bir yöntemin sağladığı minimum garanti olmalıdır.
Bu, nesnenin durumunun iyi tanımlanmasını ve tutarlı bir şekilde kullanılabilmesini garanti eder.

Güçlü Garanti: (diğer adıyla İşlem Garantisi)

Bu, yöntemin başarılı
bir şekilde tamamlanacağını veya bir İstisna atılacağını ve nesnelerin durumunun değişmeyeceğini garanti eder .

Atma Garantisi Yok:

Yöntem, hiçbir istisnanın yöntemden çıkmasına izin verilmediğini garanti eder.
Tüm yıkıcılar bu garantiyi vermelidir.
| NB Bir istisna hali hazırda yayılırken bir istisna yıkıcıdan kaçarsa
| uygulama sona erecek


Garantiler, her C ++ programcısının bilmesi gereken bir şeydir, ancak bana istisna belirtimleriyle ilgili görünmüyorlar.
David Thornley

1
@David Thornley: Garantileri, istisna spesifikasyonlarının ne olması gerektiği olarak görüyorum (yani güçlü G'ye sahip bir yöntem, korumasız bir temel G'ye sahip bir yöntemi çağıramaz). Maalesef, derleyici olarak yararlı bir şekilde uygulanacak kadar iyi tanımlandıklarından emin değilim.
Martin York

" İşte birçok kişi istisna belirtimlerinin yaptığını düşünüyor: - İşlevlerin yalnızca listelenen istisnaları (muhtemelen hiçbirini) atacağını garanti edin. - Yalnızca listelenen istisnaların (muhtemelen hiçbirinin) atılmayacağı bilgisine dayalı derleyici optimizasyonlarını etkinleştirin. Yukarıdaki beklentiler, yine aldatıcı bir şekilde doğru olmaya yakın. "Aslında her ikisi de tam olarak haklı.
meraklı adam

@curiousguy. Java bunu böyle yapar, çünkü kontroller derleme zamanında zorunlu kılınmıştır. C ++ çalışma zamanı kontrolleridir. Yani: Guarantee that functions will only throw listed exceptions (possibly none). Doğru değil. Yalnızca işlevin bu istisnaları atması durumunda uygulamanın sonlanacağını garanti eder. Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrownBunu yapamam. Az önce belirttim, istisnanın atılmayacağını garanti edemezsiniz.
Martin York

1
@LokiAstari "Java bunu böyle yapıyor çünkü denetlemeler derleme zamanında zorlanıyor_" Java istisna özelliği başarısız olan bir deneydir. Java, en kullanışlı istisna özelliklerine throw()sahip değildir : (atmaz). " Yalnızca, işlev bu istisnaları atarsa, uygulamanın sonlandıracağını garanti eder " Hayır "doğru değil" doğru anlamına gelir. C ++ 'da bir işlevin asla çağırmayacağının garantisi yoktur terminate(). " Az önce işaret ettiğim için istisnanın atılmayacağını garanti edemezsiniz " Fonksiyonun atmayacağını garanti ediyorsunuz. Tam olarak ihtiyacın olan şey bu.
meraklı adam

8

gcc, istisna belirtimlerini ihlal ettiğinizde uyarılar verir. Yaptığım şey, istisnaların benim belgelerimle uyuştuğundan emin olmak için açıkça kontrol etmek amacıyla istisna belirtimlerini yalnızca "tüy bırakmayan" mod derlemesinde kullanmak için makroları kullanmaktır.


7

Tek kullanışlı istisna belirticisi, "atmıyor" daki gibi "throw ()" dir.


2
Lütfen neden yararlı olduğuna bir neden ekleyebilir misiniz?
buti-oxa

2
neden yararlı değil? bazı işlevlerin sola ve ortaya istisnalar atmaya başlamayacağını bilmekten daha yararlı bir şey yoktur.
Matt Joiner

1
Ayrıntılı açıklama için lütfen Michael Burr'un yanıtında atıfta bulunulan Herb Sutter tartışmasına bakın.
Harold Ekstrom

4

İstisna belirtimleri, C ++ 'da harika kullanışlı araçlar değildir. Bununla birlikte, std :: unexpected ile birleştirilirse, onlar için iyi bir kullanım vardır / vardır / vardır.

Bazı projelerde yaptığım şey, istisna özellikli kod ve ardından kendi tasarımımın özel bir istisnasını atacak bir fonksiyonla set_unexpected () 'ı çağırmaktır. Bu istisna, oluşturulduktan sonra bir geri izleme alır (platforma özgü bir şekilde) ve std :: bad_exception'dan türetilir (istenirse yayılmasına izin vermek için). Bir sonlandırma () çağrısına neden olursa, genellikle olduğu gibi, geri izleme ne () tarafından yazdırılır (ve buna neden olan orijinal istisna; bunu bulmak zor değil) ve böylece sözleşmemin nerede olduğu hakkında bilgi alırım beklenmedik kütüphane istisnası gibi ihlal edildi.

Bunu yaparsam, kütüphane istisnalarının yayılmasına (standart olanlar hariç) asla izin vermem ve tüm istisnalarımı std :: exception'dan türetmem. Bir kütüphane atmaya karar verirse, her zaman kodu kontrol etmeme izin verecek şekilde yakalayıp kendi hiyerarşime dönüşeceğim. Bağımlı işlevleri çağıran şablonlu işlevler, bariz nedenlerle istisna belirtimlerinden kaçınmalıdır; ancak yine de kütüphane kodu ile şablonlu bir işlev arayüzüne sahip olmak nadirdir (ve birkaç kütüphane gerçekten şablonları yararlı bir şekilde kullanır).


3

İşlev bildirimine etrafındaki herhangi bir yorum yerine bakmayı tercih eden kişiler tarafından kullanılacak bir kod yazıyorsanız, o zaman bir belirtim onlara hangi istisnaları yakalamak isteyebileceklerini söyleyecektir.

Aksi takdirde throw(), herhangi bir istisna oluşturmadığını belirtmekten başka bir şey kullanmayı özellikle yararlı bulmuyorum .


3

Hayır. Bunları kullanırsanız ve kodunuz veya kodunuz tarafından çağrılan kod tarafından belirtmediğiniz bir istisna atılırsa, varsayılan davranış programınızı derhal sonlandırmaktır.

Ayrıca, C ++ 0x standardının mevcut taslaklarında kullanımlarının kullanımdan kaldırıldığına inanıyorum.



2

Genellikle istisna tanımlayıcıları kullanmam. Ancak, herhangi bir başka istisna program kesin mümkün olacağını söz konusu işlevinden gelmek olsaydı durumlarda düzeltmek , o zaman yararlı olabilir. Her durumda, o işlevden hangi istisnaların beklenebileceğini açıkça belgelediğinizden emin olun.

Evet, belirtilmemiş bir istisnanın istisna belirleyicileri olan bir işlevden atılması beklenen davranışı terminate () 'yi çağırmaktır.

Ayrıca Scott Meyers'in bu konuya Daha Etkili C ++ 'da değindiğini de not edeceğim. Etkili C ++ ve Daha Etkili C ++, şiddetle tavsiye edilen kitaplardır.


2

Evet, dahili dokümantasyonla ilgileniyorsanız. Ya da belki başkalarının kullanacağı bir kitaplık yazmak, böylece belgelere başvurmadan ne olacağını anlatabilirler. Atma veya atmama, neredeyse dönüş değeri gibi API'nin bir parçası olarak kabul edilebilir.

Derleyicide Java stilinin doğruluğunu zorlamak için gerçekten yararlı olmadıklarına katılıyorum, ancak hiç yoktan veya gelişigüzel yorumlardan daha iyidir.


2

Birim testi için yararlı olabilirler, böylece testler yazarken işlevin başarısız olduğunda ne yapmasını bekleyeceğinizi bilirsiniz, ancak derleyicide bunları çevreleyen bir zorlama yoktur. C ++ 'da gerekli olmayan ekstra kodlar olduklarını düşünüyorum. Emin olmanız gereken her şeyi seçerseniz, kodunuzun okunabilir kalması için proje ve ekip üyeleri genelinde aynı kodlama standardını takip etmenizdir.


0

Makaleden:

http://www.boost.org/community/exception_safety.html

"İstisnai durumda güvenli bir genel kapsayıcı yazmanın imkansız olduğu iyi bilinmektedir." Bu iddia genellikle Tom Cargill'in [4] genel bir yığın şablonu için istisna güvenliği sorununu araştırdığı bir makaleye atıfta bulunularak duyulur. Cargill makalesinde birçok yararlı soruyu gündeme getiriyor, ancak maalesef sorununa bir çözüm sunamıyor.1 Bir çözümün mümkün olmayabileceğini öne sürerek bitiriyor. Ne yazık ki makalesi birçok kişi tarafından bu spekülasyonun "kanıtı" olarak okundu. Yayınlandığından bu yana, aralarında C ++ standart kitaplık kapsayıcılarının da bulunduğu, istisnai durum korumalı jenerik bileşenlere dair birçok örnek olmuştur.

Ve gerçekten de şablon sınıflarını güvenli hale getirmenin yollarını düşünebilirim. Tüm alt sınıflar üzerinde kontrol sahibi olmadığınız sürece, yine de bir sorununuz olabilir. Bunu yapmak için, sınıflarınızda çeşitli şablon sınıfları tarafından atılan istisnaları tanımlayan typedef'ler oluşturabilirsiniz. Bu, sorunun baştan tasarlamak yerine her zaman olduğu gibi sonradan ele almak olduğunu düşünüyor ve bence asıl engel bu ek yük.


-2

İstisna özellikler = çöp, 30 yaşın üzerindeki herhangi bir Java geliştiricisine sorun


8
30 yaşın üzerindeki java programcıları C ile baş edemedikleri için kendilerini kötü hissetmeli, java kullanmak için hiçbir mazeretleri yok.
Matt Joiner
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.