unique_ptr sınıf şablonu – 3

Bu yazı unique_ptr sınıf sınıf şablonuna ilişkin yazılardan üçüncüsü. Daha önceki iki yazımda daha çok Jossutis‘in Standard C++ library isimli kitabından faydalanmıştım. Bu yazıda sınıfın diğer bazı önemli özelliklerinden ve kullanım olanaklarından söz edeceğim.

make_unique işlev şablonu

Hatırlayacağımız gibi unique_ptr sınıf şablonu dile C++11 standartlarıyla eklenmişti. Ancak C++11 standartlarında make_unique işlev şablonu yer almıyordu. Bu eksiklik C++14 standartlarıyla karşılandı. Mükemmel gönderim (perfect forwarding) mekanizmasından faydalanan make_unique değişken sayıda parametreli (variadic) işlev şablonu, bir unique_ptr nesnesini sarmalayarak geri döndürüyor:

Bu işlev şablonunun aşağıdaki gibi gerçeklendiğini düşünebiliriz:

İşleve kontrol edilecek dinamik nesnenin kurucu işlevinin kullanacağı argümanlar gönderiliyor. Aşağıdaki örneğe bakalım:

Yukarıdaki kodda make_unique işlev şablonundan üretilecek bir işlevle string sınıfının size_t ve char parametreli kurucu işlevine 10 ve ‘A’ değerleri gönderilerek önce dinamik bir string nesnesi hayata getiriliyor. Daha sonra hayata gelen dinamik nesneyi kontrol eden bir unique_ptr nesnesi geri döndürülüyor.

unique_ptr::get üye işlevi

unique_ptr sınıfının get işlevinin geri dönüş değeri kontrol edilen dinamik nesnenin adresi. unique_ptr nesnemizin yaşamını kontrol ettiği bir dinamik nesne yoksa işlev nullptr adresini döndürüyor:

Bu işlev dikkatli kullanılmalı. Örneğin bu işlevden elde edilen adresle yeni bir unique_ptr nesnesinin oluşturulması ya da bu adresteki nesnenin delete edilmesi çalışma zamanı hatasına neden olurdu:

Yukarıdaki kodda hem up1 hem de up2 unique_ptr nesneleri aynı dinamik string nesnesini kontrol ediyor hale geliyor, böylece tek sahiplik (exclusive ownwership) ilkesi çiğneniyor. Her iki nesnenin de sonlandırıcı işlevi aynı dinamik string nesnesini delete edecek (double deletion) ve bu durumda çalışma zamanı hatası oluşacak. Benzer hataya aşağıdaki gibi bir kodla da düşülebilir:

Yukarıdaki kodda get işlevinden adresi alınan dinamik nesne delete ediliyor. up nesnesinin sonlandırıcı işlevi çağrıldığında aynı nesneyi yeniden delete edecek. Bu durumda çalışma zamanı hatası oluşacak.

deleter şablon tür parametresi

unique_ptr sınıf şablonunun tanımını bir hatırlayalım:

Şablonumuzun ikinci tür parametresi olan Deleter türüne bağlı işleve yapılan çağrı hayatı kontrol edilen nesnenin delete edilmesini sağlıyor. Bu şablon tür parametresinin varsayılan tür argumanı olarak standart default_delete şablonunu aldığını görüyorsunuz. Bu durumda

gibi bir tanımlama

biçiminde bir tanımlamaya eşdeğer.

default_delete sınıf şablonunun (basitleştirilmiş) kodunun şu şekilde olduğunu düşünebilirsiniz:

default_delete sınıf şablonu dizi türleri için kısmi özelleştirmeye (partial specialization) tabi tutulmuş. Bu özelleştirmeye ilişkin kodun aşağıdaki gibi olduğunu düşünebilirsiniz:

Bu da şu anlama geliyor: default_delete sınıf şablonlarının kullanılmasıyla, unique_ptr nesnelerinin kontrol ettiği nesnelerin hayatları uygun biçimde delete ve delete[] işleçleriyle sonlandırılıyor.
Eğer yaşamı kontrol edilen dinamik nesnenin hayatının sonlandırılması özelleştirilmiş bir şekilde gerçekleşecek ise bu durumda kendi deleter türümüzü şablon tür argümanı olarak kullanmak zorundayız. Bu amaçla global bir işlev, bir işlev sınıfı (functor class), bir lambda, ya da bir işlev nesnesi kullanabiliriz. İlk örneğimizde global bir işlev kullanıyoruz:

Yukarıdaki kodda deleter olarak kullandığımız global fdel isimli işlev, delete işleminden önce delete edilecek nesnenin adresini standart çıkış akımına yazdırıyor. Şimdi de aynı iş için bir lambda kullanıyoruz:

lambdaya ilişkin kapanış sınıfının (closure) tür bilgisi için decltype işlecinin kullanımına dikkat ediniz. Şüphesiz bu iş için kendimiz de bir functor sınıf oluşturabilirdik:

Son olarak deleter olarak std::function sınıf şablonunu kullanıyoruz:

Yukarıdaki kodda yapılan şablon eş isim bildirimine dikkat ediniz. Bu bildirim ile T bir tür olmak üzere,

açılımı

açılımına karşılık geliyor. Deleter olarak

sınıfının kullanılması ile artık deleter olarak uygun parametrik yapıda işlev sağlayan herhangi bir çağrılabilir varlık (callable) kullanılabilir hale geliyor.

Bir unique_ptr nesnesinin yaşamını kontrol ettiği kaynağın new işleciyle oluşturulması zorunlu değil. Aşağıdaki kodda unique_ptr nesneleri standart fopen işleviyle oluşturulan dosyaları kontrol ediyor:

Yukarıdaki kodda up1 nesnesi için deleter olarak standart fclose işlevini sarmalayan global file_close işlevini kullanılıyor.  up2 nesnesi için ise deleter olarak bir lambdanın kullanıldığını görüyorsunuz. up3 nesnesi ise deleter türü olarak FileCloser functor sınıfını kullanıyor.

unique_ptr nesnelerinin kaplarda tutulması

Dinamik ömre sahip nesneleri STL kaplarında tutmanın bir yolu da unique_ptr sınıf şablonunu kullanmak. Aşağıdaki kodu inceleyelim:

Yukarıdaki kodda önce içinde std::unique_ptr<std::string> sınıf nesnleri tutacak std::vector sınıf şablonu açılımına UpStrvec eş ismi veriliyor. main işlevi içinde myvec isimli bir vector nesnesinin oluşturulduğunu görüyorsunuz. vector sınıfının push_back ve emplace back işlevleriyle vector‘e sırasıyla kayhancan, necati, kaan ve ayhan isimleri ekleniyor. myvec nesnesi için sonlandırıcı işlev çağrıldığında kapta tutulmakta olan unique_ptr nesnelerinin de sonlandırıcı işlevleri çağrılacak ve böylece dinamik string nesneleri delete edilecek.

Share

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Kod Eklemek İçin Okuyun