İlginç cevaplar: Hepsine (şimdiye kadar) katılmama rağmen, bu sorunun şimdiye kadar tamamen göz ardı edilen olası çağrışımları var.
Yukarıdaki basit örnek kaynak tahsisi ile genişletilirse ve ardından kaynakların serbest bırakılmasıyla sonuçlanabilecek bir hata kontrolü yapılırsa, resim değişebilir.
Yeni başlayanların alabileceği saf yaklaşımı düşünün :
int func(..some parameters...) {
res_a a = allocate_resource_a();
if (!a) {
return 1;
}
res_b b = allocate_resource_b();
if (!b) {
free_resource_a(a);
return 2;
}
res_c c = allocate_resource_c();
if (!c) {
free_resource_b(b);
free_resource_a(a);
return 3;
}
do_work();
free_resource_c(c);
free_resource_b(b);
free_resource_a(a);
return 0;
}
Yukarıdakiler, erken dönme tarzının aşırı bir versiyonunu temsil eder. Karmaşıklığı arttığında, kodun zaman içinde nasıl çok tekrarlayıcı ve bakımsız hale geldiğine dikkat edin. Günümüzde insanlar bunları yakalamak için istisna işlemeyi kullanabilir .
int func(..some parameters...) {
res_a a;
res_b b;
res_c c;
try {
a = allocate_resource_a(); # throws ExceptionResA
b = allocate_resource_b(); # throws ExceptionResB
c = allocate_resource_c(); # throws ExceptionResC
do_work();
}
catch (ExceptionBase e) {
# Could use type of e here to distinguish and
# use different catch phrases here
# class ExceptionBase must be base class of ExceptionResA/B/C
if (c) free_resource_c(c);
if (b) free_resource_b(b);
if (a) free_resource_a(a);
throw e
}
return 0;
}
Philip, aşağıdaki örneğe baktıktan sonra, yukarıdaki yakalama bloğunun içinde kesintisiz bir anahtar / kasa kullanmayı önerdi . Biri (typeof (e)) değiştirebilir ve sonra free_resourcex()
çağrıların arasından geçebilir, ancak bu önemsiz değildir ve tasarımın dikkate alınması gerekir . Kesintisiz bir anahtarın / kasanın, aşağıdaki zincirleme etiketlere sahip olana tamamen benzediğini unutmayın ...
Mark B belirttiği gibi, C ++ bunun takip etmek iyi tarzı olarak kabul edilir Kaynak Edinme Başlatma olduğu ilkesi, RAII Short. Kavramın özü, kaynakları elde etmek için nesne somutlaştırmayı kullanmaktır. Nesneler kapsam dışına çıkar çıkmaz kaynaklar otomatik olarak serbest bırakılır ve yıkıcıları çağrılır. Birbirine bağlı kaynaklar için, doğru ayırma sırasının sağlanması ve tüm yıkıcılar için gerekli verilerin mevcut olacağı şekilde nesne türlerinin tasarlanması için özel dikkat gösterilmelidir.
Veya istisnai günlerde şunları yapabilir:
int func(..some parameters...) {
res_a a = allocate_resource_a();
res_b b = allocate_resource_b();
res_c c = allocate_resource_c();
if (a && b && c) {
do_work();
}
if (c) free_resource_c(c);
if (b) free_resource_b(b);
if (a) free_resource_a(a);
return 0;
}
Ancak bu aşırı basitleştirilmiş örneğin birkaç dezavantajı vardır: Yalnızca tahsis edilen kaynaklar birbirine bağlı değilse kullanılabilir (örneğin, bellek ayırmak için kullanılamaz, sonra bir dosya tanıtıcısı açar, ardından tutamacından belleğe veri okur) ) ve dönüş değerleri olarak bireysel, ayırt edilebilir hata kodları sağlamaz.
Linus Torvalds , kodu hızlı (!), Kompakt ve kolayca okunabilir ve genişletilebilir tutmak için, meşhur goto'yu kesinlikle mantıklı bir şekilde kullanarak bile, kaynaklarla ilgilenen farklı bir çekirdek kodu stili uyguladı :
int func(..some parameters...) {
res_a a;
res_b b;
res_c c;
a = allocate_resource_a() || goto error_a;
b = allocate_resource_b() || goto error_b;
c = allocate_resource_c() || goto error_c;
do_work();
error_c:
free_resource_c(c);
error_b:
free_resource_b(b);
error_a:
free_resource_a(a);
return 0;
}
Çekirdek posta listeleri hakkındaki tartışmanın özü, goto deyimine göre "tercih edilen" çoğu dil özelliğinin büyük, ağaç benzeri if / else, istisna işleyicileri, döngü / break / continue ifadeleri vb. Gibi örtük gotos olmasıdır. Ve yukarıdaki örnekteki goto'lar, sadece küçük bir mesafeden atladıkları, açık etiketlere sahip oldukları ve hata koşullarını takip etmek için diğer karmaşanın kodunu serbest bıraktıkları için iyi kabul edilir. Bu soru ayrıca burada stackoverflow'da tartışılmıştır .
Ancak son örnekte eksik olan şey, bir hata kodu döndürmenin güzel bir yoludur. result_code++
Her free_resource_x()
aramadan sonra bir eklemeyi ve bu kodu döndürmeyi düşünüyordum, ancak bu, yukarıdaki kodlama stilinin bazı hız kazanımlarını dengeliyor. Ve başarı durumunda 0 döndürmek zordur. Belki ben hayal gücümden yoksun ;-)
Yani, evet, erken getirileri kodlama konusunda büyük bir fark olduğunu düşünüyorum. Ancak, derleyici için yeniden yapılandırmanın ve optimize etmenin daha zor veya imkansız olan yalnızca daha karmaşık kodda açıkça görüldüğünü düşünüyorum. Bu genellikle kaynak tahsisi devreye girdiğinde ortaya çıkar.