Güvenli kod (gerçekten) yazıyor musunuz? [kapalı]


312

İstisna yönetimi (EH) mevcut standart gibi görünüyor ve web'de arama yaparak, onu iyileştirmeye veya değiştirmeye çalışan yeni fikirler veya yöntemler bulamıyorum (iyi, bazı varyasyonlar var, ancak yeni bir şey yok).

Çoğu insan bunu görmezden ya da sadece kabul etmek gözükse EH sahip bazı büyük sakıncaları: istisnalar koduna görünmez ve birçok, birçok olası çıkış noktalarını oluşturur. Yazılım üzerine Joel bunun hakkında bir makale yazdı . Karşılaştırma gotouyan bana EH hakkında tekrar düşündürdü, mükemmel.

EH'den kaçınmaya çalışıyorum ve sadece dönüş değerlerini, geri çağrıları veya amaca uygun olanı kullanıyorum. Ancak güvenilir kod yazmanız gerektiğinde, bu günlerde EH'yi görmezden gelemezsiniz : Bu new, 0'ı döndürmek yerine (eski günlerde olduğu gibi) bir istisna oluşturabilecek olanla başlar . Bu, herhangi bir C ++ kodu satırını bir özel duruma karşı savunmasız hale getirir . Ve sonra C ++ temel kodunda daha fazla yer istisnalar atıyor ... std lib bunu yapıyor vb.

Bu titrek bir zeminde yürümek gibi geliyor .. Yani, şimdi istisnalara dikkat etmek zorundayız!

Ama bu zor, gerçekten zor. İstisna güvenli kod yazmayı öğrenmelisiniz ve onunla biraz deneyiminiz olsa bile, güvenli olmak için herhangi bir kod satırını iki kez kontrol etmeniz gerekecektir! Ya da her yere try / catch blokları koymaya başlarsınız, bu da kodu okunamayan bir duruma gelene kadar keser.

EH, kodunuzda birçok olası çıkış noktası oluşturan bir yaklaşımla (ancak sizin bir noktada yapmak zorunda kalırsanız), hatta kodunuz aracılığıyla çok sayıda yol oluşturur (catch bloklarındaki kod, std :: cerr .. dışında günlük oluşturma olanaklarına ihtiyacınız olan bir sunucu programını düşünün). EH'nin avantajları var, ama mesele bu değil.

Asıl sorularım:

  • Gerçekten istisna güvenlik kodu yazıyor musunuz?
  • Son "üretime hazır" kodunuzun istisna güvenli olduğundan emin misiniz?
  • Bundan emin olabilir misin?
  • Çalışan alternatifleri biliyor ve / veya gerçekten kullanıyor musunuz?

44
"EH eski temiz deterministik yaklaşımın yerini aldı (dönüş değerleri ..)" Ne? İstisnalar, dönüş kodları kadar belirleyicidir. Onlar hakkında rastgele bir şey yok.
rlbond

