Bir önceki yazımda auto_ptr'ın yeterli olmadığı durumlar için boost.org'un akıllı göstergelerini önermiştim. [1] Bu yazıda Boost akıllı göstergelerinin beş tanesini kısa örneklerle tanıtmaya çalışacağım:
Boost'a sonradan eklenmiş olan intrusive_ptr şablonunu, daha aşağıda sıraladığım nedenlerden dolayı bu yazının konusu dışında bırakacağım.
Boost Kütüphanesi
Standart kütüphaneler bir çok nedenden ötürü kısıtlıdırlar; akla gelen her olanak standartta yer alamaz:
- Standardın daha fazla geciktirilmesi istenmediği için belirli bir olanağın kabulü için zaman kalmamış olabilir
- Eksikliği hissedilen iş bazı standart olanaklar yardımıyla zaten yerine getirilebiliyordur
- Olanak; genelin değil, küçük bir azınlığın ihtiyacıdır
- Olanağın dilin desteklendiği her ortamda gerçeklenmesi mümkün değildir
- vs.
Boost kütüphaneleri, çeşitli nedenlerle standart C++ kütüphanesinin kapsamı dışında kalmış olan bir grup sınıf, sınıf şablonu, ve makrolar topluluğudur.
Boost kütüphaneleri tamamen gönüllüler tarafından önerilir, gerçeklenir, denetlenir, ve kullanıma sunulur.
Rassal sayı üretimi, Python diliyle uyum, platformdan bağımsız işletim dizisi (thread) programcılığı ve düzenli ifadeler gibi çok sayıdaki olanak arasından benim kendi adıma en çok yararlandıklarım; akıllı göstergelerdir.
Boost Başlıkları
Boost kütüphaneleri hemen hemen tamamen başlık kütükleri içinde tanımlanmışlardır. [2] Bu yüzden, kütüphanelerin büyük çoğunluğunu yalnızca başlıklarını edinerek kullanabilirsiniz. Kullanmaya başlamadan önce, istediğiniz bir yere kopyalamak dışında herhangi bir kurulum işlemi yapmanıza gerek yoktur.
Bütün başlık kütükleri 'boost' dizini altında ve 'hpp' uzantıları ile yer alırlar.
Boost Ad Alanı
Standart kütüphanedeki adların 'std' ad alanı içinde tanımlanmış olmalarına benzer şekilde, Boost kütüphanesindeki adlar da 'boost' ad alanı içinde tanımlanmışlardır.
O yüzden, o olanakları kullanırken ya tam adlarını yazmak, ya da 'using' bildirimleri veya 'using' komutları kullanmak gerekir.
Bu üç yöntemi, kolayca tür dönüştürme amacıyla kullanılan boost::lexical_cast'i kullanarak göstermek istiyorum:
/* Bu islev lexical_cast'i tam adiyla kullaniyor */ void tamsayi(string const & yaziyla) { int sayi = boost::lexical_cast<int>(yaziyla); cout << (sayi * 2) << ' '; }
/* Bu islev lexical_cast'in tam adini yazmak zorunda degil, cunku bir 'using' bildirimi kullaniyor */ void kesirliSayi(string const & yaziyla) { /* Bu islev icinde 'lexical_cast' goruldugunde onun 'boost::lexical_cast' oldugu anlasilsin diye... */ using boost::lexical_cast;
/* Burada 'boost::' kullanmak zorunda degiliz... */ double sayi = lexical_cast<double>(yaziyla); cout << (sayi + 0.6) << ' '; }
/* Bu noktadan sonraki islevler acikca 'boost::' yazmak zorunda degiller cunku bir 'using' komutu kullaniliyor... */
using namespace boost;
/* Bu islev yalnizca 'lexical_cast' yaziyor ve onun ne oldugu anlasiliyor. */ void karakter(string const & yaziyla) { char k = lexical_cast<char>(yaziyla); ++k; cout << k << ' '; }
int main() { tamsayi("123"); kesirliSayi("4.5"); karakter("a"); }
Ben yazının geri kalanında 'using' komutu yöntemini kullanacak ve her seferinde 'boost::' yazmaktan kurtulacağım.
Boost akıllı göstergeleri sınıf şablonudurlar
Bu göstergeler, her türle çalışabilsinler diye sınıf şablonu olarak gerçeklenmişlerdir. Hatırlarsanız, sınıf şablonlarını kullanırken şablon parametrelerini açıkça belirtmek gerekir.
Onun için, örneğin 'int' türünden bir nesneden sorumlu olan bir scoped_ptr'ı 'int' türünü açılı parantezler arasında belirterek şöyle tanımlarız:
scoped_ptr<int> p(/* ... */);
scoped_ptr ----------
'Scope' İngilizce'de kapsam anlamına gelir. C++ dünyasında bir kod bloğu bir kapsamdır.
scoped_ptr, 'new' ile ayrılan bir bellekte yaşayan ve bir kapsamdan çıkıldığında silinmesi gereken nesneler için kullanılır. Asıl işi, kendisine emanet edilen adresi, kendi yaşamı sonlanırken 'delete' işlecine göndermektir.
'delete'in işi de bir bellekte bulunan nesnenin bozucusunu çağırmak ve o belleği geri vermek olduğu için, scope_ptr kullanarak nesne temizliği hatalarını ve bellek sızıntılarını önlemiş oluruz. Bu "akıllılığının" dışında, sıradan bir gösterge gibi de çalışır. get() öge işlevi yardımıyla yalın gösterge bekleyen işlevlere rahatça gönderilebilir.
scoped_ptr masrafsız ve basit bir sınıf şablonu olarak tasarlanmıştır. Onun için, atama işleci veya kopyalayıcı tanımlamaz.
Öte yandan, iki tane özel öge işlev sunar:
1) T * get() const;
Sorumluluğunda bulunan nesnenin adresini döndürür.
2) void reset(T * p = 0);
Sorumluluğunda bulunan nesneyi silmesi ve yeni bir nesnenin sorumluluğunu üstlenmesi için kullanılır.
Bu bilgileri kullanan bir örnek program şöyle yazılabilir:
/* Bu islevin tanimlamis oldugu kapsam nasil sonlanirsa sonlansin; ornegin islevden
- sonuna gelindigi icin - bir 'return' satiri ile, - atilan bir aykiri durum yuzunden
cikilmasi durumlarinda, 'gosterge'nin tuttugu nesne mutlaka silinecektir.
Bunu, Yapi yapisi icin
- kurucu, - bozucu, - atama isleci ve - kopyalayici
tanimlayarak ve onlara girildiginde ekrana yazilar yazdirarak gorebilirsiniz. */ }
void ikiye_katla(int * p) { *p *= 2; }
/* Bu da 'int' turunu gosteren bir scoped_ptr ornegi */ void int_deneme() { scoped_ptr<int> p(new int); *p = 1000; ikiye_katla(p.get()); cout << *p << ' '; }
int main() { Yapi_deneme(); int_deneme(); }
Aslında bu örnekler scoped_ptr'ın kullanımını göstermek için biraz zorlama oldular. Çünkü örneklerdeki Yapi ve int nesnelerini dinamik bellekten almanın gereği yoktur. Onun yerine otomatik değişken kullanmak daha doğal olacaktır:
Yapi yapi;
ve
int sayi;
gibi... Burada çokşekilliliğin gerektiği bir örnek daha uygun olabilirdi. Aşağıdaki örnekte, hayvanYap işlevinin dinamik bellekten alarak döndürdüğü belleği hemen bir scoped_ptr'a geçirerek o belleğin otomatik olarak geri verilmesini sağlamış oluyoruz.
#include <boost/scoped_ptr.hpp>
using namespace boost;
struct Hayvan { virtual ~Hayvan() {} };
struct Kedi : public Hayvan {}; struct Kopek : public Hayvan {};
Hayvan * hayvanYap() { return new Kedi; }
int main() { scoped_ptr<Hayvan> hayvan(hayvanYap()); }