C ++ uygulamasında assert () kullanmak kötü bir uygulama mı?


94

Sürüm yapılarının performansını etkilemeden hata ayıklamayı kolaylaştırmak için C ++ koduma çok sayıda iddia ekleme eğilimindeyim. Şimdi, assertakılda C ++ mekanizmaları olmadan tasarlanmış saf bir C makrosu.

Öte yandan C ++ std::logic_error, programın mantığında (dolayısıyla adı) bir hata olduğu durumlarda atılması amaçlanan tanımlar . Bir örnek fırlatmak sadece mükemmel, daha C ++ ish alternatifi olabilir assert.

Sorun şu ki assertve aborther ikisi de yıkıcıları çağırmadan programı hemen sonlandırıyor, bu nedenle temizleme işlemini atlamak, manuel olarak bir istisna atıldığında gereksiz çalışma süresi maliyetleri ekliyor. Bunu aşmanın bir yolu SAFE_ASSERT, aynı C muadili gibi çalışan, ancak başarısızlık durumunda bir istisna atan kendi onaylama makrosu oluşturmaktır .

Bu sorunla ilgili üç fikir düşünebilirim:

  • C'nin iddiasına sadık kalın. Program hemen sonlandırıldığı için, değişikliklerin doğru şekilde silinip silinmediği önemli değildir. Ayrıca, #defineC ++ 'da s kullanmak da aynı derecede kötüdür.
  • Bir istisna atın ve main () 'de yakalayın . Programın herhangi bir durumunda kodun yıkıcıları atlamasına izin vermek kötü bir uygulamadır ve her ne pahasına olursa olsun önlenmelidir ve sonlandırma () çağrıları da öyle. İstisnalar atılırsa, yakalanmaları gerekir.
  • Bir istisna atın ve programı sonlandırmasına izin verin. Bir programı sonlandıran bir istisna tamamdır ve NDEBUGbu nedenle, bu asla bir sürüm yapısında olmayacaktır. Yakalama gereksizdir ve dahili kodun uygulama ayrıntılarını main().

Bu soruna kesin bir cevap var mı? Herhangi bir profesyonel referans var mı?

Düzenlendi: Yıkıcıları atlamak elbette tanımlanmamış bir davranış değildir.


22
Hayır, gerçekten logic_errormantık hatasıdır. Programın mantığındaki bir hata, hata olarak adlandırılır. Hataları istisnalar atarak çözemezsiniz.
R. Martinho Fernandes

4
İddialar, istisnalar, hata kodları. Her birinin tamamen farklı bir kullanım durumu vardır ve bunlardan birini diğerinin gerekli olduğu yerlerde kullanmamalısınız.
Kerrek SB

5
static_assertElinizde varsa, uygun olan yerde kullandığınızdan emin olun .
Flexo

4
@trion Bunun nasıl yardımcı olduğunu anlamıyorum. Atar std::bugmısın
R. Martinho Fernandes

3
@trion: Bunu yapma. İstisnalar hata ayıklama için değildir. Birisi istisnayı yakalıyor olabilir. Ararken UB için endişelenmenize gerek yok std::abort(); sadece sürecin sonlanmasına neden olan bir sinyal yükseltir.
Kerrek SB

Yanıtlar:


74

İddialar C ++ kodunda tamamen uygundur. İstisnalar ve diğer hata işleme mekanizmaları gerçekte iddialarla aynı şeyi amaçlamaz.

Hata işleme, bir hatayı düzeltmek veya kullanıcıya güzel bir şekilde bildirmek için bir potansiyel olduğunda yapılır. Örneğin, bir girdi dosyasını okumaya çalışırken bir hata varsa, bununla ilgili bir şeyler yapmak isteyebilirsiniz. Hatalar hatalardan kaynaklanabilir, ancak belirli bir girdi için basitçe uygun çıktı da olabilirler.

İddialar, API normalde kontrol edilmediğinde bir API gereksinimlerinin karşılandığını kontrol etmek veya geliştiricinin inşaat tarafından garanti edildiğini düşündüğü şeyleri kontrol etmek gibi şeyler içindir. Örneğin, bir algoritma sıralı girdi gerektiriyorsa, normalde bunu kontrol etmezsiniz, ancak hata ayıklama yapılarının bu tür bir hatayı işaretlemesi için kontrol etmek için bir iddianız olabilir. Bir iddia, her zaman hatalı çalışan bir programı göstermelidir.