2
EH uzun bir süredir standart (Ada'dan beri!)

12
"EH" kural dışı durum işleme için belirlenmiş bir kısaltma mı? Daha önce hiç görmedim.
Thomas Padron-McCarthy

3
Newton yasaları bugün birçok şey için geçerlidir. Yüzyıllar boyunca çok çeşitli fenomenler öngörmüş olmaları önemlidir.
David Thornley

4
@frunsi: Heh, kafa karıştırdığın için üzgünüm! Ama bence, bir şey varsa, garip ve açıklanamayan bir kısaltma veya kısaltma insanları daha fazla cesaretlendiremez.
Thomas Padron-McCarthy

Yanıtlar:


520

Sorunuz, "İstisna güvenli kod yazmanın çok zor olduğunu" iddia ediyor. Önce sorularınızı, sonra da arkasındaki gizli soruyu cevaplayacağım.

Soruları cevaplama

Gerçekten istisna güvenlik kodu yazıyor musunuz?

Tabii ki biliyorum.

Bu Java C ++ programcısı (RAII semantik eksikliği) olarak benim için cazibesini kaybetti sebebi ama digressing ediyorum: Bu C ++ sorudur.

Aslında, STL veya Boost koduyla çalışmanız gerektiğinde gereklidir. Örneğin, C ++ iş parçacıkları ( boost::threadveya std::thread) zarif bir şekilde çıkmak için bir istisna atar.

Son "üretime hazır" kodunuzun istisna güvenli olduğundan emin misiniz?

Bundan emin olabilir misin?

İstisna güvenli kod yazmak, hatasız kod yazmak gibidir.

Kodunuzun istisna güvenli olduğundan% 100 emin olamazsınız. Ama sonra, iyi bilinen kalıpları kullanarak ve iyi bilinen anti-kalıplardan kaçınmak için çaba gösteriyorsunuz.

Çalışan alternatifleri biliyor ve / veya gerçekten kullanıyor musunuz?

Orada hiçbir C uygun alternatifler ++ (eğer C ve önlemek C ++ kütüphaneleri yanı sıra, Windows SEH gibi dış sürprizler geri dönmek gerekir yani).

Özel durum güvenlik kodu yazma

İstisna güvenlik kodu yazmak için, önce yazdığınız her talimatın istisna güvenlik düzeyinin ne olduğunu bilmelisiniz .

Örneğin, a newbir istisna atabilir, ancak yerleşik (örneğin int veya işaretçi) atamak başarısız olmaz. Bir takas asla başarısız olmaz (asla atma takas yazma), bir std::list::push_backkutu atma ...

İstisna garantisi

Anlamanız gereken ilk şey, tüm işlevleriniz tarafından sunulan istisna garantisini değerlendirebilmenizdir:

  1. none : Kodunuz asla bunu sunmamalıdır. Bu kod her şeyi sızdırır ve atılan ilk istisnada bozulur.
  2. temel : Bu, en azından teklif etmeniz gereken garantidir, yani bir istisna atılırsa, hiçbir kaynak sızdırılmaz ve tüm nesneler hala bütündür
  3. strong : İşlem başarılı olur veya bir istisna atar, ancak atarsa, veriler işlemin hiç başlamamış olmasıyla aynı durumda olur (bu, C ++ 'ya işlem gücü sağlar)
  4. nothrow / nofail : İşlem başarılı olacak.

Kod örneği

Aşağıdaki kod doğru C ++ gibi görünüyor, ancak gerçekte "none" garantisi sunuyor ve bu nedenle doğru değil:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   X * x = new X() ;                // 2. basic : can throw with new and X constructor
   t.list.push_back(x) ;            // 3. strong : can throw
   x->doSomethingThatCanThrow() ;   // 4. basic : can throw
}

Tüm kodumu bu tür analizleri akılda tutarak yazıyorum.

Sunulan en düşük garanti temeldir, ancak daha sonra her komutun sıralaması tüm işlevi "none" yapar, çünkü 3. atarsa ​​x sızıntı yapar.

Yapılacak ilk şey, listeyi güvenli bir şekilde sahip oluncaya kadar x'i akıllı bir işaretçiye koyarak "temel" işlevini yapmak olacaktır:

void doSomething(T & t)
{
   if(std::numeric_limits<int>::max() > t.integer)  // 1.   nothrow/nofail
      t.integer += 1 ;                              // 1'.  nothrow/nofail
   std::auto_ptr<X> x(new X()) ;    // 2.  basic : can throw with new and X constructor
   X * px = x.get() ;               // 2'. nothrow/nofail
   t.list.push_back(px) ;           // 3.  strong : can throw
   x.release() ;                    // 3'. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 4.  basic : can throw
}

Şimdi, kodumuz "temel" bir garanti sunuyor. Hiçbir şey sızdırmaz ve tüm nesneler doğru durumda olur. Ama daha fazlasını, yani güçlü garantiyi sunabiliriz. O burasıdır olabilir masraflı hale gelir ve bu yüzden tüm C ++ kod güçlüdür. Hadi deneyelim:

void doSomething(T & t)
{
   // we create "x"
   std::auto_ptr<X> x(new X()) ;    // 1. basic : can throw with new and X constructor
   X * px = x.get() ;               // 2. nothrow/nofail
   px->doSomethingThatCanThrow() ;  // 3. basic : can throw

   // we copy the original container to avoid changing it
   T t2(t) ;                        // 4. strong : can throw with T copy-constructor

   // we put "x" in the copied container
   t2.list.push_back(px) ;          // 5. strong : can throw
   x.release() ;                    // 6. nothrow/nofail
   if(std::numeric_limits<int>::max() > t2.integer)  // 7.   nothrow/nofail
      t2.integer += 1 ;                              // 7'.  nothrow/nofail

   // we swap both containers
   t.swap(t2) ;                     // 8. nothrow/nofail
}

Operasyonları yeniden sipariş ettik, önce Xdoğru değeri yaratıp ayarladık . Herhangi bir işlem başarısız olursa, tdeğiştirilmez, bu nedenle 1'den 3'e kadar işlem "güçlü" olarak kabul edilebilir: Bir şey atarsa, tdeğiştirilmez ve Xakıllı işaretçiye ait olduğu için sızmaz.

Sonra, bir kopyasını oluşturmak t2ait tbir şey atarsa, 7'ye operasyon 4'ten bu kopya üzerinde ve çalışmalarını t2değiştirilir, ancak daha sonra, thala orijinaldir. Hala güçlü garanti veriyoruz.

Sonra, takas tve t2. Takas işlemleri C ++ 'da nothrow olmamalıdır, bu yüzden yazdığınız Ttakasın nothrow olmadığını umalım (eğer değilse, yeniden yazmamak için yeniden yazın).

Yani, fonksiyonun sonuna ulaşırsak, her şey başarılı oldu (bir dönüş tipine gerek yok) ve tistisna değeri var. Başarısız olursa, thala orijinal değerine sahiptir.

Şimdi, güçlü garantiyi sunmak oldukça maliyetli olabilir, bu yüzden tüm kodunuza güçlü garanti sunmaya çalışmayın, ancak bunu bir maliyet olmadan yapabilirsiniz (ve C ++ satır içi ve diğer optimizasyon yukarıdaki kodları maliyetsiz yapabilir) , o zaman yap. İşlev kullanıcısı bunun için size teşekkür edecektir.

Sonuç

İstisna güvenli kod yazmak biraz alışkanlık gerektirir. Kullanacağınız her talimatın sunduğu garantiyi değerlendirmeniz ve ardından talimatlar listesinin sunduğu garantiyi değerlendirmeniz gerekir.

Tabii ki, C ++ derleyicisi garantiyi yedeklemeyecek (benim kodumda, garantiyi @ uyarıcı bir oksijen etiketi olarak sunuyoruz), ki bu biraz üzücü, ancak istisna güvenli kod yazmaya çalışmanızı engellememelidir.

Normal hataya karşı hata

Bir programcı, hatasız bir işlevin her zaman başarılı olacağını nasıl garanti edebilir? Sonuçta, işlevin bir hatası olabilir.

Bu doğru. İstisna garantilerinin hatasız kodla sunulması beklenir. Ancak, herhangi bir dilde, bir işlevi çağırmak, işlevin hatasız olduğunu varsayar. Hiçbir aklı başında kod, bir hataya sahip olma olasılığına karşı kendini korumaz. Kodu yapabildiğiniz en iyi şekilde yazın ve ardından hatasız olduğu varsayımıyla garanti verin. Ve bir hata varsa, düzeltin.

İstisnalar, kod hataları için değil, istisnai işlem hataları içindir.

Son sözler

Şimdi, soru "Buna değer mi?"

Tabiki öyle. İşlevin başarısız olmayacağını bilen bir "nothrow / no-fail" fonksiyonuna sahip olmak büyük bir nimettir. Aynı şey, veritabanları gibi işlemsel semantiklerle, kodlama / geri alma özelliklerine sahip kod yazmanıza olanak tanıyan "güçlü" bir işlev için de söylenebilir;

O zaman, "temel" sunmanız gereken en az garantidir. C ++, kaynak sızıntılarından kaçınmanızı sağlayan kapsamlı bir dildir (bir çöp toplayıcının veritabanı, bağlantı veya dosya tanıtıcıları için zor olduğunu düşündüğü bir şey).

Yani, bildiğim kadarıyla gördüğüm kadarıyla, bu ise buna değer.

Edit 2010-01-29: Atmayan takas hakkında

nobar, "istisna güvenli kod nasıl yazılır" ın bir parçası olduğu için oldukça alakalı olduğuna inandığım bir yorum yaptı:

  • [ben] Takas asla başarısız olmaz (fırlatma takas bile yazma)
  • [nobar] Bu, özel olarak yazılmış swap()işlevler için iyi bir öneridir . Bununla birlikte, std::swap()dahili olarak kullandığı işlemlere dayanarak başarısız olabileceğine dikkat edilmelidir.

varsayılan std::swap, bazı nesneler için atabileceğiniz kopyalar ve atamalar yapar. Böylece, varsayılan takas sınıflarınız için veya hatta STL sınıfları için kullanılabilir. Bildiğim kadarıyla ++ standardı ilgilidir C, takas işlemi için vector, dequeve listo olabilir için ise atmaz mapkarşılaştırma funktoru (Bkz kopya inşaat atabilir eğer C ++ Dili, Special Edition, apandis E E.4.3 Programlama Değiştir ).

Vektörün takasının Visual C ++ 2008 uygulamasına bakıldığında, iki vektörün aynı ayırıcıya (yani, normal durumda) sahip olması durumunda vektörün takası atmaz, ancak farklı ayırıcıları varsa kopyalar oluşturur. Ve böylece, bu son davaya atılabileceğini varsayıyorum.

Bu nedenle, orijinal metin hala geçerlidir: Asla bir atma takas yazma, ancak nobar'ın yorumu hatırlanmalıdır: Takas ettiğiniz nesnelerin atmayan bir takas olduğundan emin olun.

Edit 2011-11-06: İlginç makale

Bize temel / güçlü / huzursuz garantileri veren Dave Abrahams , bir makalede STL istisnasını güvence altına alma konusundaki deneyimini şöyle anlattı:

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

Her durumun test edildiğinden emin olmak için otomatik birim testine dayandığı 7. noktaya (istisna güvenliği için otomatik test) bakın. Sanırım bu bölüm, yazarın "Bundan emin olabilir misiniz, " sorusuna mükemmel bir cevap .

Düzenleme 2013/05/31: dan Açıklama dionadar

t.integer += 1;taşma olmayacağının garantisi istisna güvenli DEĞİLDİR ve aslında teknik olarak UB'yi çağırabilir! (İmzalı taşma UB: C ++ 11 5/4 "Bir ifadenin değerlendirilmesi sırasında sonuç matematiksel olarak tanımlanmazsa veya türü için temsil edilebilir değerler aralığında değilse, davranış tanımsızdır.") tamsayı taşmaz, ancak hesaplamalarını bir eşdeğerlik sınıfı modulo 2 ^ # bit ile yapın.

Dionadar, aslında tanımlanmamış davranışa sahip aşağıdaki satıra atıfta bulunuyor.

   t.integer += 1 ;                 // 1. nothrow/nofail

Buradaki çözüm std::numeric_limits<T>::max(), eklemeyi yapmadan önce tamsayı zaten maksimum değerinde (kullanarak ) olup olmadığını doğrulamaktır .

Benim hatam "Normal hata vs hata" bölümünde, yani bir hata gider. Gerekçeyi geçersiz kılmaz ve istisna-güvenli kodun işe yaramaz olduğu anlamına gelmez, çünkü elde edilmesi imkansızdır. Kendinizi bilgisayarın kapanmasına, derleyici hatalarına, hatta hatalarınıza veya diğer hatalara karşı koruyamazsınız. Mükemmelliğe ulaşamazsınız, ancak mümkün olduğunca yaklaşmaya çalışabilirsiniz.

Kodu Dionadar'ın yorumları göz önünde bulundurarak düzelttim.


6
Çok teşekkürler! Hala farklı bir şey umuyordum. Ama cevabınızı C ++ 'a bağlı kaldığınız ve ayrıntılı açıklamanız için kabul ediyorum. Hatta "Bu bir istisna için savunmasız herhangi bir C ++ kod satırı yapar" -çözlemimi çürüttünüz. Sonuçta bu satır satır analiz mantıklı ... Bunu düşünmek zorundayım.
Frunsi

8
"Bir takas asla başarısız olmaz". Bu, özel yazılmış swap () işlevleri için iyi bir öneridir. Bununla birlikte, std :: swap () işlevinin dahili olarak kullandığı işlemlere bağlı olarak başarısız olabileceğine dikkat edilmelidir.
nobar

7
@Mehrdad:: "finally" does exactly what you were trying to achieveElbette öyle. Ve kırılgan kod üretmek kolay olduğu için finally, "kaynakla deneme" kavramı nihayet Java 7'de (yani C # 'ların 10 yıl sonra usingve C ++ yıkıcılarından 3 yıl sonra) ortaya çıktı. Eleştirdiğim bu kırılganlık bu. Gelince Just because [finally] doesn't match your taste (RAII doesn't match mine, [...]) doesn't mean it's "failing": Bu, Sanayi Çöp Toplanan diller RAII'den ilham alan ifadeler (C # usingve Java try) ekleme eğiliminde olduğundan zevkinize katılmıyor .
paercebal

