Orada mükemmel cevaplar var, bu yüzden unuttuğum bazı şeyleri ekliyorum.
0. RAII kapsamlarla ilgilidir
RAII ikisiyle de ilgilidir:
- yapıcıda bir kaynak elde etmek (hangi kaynak olursa olsun) ve yıkıcıda onu geri almak.
- değişken bildirildiğinde yapıcının çalıştırılması ve yıkıcının değişken kapsam dışına çıktığında otomatik olarak yürütülmesi.
Diğerleri bunu zaten cevapladı, bu yüzden ayrıntıya girmeyeceğim.
1. Java veya C # ile kodlarken, zaten RAII kullanıyorsunuz ...
MONSIEUR JOURDAIN: Ne! "Nicole, terliklerimi getir ve içkimi bana ver" dediğimde, bu düz yazı mı?
PHILOSOPHY MASTER: Evet efendim.
MONSIEUR JOURDAIN: Kırk yıldan fazla bir süredir onun hakkında hiçbir şey bilmeden düz yazı konuşuyorum ve bunu bana öğrettiğim için sana çok minnettarım.
- Molière: Orta Sınıf Beyefendi, Perde 2, Sahne 4
Mösyö Jourdain'in düzyazıda yaptığı gibi, C # ve hatta Java insanları zaten RAII kullanıyor, ancak gizli yollarla. Örneğin, aşağıdaki Java kodu (aynı şekilde C # ile değiştirilerek yazılır)synchronized
ile lock
):
void foo()
{
// etc.
synchronized(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
... zaten RAII kullanıyor: Mutex edinimi (synchronized
veya lock
) ve edinme kapsamdan çıkılırken yapılacaktır.
RAII'yi hiç duymamış insanlar için bile neredeyse hiçbir açıklama gerektirmeyecek kadar doğal.
C ++ 'nın Java ve C # üzerinde sahip olduğu avantaj, RAII kullanılarak her şeyin yapılabilmesidir. Örneğin, doğrudan yerleşik eşdeğeri yoktursynchronized
ne de lock
C ++, ama biz hala onları olabilir.
C ++ 'da şöyle yazılır:
void foo()
{
// etc.
{
Lock lock(someObject) ; // lock is an object of type Lock whose
// constructor acquires a mutex on
// someObject and whose destructor will
// un-acquire it
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
Java / C # yoluyla kolayca yazılabilir (C ++ makroları kullanılarak):
void foo()
{
// etc.
LOCK(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
2. RAII'nin alternatif kullanımları vardır
BEYAZ TAVŞAN: [şarkı söyler] Geç kaldım / Geç kaldım / Çok önemli bir tarih için. / "Merhaba" demeye zaman yok. / Güle güle. / Geç kaldım, geç kaldım, geç kaldım.
- Alice Harikalar Diyarında (Disney versiyonu, 1951)
Yapıcının ne zaman çağrılacağını (nesne bildiriminde) bilirsiniz ve karşılık gelen yıkıcının (kapsamın çıkışında) ne zaman çağrılacağını bilirsiniz, böylece sadece bir satırla neredeyse sihirli bir kod yazabilirsiniz. C ++ harikalar diyarına hoş geldiniz (en azından bir C ++ geliştiricisinin bakış açısından).
Örneğin, bir sayaç nesnesi yazabilir (bunu bir alıştırma olarak kabul ettim) ve yukarıdaki kilit nesnesinin kullanıldığı gibi sadece değişkenini bildirerek kullanabilirsiniz:
void foo()
{
double timeElapsed = 0 ;
{
Counter counter(timeElapsed) ;
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
Tabii ki, yine bir makro kullanılarak Java / C # yolu ile yazılabilir:
void foo()
{
double timeElapsed = 0 ;
COUNTER(timeElapsed)
{
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
3. C ++ neden eksik finally
?
[BAĞIRMA] Bu son geri sayım!
- Avrupa: Son Geri Sayım (üzgünüm, burada alıntılar kalmamıştı ... :-)
finally
Fıkra kapsamı çıkışında durumunda kaynak bertarafını işlemek için C # / Java kullanılır (bir içinden return
veya atılmış istisna).
Astute belirtim okuyucuları, C ++ 'nın nihayet cümlesi olmadığını fark edeceklerdir. Ve bu bir hata değildir, çünkü RAII zaten kaynak kullanımının üstesinden geldiğinden C ++ buna ihtiyaç duymaz. (Ve inan bana, bir C ++ yıkıcı yazmak, doğru Java nihayet cümlesini veya hatta bir C # 'ın doğru Dispose yöntemini yazmaktan çok daha kolaydır).
Yine de bazen bir finally
cümle harika olurdu. Bunu C ++ ile yapabilir miyiz? Evet yapabiliriz! Ve yine alternatif bir RAII kullanımıyla.
Sonuç: RAII, C ++ 'da bir felsefeden daha fazlasıdır: C ++' dır
RAII? BU C ++ IS !!!
- C ++ geliştiricisinin öfkeli yorumu, belirsiz bir Sparta kralı ve 300 arkadaşı tarafından utanmadan kopyalandı
C ++ 'da belirli bir deneyim düzeyine ulaştığınızda , dönüştürücü ve yıkıcıların otomatikleştirilmiş yürütmesi açısından RAII açısından düşünmeye başlarsınız .
Kapsamlar açısından düşünmeye başlarsınız ve {
ve }
karakterleri kodunuzda en önemlilerinden biri haline gelir.
Ve hemen hemen her şey RAII açısından tam olarak uyuyor: istisna güvenliği, muteksler, veritabanı bağlantıları, veritabanı istekleri, sunucu bağlantısı, saatler, işletim sistemi tutamaçları vb. Ve son olarak, ama en önemlisi, bellek.
Veritabanı kısmı göz ardı edilemez, çünkü fiyatı ödemeyi kabul ederseniz, bir " işlemsel programlama " tarzında yazabilirsiniz , sonunda tüm değişiklikleri yapmak isteyip istemediğinize karar verene kadar satırları ve satırları çalıştırabilirsiniz. veya mümkün değilse, tüm değişikliklerin geri alınması (her satır en azından Güçlü İstisna Garantisini karşıladığı sürece). (bu Herb's Sutter makalesinin ikinci bölümüne bakın işlemsel programlama için ).
Ve bir bulmaca gibi her şey uyuyor.
RAII, C ++ 'nın o kadar büyük bir parçası ki, C ++ onsuz C ++ olamaz.
Bu, deneyimli C ++ geliştiricilerinin neden RAII'ye bu kadar aşık olduğunu ve başka bir dili denerken neden ilk aradıkları şeyin RAII olduğunu açıklıyor.
Ve Çöp Toplayıcının kendi başına muhteşem bir teknoloji parçası olmasına rağmen, bir C ++ geliştiricisinin bakış açısından neden bu kadar etkileyici olmadığını açıklıyor:
- RAII zaten bir GC tarafından ele alınan vakaların çoğunu ele alıyor
- Bir GC, saf yönetilen nesneler üzerinde döngüsel referanslarla RAII'den daha iyi işlem yapar (zayıf işaretçilerin akıllı kullanımıyla azaltılır)
- Yine de GC bellekle sınırlıdır, RAII ise her tür kaynağı idare edebilir.
- Yukarıda açıklandığı gibi, RAII çok daha fazlasını yapabilir ...