Temiz olmayan bir kapatmanın soruna neden olabileceği bir program yazıyorsanız, iddialardan kaçınmak isteyebilirsiniz. Kesinlikle C ++ dili açısından tanımlanmamış davranış, burada böyle bir sorun olarak nitelendirilmez, çünkü bir iddiaya uymak muhtemelen zaten tanımlanmamış davranışın sonucudur veya bazı temizlemelerin düzgün çalışmasını engelleyebilecek başka bir gereksinimin ihlalidir.

Ayrıca, iddiaları bir istisna açısından uygularsanız, bu, iddianın amacıyla çelişse bile, potansiyel olarak yakalanabilir ve 'ele alınabilir'.


1
Bunun yanıtta özellikle belirtilip belirtilmediğinden tam olarak emin değilim, bu yüzden burada belirteceğim: Kodu yazarken belirlenemeyen, kullanıcı girişi içeren hiçbir şey için bir iddia kullanmamalısınız. Kodunuz 3yerine bir kullanıcı geçerse 1, genel olarak bu bir onaylamayı tetiklememelidir. İddialar yalnızca programcı hatasıdır, kitaplığın kullanıcısı veya uygulama hatası değildir.
SS Anne

101
  • İddialar hata ayıklama içindir . Gönderilen kodunuzun kullanıcısı onları asla görmemelidir. Bir iddiaya rastlanırsa, kodunuzun düzeltilmesi gerekir.

    CWE-617: Ulaşılabilir Onaylama

Ürün, bir saldırgan tarafından tetiklenebilen bir assert () veya benzer bir ifade içerir ve bu, bir uygulama çıkışına veya gereğinden daha şiddetli başka bir davranışa yol açar.

İddia, mantık hatalarını yakalamak ve daha ciddi güvenlik açığı koşullarına ulaşma olasılığını azaltmak için iyi olsa da, yine de bir hizmet reddine yol açabilir.

Örneğin, bir sunucu aynı anda birden çok bağlantıyı yönetiyorsa ve tek bir bağlantıda diğer tüm bağlantıların kesilmesine neden olan bir assert () ortaya çıkarsa, bu, hizmet reddine yol açan erişilebilir bir iddiadır.

  • İstisnalar, istisnai durumlar içindir . Biriyle karşılaşılırsa, kullanıcı istediğini yapamaz, ancak başka bir yerden devam edebilir.

  • Hata işleme normal program akışı içindir. Örneğin, kullanıcıdan bir numara ister ve değiştirilemez bir şey alırsanız, bu normaldir , çünkü kullanıcı girişi kontrolünüz altında değildir ve elbette her zaman olası tüm durumları ele almalısınız. (Örneğin, aralarında "Üzgünüz, tekrar deneyin" diyerek geçerli bir giriş elde edene kadar döngü yapın.)


1
bu iddiayı yeniden arayan geldi; üretim kodundan herhangi bir iddianın geçmesi kötü tasarım ve kalite güvencesine işaret eder. Bir iddianın çağrıldığı nokta, bir hata koşulunun zarif bir şekilde işlenmesinin gerektiği yerdir. ( Asla assert kullanmıyorum). İstisnalara gelince, bildiğim tek kullanım durumu ctor'un ne zaman başarısız olabileceği, diğerlerinin tümü normal hata işleme içindir.
slashmais

5
@slashmais: Duygu övgüye değer, ancak mükemmel, hatasız bir kod göndermediğiniz sürece, tanımlanmamış davranışa tercih edilen bir iddia (kullanıcıyı çökerten bir iddia bile) buluyorum. Hatalar karmaşık sistemlerde meydana gelir ve bir iddia ile, nerede olduğunu görme ve teşhis etme yoluna sahip olursunuz.
Kerrek SB

@KerrekSB Bir iddia yerine bir istisna kullanmayı tercih ederim. En azından kodun başarısız olan dalı atma ve başka yararlı bir şey yapma şansı vardır. En azından, RAII kullanıyorsanız, dosyaları açmak için tüm arabellekleriniz düzgün bir şekilde temizlenir.
daemonspring