5
@Mehrdad:: RAII doesn't match mine, since it needs a new struct every single darn time, which is tedious sometimesHayır, değil. Kaynakları "korumak" için akıllı işaretçiler veya yardımcı program sınıfları kullanabilirsiniz.
paercebal

5
@Mehrdad: "sizi bir sarmalayıcıya ihtiyacınız olmayacak bir şey için bir sarmalayıcı yapmaya zorlar" - RAII tarzında kodlama yaparken, herhangi bir sarmalayıcıya ihtiyacınız olmaz: kaynak, nesnedir ve nesnelerin ömrü, kaynak ömrüdür. Ancak, ben bir C ++ dünyasından geliyorum, şu anda bir Java projesi ile mücadele ediyorum. C ++ RAII ile karşılaştırıldığında, Java "manuel kaynak yönetimi" bir MESS! Benim Görüşüm, ama Java uzun zaman önce "otomatik kaynak mgmt" için "otomatik bellek mgmt" ticareti.
Frunsi

32

C ++ 'da istisna açısından güvenli kod yazmak pek çok try {} catch {} bloğu kullanmakla ilgili değildir. Kodunuzun ne tür garantiler sağladığını belgelemekle ilgilidir.

Herb Sutter'in Guru Of The Week serisini, özellikle 59, 60 ve 61 taksitlerini okumanızı tavsiye ederim .

