Kaynak Edinimi ile Başlatma (RAII) nedir?
Kaynak Edinimi ile Başlatma (RAII) nedir?
Yanıtlar:
İnanılmaz derecede güçlü bir konsept için gerçekten korkunç bir isim ve belki de C ++ geliştiricilerinin diğer dillere geçtiklerinde kaçırdığı 1 numaralı şeyden biri. Henüz kavranmamış gibi görünse de, bu kavramı Scope-Bound Kaynak Yönetimi olarak yeniden adlandırmaya çalışmak için biraz hareket oldu .
'Kaynak' dediğimizde sadece bellek demek değildir - dosya tutamakları, ağ yuvaları, veritabanı tutamakları, GDI nesneleri olabilir ... Kısacası, sınırlı bir kaynağa sahip olduğumuz şeyler ve bu yüzden yapabilmemiz gerekir kullanımlarını kontrol eder. 'Kapsama bağlı' yönü, nesnenin ömrünün bir değişkenin kapsamına bağlı olduğu anlamına gelir, bu nedenle değişken kapsam dışına çıktığında, yıkıcı kaynağı serbest bırakır. Bunun çok kullanışlı bir özelliği, daha fazla istisna güvenliği sağlamasıdır. Örneğin, bunu karşılaştırın:
RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation(); // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks
RAII one ile
class ManagedResourceHandle {
public:
ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
~ManagedResourceHandle() {delete rawHandle; }
... // omitted operator*, etc
private:
RawResourceHandle* rawHandle;
};
ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();
Bu son durumda, istisna atıldığında ve yığın çözüldüğünde, yerel değişkenler yok edilir, bu da kaynağımızın temizlenmesini ve sızmamasını sağlar.
Scope-Bound
Depolama sınıfı belirleyicilerinin kapsamla birlikte bir kuruluşun depolama süresini belirlediğinden, burada en iyi ad seçiminin olup olmadığından emin değilim . Kapsamına daraltmak belki de yararlı bir basitleştirmedir, ancak% 100 kesin değildir
Bu bir programlama deyimidir.
Bu, kaynak kullanımdayken ne olursa olsun, sonunda serbest bırakılacağını garanti eder (normal geri dönüş, içerdiği nesnenin imhası veya atılmış bir istisna nedeniyle).
C ++ 'da yaygın olarak kullanılan iyi bir uygulamadır, çünkü kaynaklarla başa çıkmanın güvenli bir yolu olmasının yanı sıra, hata işleme kodunu ana işlevsellik ile karıştırmanız gerekmediğinden kodunuzu daha temiz hale getirir.
*
Güncelleme: "local", yerel bir değişken veya sınıfın statik olmayan bir üye değişkeni anlamına gelebilir. İkinci durumda üye değişkeni, sahip nesnesiyle başlatılır ve yok edilir.
**
Güncelleme2: @sbi'nin işaret ettiği gibi, kaynak - genellikle yapıcı içinde tahsis edilmiş olmasına rağmen - dışarıda tahsis edilebilir ve parametre olarak aktarılabilir.
open()
/ close()
yöntem olmadığını, sadece yapıcıyı ve yıkıcı olduğunu, bu yüzden kaynağın 'tutulması', nesnenin ömrüdür, içerik (yığın) veya açıkça (dinamik ayırma) tarafından işlenir
"RAII", "Kaynak Edinimi Başlatmadır" anlamına gelir ve aslında kaynak edinimi (ve bir nesnenin başlatılması) değil, kaynağı serbest bırakması (bir nesnenin imhası yoluyla) serbest bırakılması nedeniyle oldukça yanlış bir isimdir. ).
Ama RAII sahip olduğumuz isim ve yapışıyor.
Özünde, deyim, yerel, otomatik nesnelerdeki kaynakları (bellek parçaları, açık dosyalar, kilitsiz muteksler, ad-it-it) sarmalayan ve nesne yok edildiğinde kaynağı serbest bırakan nesnenin yıkıcısına sahip ait olduğu kapsamın sonu:
{
raii obj(acquire_resource());
// ...
} // obj's dtor will call release_resource()
Tabii ki, nesneler her zaman yerel, otomatik nesneler değildir. Onlar da bir sınıfın üyesi olabilirler:
class something {
private:
raii obj_; // will live and die with instances of the class
// ...
};
Bu tür nesneler belleği yönetiyorsa, genellikle "akıllı işaretçiler" olarak adlandırılırlar.
Bunun birçok varyasyonu var. Örneğin, ilk kod parçacıklarında birisi kopyalamak isterse ne olacağı sorusu ortaya çıkar obj
. En kolay çıkış, kopyalamaya izin vermemek olacaktır. std::unique_ptr<>
bir sonraki C ++ standardının öne çıkardığı gibi standart kütüphanenin parçası olacak akıllı bir işaretçi bunu yapar.
Başka bir akıllı işaretçi, std::shared_ptr
sahip olduğu kaynağın (dinamik olarak ayrılmış bir nesne) "paylaşılan sahipliğini" içerir. Yani, serbestçe kopyalanabilir ve tüm kopyalar aynı nesneyi ifade eder. Akıllı işaretçi, aynı nesneye kaç kopya atıfta bulunduğunu izler ve sonuncusu yok edildiğinde onu siler.
Üçüncü bir varyantstd::auto_ptr
bir tür hareket semantiği uygular: Bir nesneye yalnızca bir işaretçi aittir ve bir nesneyi kopyalamaya çalışmak nesnenin sahipliğini kopyalama işleminin hedefine aktarmaya neden olur (sözdizimi korsanlığı yoluyla).
std::auto_ptr
eski sürümü std::unique_ptr
. std::auto_ptr
C ++ 98'de mümkün olduğunca simüle edilmiş hareket std::unique_ptr
semantiği, C ++ 11'in yeni hareket semantiğini kullanır. Yeni sınıf, C ++ 11'in taşıma semantiği daha açık ( std::move
geçici olanlar dışında gerektirir ), çünkü const olmayan öğeden herhangi bir kopya için varsayılan olarak oluşturuldu std::auto_ptr
.
Bir nesnenin ömrü kapsamı tarafından belirlenir. Bununla birlikte, bazen yaratıldığı kapsamdan bağımsız olarak yaşayan bir nesne yaratmamız gerekir veya faydalıdır. C ++ 'da, operatör new
böyle bir nesne oluşturmak için kullanılır. Ve nesneyi yok etmek için operatör delete
kullanılabilir. Operatör tarafından oluşturulan nesneler new
dinamik olarak ayrılır, yani dinamik bellekte ( yığın veya serbest depolama olarak da adlandırılır) ayrılır . Böylece, tarafından oluşturulan bir nesne, new
açıkça kullanılarak yok edilene kadar var olmaya devam edecektir delete
.
Kullanırken oluşabilecek bazı hatalar new
ve delete
şunlardır:
new
bir nesneyi ayırmak ve nesneyi unutmak için delete
kullanılır.delete
kullanın.delete
bir nesneye iki kez denenir.Genellikle, kapsamlandırılmış değişkenler tercih edilir. Bununla birlikte, RAII alternatif olarak kullanılabilir new
ve delete
canlı, bağımsız bir şekilde kapsamının bir nesne yapmak için. Böyle bir teknik, işaretçiyi öbek üzerinde tahsis edilen nesneye götürüp bir tutamaç / yönetici nesnesine yerleştirmekten oluşur . İkincisi, nesneyi yok etmeye özen gösterecek bir yıkıcıya sahiptir. Bu, nesnenin kendisine erişmek isteyen herhangi bir işlev için kullanılabilir olmasını ve açık nesneye ihtiyaç duyulmadan, tanıtıcı nesnenin ömrü sona erdiğinde nesnenin yok edilmesini garanti eder .
RAII kullanan C ++ standart kitaplığından örnekler std::string
ve std::vector
.
Şu kod parçasını düşünün:
void fn(const std::string& str)
{
std::vector<char> vec;
for (auto c : str)
vec.push_back(c);
// do something
}
bir vektör oluşturduğunuzda ve öğeleri ona aktardığınızda, bu tür öğeleri ayırmayı ve yeniden ayırmayı umursamazsınız. Vektör new
öbek üzerindeki öğeleri için yer ayırmak ve bu alanı boşaltmak için kullanır delete
. Bir vektörün kullanıcısı olarak uygulama ayrıntılarını önemsemezsiniz ve vektörün sızdırmamasına güvenirsiniz. Bu durumda, vektör, elemanlarının tutamak nesnesidir .
Standart kütüphanesinden diğer örnekleri kullanımı RAII olduğu std::shared_ptr
, std::unique_ptr
ve std::lock_guard
.
Bu tekniğin bir diğer adı da Kapsam Kapsamlı Kaynak Yönetiminin kısaltması olan SBRM'dir .
Ortaya Çıkan Tasarım Desenleri ile C ++ Programlama kitabı RAII'yi şöyle tarif eder:
Nerede
Kaynaklar sınıf olarak uygulanır ve tüm işaretçilerin etrafında sınıf sarmalayıcıları bulunur (onları akıllı işaretçiler haline getirir).
Kaynaklar kurucularını çağırarak elde edilir ve yıkıcılarını çağırarak dolaylı olarak (edinme sırasının tersine) serbest bırakılır.
Bir RAII sınıfının üç bölümü vardır:
RAII, "Kaynak Edinimi başlatmadır" anlamına gelir. RAII'nin "kaynak edinimi" bölümü, daha sonra sona erdirilmesi gereken bir şeye başladığınız yerdir, örneğin:
"Başlatma" bölümü, edinimin bir sınıfın yapıcısının içinde gerçekleştiği anlamına gelir.
Manuel bellek yönetimi, programcıların derleyicinin icadından bu yana kaçınmanın yollarını icat ettiği bir kabustur. Çöp toplayıcılarla dil programlamak hayatı kolaylaştırır, ancak performans pahasına. Bu makalede - Çöp Toplayıcıyı ortadan kaldırmak: RAII Yolu , Toptal mühendisi Peter Goodspeed-Niklaus bize çöp toplayıcılarının tarihine bir göz atıyor ve sahiplik ve borçlanma kavramlarının güvenlik garantilerinden ödün vermeden çöp toplayıcılarını nasıl ortadan kaldırabileceğini açıklıyor.