14

İddialar, bazı yöntemlerin yürütülmesinden önceki veya sonraki dahili durum gibi dahili uygulama değişmezlerini doğrulamak için kullanılabilir. Eğer iddia başarısız olursa, bu gerçekten programın mantığının bozulduğu ve bundan kurtulamayacağınız anlamına gelir. Bu durumda yapabileceğiniz en iyi şey, kullanıcıya istisna yapmadan mümkün olan en kısa sürede ara vermektir. İddialar hakkında gerçekten güzel olan şey (en azından Linux'ta), çekirdek dökümü işlemin sonlandırılmasının bir sonucu olarak üretilir ve böylece yığın izini ve değişkenleri kolayca inceleyebilirsiniz. Bu, mantık hatasını anlamak için istisna mesajından çok daha kullanışlıdır.


Benzer bir yaklaşımım var. Mantık için muhtemelen yerel olarak doğru olması gereken iddiaları kullanıyorum (örneğin döngü değişmezleri). Yerel olmayan (harici) bir durum tarafından kod üzerinde bir mantık hatasının zorlandığı durumlar istisnadır.
spraff

Bir iddia başarısız olursa, bu programın bir kısmının mantığının bozulduğu anlamına gelir . Başarısız bir iddia, hiçbir şeyin başarılamayacağı anlamına gelmez . Bozuk bir eklenti muhtemelen tüm bir kelime işlemciyi iptal etmemelidir.
daemonspring

13

Yıkıcıların alling abort () nedeniyle çalıştırılmaması tanımsız bir davranış değildir!

Öyle olsaydı, o zaman da çağırmak tanımsız bir davranış std::terminate()olurdu ve o halde bunu sağlamanın amacı ne olurdu?

assert() C ++ 'da olduğu kadar C de kullanışlıdır. İddialar hata işleme için değildir, programı hemen iptal etmek içindir.


1
abort()Programı hemen iptal ettiğiniz için söyleyebilirim . İddiaların hata işleme için olmadığı konusunda haklısınız, ancak iddianın iptal ederek hatayı halletmeye çalıştığını iddia ediyor. Bunun yerine bir istisna atıp, yapabiliyorsa arayanın hatayı halletmesine izin vermeniz gerekmez mi? Sonuçta, arayan kişi, bir işlevin başarısızlığının başka bir şey yapmaya değip değmeyeceğini belirlemek için daha iyi bir konumdadır. Belki arayan kişi ilgisiz üç şey yapmaya çalışıyor ve yine de diğer iki işi tamamlayıp bunu iptal edebilir.
daemonspring

Ve assertaramak için tanımlanır abort(koşul yanlış olduğunda). İstisnalar atmaya gelince, hayır, bu her zaman uygun değildir. Bazı şeyler arayan tarafından halledilemez. Arayan, üçüncü taraf kitaplık işlevindeki bir mantık hatasının kurtarılabilir olup olmadığını veya bozuk verilerin düzeltilip düzeltilemeyeceğini belirleyemez.
Jonathan Wakely

6

IMHO, iddialar ihlal edildiğinde her şeyi saçma sapan koşulları kontrol etmek içindir. Ve bu nedenle onlardan kurtulamazsınız veya daha doğrusu, iyileşmenin önemi yoktur.

Onları 2 kategoriye ayırırdım:

  • Geliştirici günahları (örneğin, negatif değerler döndüren bir olasılık işlevi):

float olasılığı () {dönüş -1.0; }

assert (olasılık ()> = 0.0)

  • Makine bozuk (örneğin, programınızı çalıştıran makine çok yanlış):

int x = 1;

iddia (x> 0);

Bunların ikisi de önemsiz örneklerdir, ancak gerçeklikten çok uzak değildir. Örneğin, vektörlerle kullanmak için negatif indeksler döndüren saf algoritmaları düşünün. Veya özel donanıma gömülü programlar. Daha doğrusu b * k olduğu için .

Ve bu tür geliştirme hataları varsa, uygulanan herhangi bir kurtarma veya hata işleme mekanizmasına güvenmemelisiniz. Aynısı donanım hataları için de geçerlidir.


1
assert (olasılık ()> = 0.0)
Elliott
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.