Özetlemek gerekirse, sağlayabileceğiniz üç istisna güvenliği düzeyi vardır:

  • Temel: Kodunuz bir istisna attığında, kodunuz kaynak sızdırmaz ve nesneler yok edilebilir kalır.
  • Güçlü: Kodunuz bir istisna attığında, uygulamanın durumunu değiştirmez.
  • Atma yok: Kodunuz hiçbir zaman istisna oluşturmaz.

Şahsen, bu makaleleri oldukça geç keşfettim, C ++ kodumun çoğu kesinlikle istisna açısından güvenli değil.


1
"Olağanüstü C ++" adlı kitabı da iyi bir okuma. Ama hala EH kavramını sorgulamaya çalışıyorum ...
Frunsi

8
+1 OP, kullanım istisnalarını (yakalama) istisna güvenliği (genellikle RAII hakkında daha fazla) ile birleştirir
jk.

Çok az üretim C ++ kodunun istisna güvenli olduğundan şüpheleniyorum.
Raedwald

18

Bazılarımız 20 yılı aşkın bir süredir istisna kullanıyoruz. Örneğin PL / I var. Yeni ve tehlikeli bir teknoloji oldukları fikri benim için şüpheli görünüyor.


Lütfen beni yanlış anlamayın, EH'yi sorgularım (veya yapmaya çalışıyorum). Ve özellikle C ++ EH. Ve alternatifler arıyorum. Belki de kabul etmeliyim (ve eğer tek yolu olacaksa), ama daha iyi alternatifler olabileceğini düşünüyorum. Ben kavram yeni olduğunu düşünüyorum değil, ama evet, ben dönüş kodları ile açık hata işleme daha tehlikeli olabilir düşünüyorum ...
Frunsi

1
Eğer beğenmediyseniz, kullanmayın. Atmanız gereken şeylerin etrafına deneme blokları koyun ve ye-olde-hata-kodunu yeniden keşfedin ve bazı durumlarda tolere edilebilir olan sorunları yaşayın.
bmargulies

Tamam, mükemmel, sadece EH ve hata kodlarını kullanacağım ve onunla yaşayacağım. Ben bir nitwit'im, bu çözüme kendi başıma gelmeliydim! ;)
Frunsi 20 Aralık'ta

17

Her şeyden önce (Neil'in belirttiği gibi) SEH, Microsoft'un Yapılandırılmış İstisna İşleme'dir. C ++ 'da istisna işleme benzer ancak aynı değildir. Aslında, Visual Studio'da isterseniz C ++ Özel Durum İşleme özelliğini etkinleştirmeniz gerekir - varsayılan davranış yerel nesnelerin her durumda yok edildiğini garanti etmez! Her iki durumda da, durum işleme gerçekten değil zor sadece bir farklı .

Şimdi gerçek sorularınız için.

Gerçekten istisna güvenlik kodu yazıyor musunuz?

Evet. Ben her durumda istisna güvenli kod için çalışıyoruz. Kaynaklara kapsamlı erişim için RAII tekniklerini kullanarak evanjelleştiriyorum (örneğin, boost::shared_ptrbellek boost::lock_guardiçin, kilitleme için). Genel olarak, RAII ve kapsam koruma tekniklerinin tutarlı kullanımı istisna güvenlik kodunun yazılmasını çok daha kolay hale getirecektir. İşin püf noktası, neyin var olduğunu ve nasıl uygulanacağını öğrenmektir.

Son "üretime hazır" kodunuzun istisna güvenli olduğundan emin misiniz?

Hayır. Olduğu kadar güvenlidir. 7/24 faaliyetin birkaç yılında istisna nedeniyle süreç hatası görmediğimi söyleyebilirim. Mükemmel kod beklemiyorum, sadece iyi yazılmış kod. İstisna güvenlik sağlamanın yanı sıra, yukarıdaki teknikler try/ catchbloklarla elde edilmesi neredeyse imkansız bir şekilde doğruluğu garanti eder. Üst kontrol kapsamınızdaki (iş parçacığı, işlem vb.) Her şeyi yakalıyorsanız, istisnalar karşısında çalışmaya devam edeceğinizden emin olabilirsiniz ( çoğu zaman ). Aynı teknikler ayrıca çalışmaya devam yardımcı olacaktır doğru istisnalar karşısında olmadan try/ catchher yerde bloklar .

Bunun olduğundan bile emin olabilir misin?

Evet. Kapsamlı bir kod denetiminden emin olabilirsiniz, ancak kimse bunu gerçekten yapmıyor mu? Düzenli kod incelemeleri ve dikkatli geliştiriciler oraya ulaşmak için uzun bir yol kat ediyorlar.

Çalışan alternatifleri biliyor ve / veya gerçekten kullanıyor musunuz?

Böyle üst bit (ala devletler şifreleyen yılda birkaç varyasyon çalıştılar HRESULTler ) ya da bu kötü setjmp() ... longjmp()kesmek. Bunların her ikisi de tamamen farklı şekillerde olsa da pratikte parçalanmaktadır.


Sonunda, birkaç teknik uygulama alışkanlığı edinir ve bir istisnaya yanıt olarak bir şeyi gerçekte nerede yapabileceğinizi dikkatlice düşünürseniz, istisna açısından güvenli olan çok okunabilir bir kodla karşılaşırsınız. Aşağıdaki kuralları izleyerek bunu özetleyebilirsiniz:

  • Yalnızca belirli bir istisna hakkında bir şeyler yapabildiğinizi görmek istediğinizde try/ catchyapmak istediğinizde
  • Neredeyse hiç ham newveya deletekod görmek istemiyorsunuz
  • Eschew std::sprintf, snprintfve diziler genel olarak - std::ostringstreamdizileri biçimlendirmek ve değiştirmek için kullanın std::vectorvestd::string
  • Şüphe duyduğunuzda, kendinizinkini almadan önce Boost veya STL'de işlevsellik arayın

Sadece istisnaları düzgün kullanmayı öğrenmek ve C ++ ile yazmayı planlıyorsanız sonuç kodlarını unutmanızı tavsiye ederim. İstisnalardan kaçınmak istiyorsanız , bunları içermeyen veya güvenli hale getiren başka bir dilde yazmayı düşünebilirsiniz . C ++ 'dan tam olarak nasıl yararlanacağınızı gerçekten öğrenmek istiyorsanız, Herb Sutter , Nicolai Josuttis ve Scott Meyers'den birkaç kitap okuyun .


"varsayılan davranış her durumda yerel nesnelerin yok edilmesini garanti etmez" Visual Studio C ++ derleyicisinin varsayılan ayarının özel durumlar karşısında yanlış kod ürettiğini söylüyorsunuz . Gerçekten öyle mi?
Raedwald

"Neredeyse hiçbir zaman ham newya deleteda kod görmek istemezsiniz ": ham olarak bir kurucu ya da yıkıcı dışında demek istediniz.
Raedwald

@Raedwald - re: VC ++: VC ++ VS2005 sürümü, bir SEH istisnası atıldığında yerel nesneleri yok etmez. "C ++ Özel Durum İşleme özelliğini etkinleştirme" bölümünü okuyun . VS2005'te, SEH istisnaları varsayılan olarak C ++ nesnelerinin yıkıcılarını çağırmaz. Win32 işlevlerini veya bir C-arabirimi DLL'sinde tanımlanan herhangi bir şeyi çağırırsanız, o zaman bu konuda endişelenmeniz gerekir, çünkü onlar bir SEH istisnasını yolunuza atabilirler (ve bazen).
D.Shawley

@Raedwald: re: raw : temel olarak, deleteasla tr1::shared_ptrve benzeri uygulamaların dışında kullanılmamalıdır . newkullanımı gibi bir şey olması koşuluyla kullanılabilir tr1::shared_ptr<X> ptr(new X(arg, arg));. Önemli olan, sonucun newdoğrudan yönetilen bir işaretçiye gitmesidir. Sayfa boost::shared_ptrİyi Uygulamalar iyi açıklamaktadır.
D.Shawley

İstisna güvenliği için kural setinizde neden std :: sprintf (et al) 'e başvuruyorsunuz? Bunlar herhangi bir istisna oluşturduklarını belgelemez
mabraham

10

"Herhangi bir satırın atabileceği" varsayımı altında istisna-güvenli kod yazmak mümkün değildir. İstisna güvenlik kodunun tasarımı, kodunuzda beklemeniz, gözlemlemeniz, takip etmeniz ve uygulamanız gereken belirli sözleşmelere / garantilere eleştirel olarak dayanır. Asla atmaması garanti edilen bir koda sahip olmak kesinlikle gereklidir . Orada başka istisna garantileri vardır.

Başka bir deyişle, istisna-güvenli kod oluşturmak, sadece düz kodlama meselesi değil , büyük ölçüde program tasarımı meselesidir .


8
  • Gerçekten istisna güvenlik kodu yazıyor musunuz?

Kesinlikle niyetindeyim.

  • Son "üretime hazır" kodunuzun istisna güvenli olduğundan emin misiniz?

İstisnalar kullanılarak oluşturulan 7/24 sunucularımın 7/24 çalıştığından eminim ve bellek sızdırmıyor.

  • Bundan emin olabilir misin?

Herhangi bir kodun doğru olduğundan emin olmak çok zordur. Tipik olarak, sadece sonuçlara göre gidilebilir

  • Çalışan alternatifleri biliyor ve / veya gerçekten kullanıyor musunuz?

Hayır. İstisnaları kullanmak, programlamada son 30 yıldır kullandığım alternatiflerden daha temiz ve daha kolay.


30
Bu cevabın bir değeri yok.
Matt Joiner

6
@MattJoiner O zaman sorunun değeri yok.
Miles Rout

5

SEH ve C ++ istisnaları arasındaki karışıklığı bir kenara bırakarak, istisnaların herhangi bir zamanda atılabileceğini bilmeniz ve kodunuzu akılda tutarak yazmanız gerekir. İstisna güvenlik ihtiyacı büyük ölçüde RAII, akıllı işaretçiler ve diğer modern C ++ tekniklerinin kullanımını yönlendirir.

İyi yapılandırılmış kalıpları izlerseniz, özel durum için güvenli kod yazmak özellikle zor değildir ve aslında her durumda hata döndürmelerini düzgün şekilde işleyen bir kod yazmaktan daha kolaydır.


3

EH genellikle iyidir. Ancak, C ++ 'ın uygulaması çok kolay değildir, çünkü istisnanın kapsamını yakalamanın ne kadar iyi olduğunu söylemek gerçekten zordur. Örneğin Java bunu kolaylaştırır, olası istisnaları işlemezseniz derleyici başarısız olma eğilimindedir.


2
Şiddetli kullanımı ile çok daha iyi noexcept.
einpoklum

2

Eclipse ve Java ile çalışmayı gerçekten seviyorum (Java'ya yeni), çünkü bir EH işleyicisi eksikse editörde hatalar atıyor. Bu, bir istisnayı ele almayı unutmayı çok daha zor hale getirir ...

Ayrıca, IDE araçlarıyla, try / catch bloğunu veya başka bir catch bloğunu otomatik olarak ekler.


5
İşaretli (Java) ve işaretsiz (c ++) istisnalar arasındaki fark budur (Java da bunlardan birkaçına sahiptir). Kontrol edilen istisnaların avantajı yazdıklarınızdır, ancak orada kendi dezavantajları vardır. Fark yaklaşımları ve sahip oldukları farklı sorunlar için Google.
David Rodríguez - dribeas

2

Bazılarımız Java gibi dilleri, C ++ ve C # 'da olduğu gibi görünmez yapmak yerine, yöntemlerle atılan tüm istisnaları bildirmeye zorlayan dilleri tercih ediyoruz.

Düzgün bir şekilde yapıldığında, başarısızlıkları çağrı zincirinde manuel olarak yaymanız gerekmiyorsa, hata dönüş kodlarından daha üstündür.

Bununla birlikte, düşük seviyeli API kitaplığı programlaması, istisna yönetiminden kaçınmalı ve hata dönüş kodlarına bağlı kalmalıdır.

Benim deneyim C ++ temiz istisna işleme kodu yazmak zor oldu. Sonunda new(nothrow)çok kullanıyorum .


4
Standart kitaplıkların çoğundan da kaçıyorsunuz? Kullanmak new(std::nothrow)yeterli değildir. Bu arada, C ++ 'da istisna-güvenli kod yazmak Java'dan daha
kolaydır

3
Java tarafından kontrol edilen istisna kullanılabilirliği oldukça abartılı. Aslında, Java dışı diller, bir başarı olarak DEĞİLDİR. Bu yüzden C ++ 'daki "throw" ifadesi artık kullanılmıyor ve C # bunları uygulamak konusunda hiçbir zaman ciddi olarak düşünülmedi (bu bir tasarım seçimiydi). Java için aşağıdaki belge aydınlatıcı olabilir: googletesting.blogspot.com/2009/09/…
paercebal

2
Benim deneyimim, C ++ 'da istisna-güvenli kod yazmanın o kadar da zor olmadığı ve genel olarak daha temiz kodlara yol açma eğiliminde olması. Tabii ki, bunu yapmayı öğrenmek zorundasınız.
David Thornley

2

İstisna için güvenli kod yazmak için elimden gelenin en iyisini yapmaya çalışıyorum, evet.

Araçlarla Ben göz kulak özen hangi hatlar atabilir. Herkes yapamaz ve bunu akılda tutmak önemlidir. Anahtar, standartta tanımlanan istisna garantileri hakkında düşünmek ve kodunuzu tatmin edecek şekilde tasarlamaktır.

Bu işlem güçlü istisna garantisi sağlamak için yazılabilir mi? Temel olana razı olmak zorunda mıyım? Hangi satırlar istisnalar atabilir ve eğer yaparlarsa nesneyi bozmadıklarından nasıl emin olabilirim?


2
  • Gerçekten istisna güvenlik kodu yazıyor musunuz? [Öyle bir şey yok. İstisnalar, yönetilen bir ortamınız olmadığı sürece hatalara karşı bir kağıt kalkanıdır. Bu ilk üç soru için geçerlidir.]

  • Çalışan alternatifleri biliyor ve / veya gerçekten kullanıyor musunuz? [Neye alternatif? Buradaki problem, insanların gerçek hataları normal program işleminden ayırmamasıdır. Normal program işlemi (yani bir dosya bulunamadı) ise, gerçekten hata işleme değildir. Bu gerçek bir hataysa, bunu 'ele almanın' bir yolu yoktur veya gerçek bir hata değildir. Buradaki amacınız neyin yanlış gittiğini öğrenmek ve e-tabloyu durdurmak ve bir hata kaydetmek, sürücüyü ekmek kızartma makinenize yeniden başlatmak veya sadece yazılımcı olsa bile jetfighter'ın uçmaya devam edebilmesi için dua edebilmesi ve en iyisini ummasıdır.]


0

Bir çok insan (en çok söyleyebilirim bile) yapar.

İstisnalar hakkında gerçekten önemli olan şey, herhangi bir taşıma kodu yazmazsanız - sonucun tamamen güvenli ve iyi davranılmış olmasıdır. Paniğe çok hevesli ama güvenli.

Güvenli olmayan bir şey elde etmek için işleyicilerde aktif olarak hatalar yapmanız gerekir ve yalnızca catch (...) {} hata kodunu yoksaymakla karşılaştırır.


4
Doğru değil. İstisna açısından güvenli olmayan kod yazmak çok kolaydır. Örneğin: f = new foo(); f->doSomething(); delete f;doSomething yöntemi bir istisna atarsa, bellek sızıntısı olur.
Kristopher Johnson

1
Program sona erdiğinde sorun olmaz, değil mi? Yürütmeye devam etmek için istisnaları aktif olarak yutmanız gerekir . Peki, temizlemeden sonlandırmanın hala kabul edilemez olduğu bazı özel durumlar vardır, ancak bu gibi durumlar herhangi bir programlama dili ve stilinde özel dikkat gerektirir.
ima

Ne C ++ 'da ne de yönetilen kodda istisnaları göz ardı edemezsiniz (ve işleme kodu yazamazsınız). Güvensiz olacak ve iyi davranmayacak. Belki bazı oyuncak kodu hariç.
Frunsi

1
Uygulama kodundaki istisnaları yoksayarsanız, dış kaynaklar söz konusu olduğunda program yine de iyi davranmayabilir. Doğru, işletim sistemi dosya tanıtıcıları, kilitler, prizler vb. Ancak her şey ele alınmaz, örneğin gereksiz dosyalar bırakabilir veya onlara yazarken dosyalara zarar verebilir vb. İstisnaları yoksayarsanız, Java'da bir sorununuz vardır, C ++ 'da RAII ile çalışabilirsiniz (ancak RAII tekniğini kullandığınızda, büyük olasılıkla istisnaları önemsediğiniz için bunları kullanırsınız ) ...
Frunsi

3
Lütfen, sözlerimi bükmeyin. "Herhangi bir işlem kodu yazmazsanız" yazdım - ve tam olarak bunu demek istedim. İstisnaları yok saymak için kod yazmanız gerekir.
ima
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.