Bu sayfadaki yazılar
Sayfanın metni
Sayfa 38
Bu noktalara dikkat edilerek kullanıldıklarında auto ptr'ler özellikle üretici/tüketici ilişkileri olan sınıflar için çok zarif ve güvenli kod yazılmasını sağlarlar.
Shared ptr
Akıllı işaretçiler arasında en güçlü özelliklere sahip olan boost::shared ptr<>'den bahsetmeye geldi sıra. Bu işaretçi sayesinde en zorlu bellek yönetim ve nesne ömrü problemlerini çözebiliyoruz.
shared_ptr, daha önce bahsettiğimiz scoped_ptr ve auto_ptr'ye temelde benziyor. Yine amacı bir nesneye işaret etmek ve doğru zamanda nesneyi silmek. Ancak diğer iki akıllı işaretçide olan bir nesneye yalnız bir işaretçinin “sahip” olması zorunluluğu yok. Aksine shared_ptr'leri kullanarak bir nesneyi birden fazla işaretçinin sahipliğine veya paylaşımına verebiliyoruz. Başka bir deyişle bir grup shared_ptr aynı nesneyi paylaşıyor ve paylaşan bütün işaretçiler ortadan kalktığında, nesne de siliniyor. Bu paylaşım kopyalama/atama yoluyla oluyor.
boost::shared ptr<T> Yonetici::YeniTYarat () | boost::shared ptr<T> pl1l( new T() ); this->mKayitListesi.push back(l pl ); return pl; ) void App::hede() boost::shared ptr<T> p
// p ile birseyler yap
Mesela bu örnekte önce YeniTYarat metodunun içinde p1 işaretçisi objeye bakan ilk işaretçi oluyor. Ardından p1'in bir kopyası bir vector kabına atılıyor. Yani artık iki sahip var. Biri p1, diğeri de vector'ün içindeki işaretçi. Sonra metoddan dönülürken, p1 geçici bir değişkene atanıyor (3. sahip ortaya çıkıyor) ve hemen ardından p1'in ömrü doluyor (kapsam sonuna gelindiği için). Bunun ardından hede metodunun kapsamı içinde isimsiz geçici işaretçi, p işaretçisine atanıyor (bu noktada geçici isimsiz işaretçinin ömrünün dolduğunu varsayabiliriz). Artık bu anda yine iki sahip var. Biri hede kapsamındaki p, diğeri mYönetici nesnesinin içindeki vektörde bulunan işaretçi. Hede metodundan çıkılırken de p yok oluyor ve tek sahip kalıyor. Eğer programın ilerleyen aşamalarında mYönetici nesnesi herhangi bir sebeple silinirse (ve en başta yarattığımız T nesnesine bakan başka shared_ptr yaratılmadıysa), o esnada vektörün içindeki shared_ptr'nin yıkıcı metodu T nesnesini silecek.
Görüldüğü gibi, bu şekilde shared_ptr'ler kullanarak nesnelerin sahipliğini istediğiniz kadar çok sayıda yere bölüştürebilir ve hiçbir zaman elinizdeki hiçbir shared_ptr'nin sallantıya düşmeyeceğinden (silinmiş bir nesneye bakmayacağından) emin olabilir- siniz.
shared_ptr'ler yukarıdaki örnekte görebileceğiniz gibi STL kapları ile kullanılmaya uygundurlar. Hatta karşılaştırma operatörleri de tanımlı olduğu için, ilişkisel kaplarda (associative container) yani std::map ve std::set gibi kaplarda da kullanılabilirler.
shared_ptr kullanırken dikkat etmeniz gereken iki kritik nokta var.
Birincisi birden fazla argüman alan metodlara shared_ptr geçirirken, her zaman isimli bir shared_ptr kullanmak. Mesela elimizde şöyle iki metod olsun.
int 9(); void f( boost::shared ptr<T>,
int )/;
Daha sonra bunları şöyle kullandığımızı düşünelim.
boost::shared ptr<T> p(l( new T() £( Pp, 90
Vi
Bu kullanım yukarıda bahsettiğimiz kurala uyuyor. Aynı kodu aşağıdaki gibi yazmamız ise sakıncalı:
£( boost::shared ptr<T>(new T() ), 9() );
Buradaki problem şu: standardı bu şekilde yazılan bir kodda new T(), g() ve shared_ptr yapıcısının hangi sırada çağırılacağını tanımlamamıştır. Yani bu kod çalışırken, bu bahsettiğimiz üç parça herhangi bir sırada çalışabilir. Dolayısıyla eğer önce new çalışır, ardından g() çalışır ve en son shared_ptr yapıcısı çalışırsa sızıntı tehlikesi vardır. Bu senaryoda eğer g() istisna tetiklerse henüz shared_ptr yaratılmadığı ve kapsamdan çıkılırken yıkıcısı çağırılmayacağı için, new ile alınan T nesnesi sızdırılmış olur.
Bu uyarıyı tam anlamamış olabilirsiniz. İstisna güvenliği konusu derin bir konu ve o konuda daha sonra yazacağım. Ancak siz anlamadıysanız bile, birden fazla argüman alan metodlara shared_ptr geçirirken bu şekilde satır tasarrufu yapmaya çalışmayın.
İkinci uyarı noktası ise nesneler arası döngüsel shared_ptr kullanım problemi. Mesela T cinsinden iki nesnemiz olsun. t1 ve t2 diyelim. T sınıfının üye değişkenlerinden biri de shared_ptr<T> tipine sahip olsun.
elass Tİ public: ptr<T> p; Vi void hede() boost::shared ptr<T> t1( new T() ); boost::shared ptr<T> t2( new T() ); tl1->p t2->p
t2; El;
Burada bir şekilde kodda t1 ve t2 nesneleri birbirlerine bakan bir shared_ptr'ye sahip olmuş durumda. Yani bir işaretçi döngüsü oluşmuş. Bu durumda bir problem oluyor. Dikkat ederseniz birbirine bakan böyle iki nesne olduğu zaman, dışarıdan bu nesnelere bakan bütün shared_ptr'ler silinse bile, bu iki nesnenin içlerindeki shared_ptr'ler kaldığı sürece nesneler silinmiyor. Başka bir deyişle iki nesne birbirini hayatta tutuyor. Örneğin yukarıdaki kodda hede metodundan çıkıldığında t1 ve t2 işaretçileri yok olacak ve bizim elimizde bu iki nesneye bakan bir işaretçi kalmayacak. Bu da bir nevi sızıntı.
Klavyeyle gezinmek için ← ve → tuşları.
Sayfa 39
İşte shared_ptr'ler böyle döngüsel kullanıma uygun olmadıkları için bu durumlarda döngüleri kırmak için weak_ptr adı verilen boost işaretçilerini kullanıyoruz.
Weak_ptr
Weak pointer ile bir veya birkaç shared_ptr'nin baktığı bir nesneye bakan bir işaretçi elde edebiliyoruz. Fakat bu işaretçi shared_ptr'lerin referans sayacını artırmıyor. Örneğin:
void (| boost::shared ptr<int> plnew int(5)); boost::weak ptr<int> d(p);
)
Burada g adlı weak_ptr yaratıldığı anda hala p işaretçisi nesnenin tek sahibi. Eğer p'nin kapsamından çıkarsak, p yok olduğu anda nesne de silinecek.
Peki weak_ptr'nin normal bir işaretçiden ne farkı var o zaman? Farkı şu, weak_ptr, normal işaretçiler veya shared_ptr gibi -> operatörünü desteklemiyor. Yani şöyle bir kod yazamıyoruz.
g'nun gösterdiği nesneyi kullanmanın tek yolu var o da g'dan geçici bir shared_ptr yaratmak.
boost::shared ptr<int> r - g.lock(); r>>hodol);
Eğer bu anda p ölmüş ve nesne yok olmuş ise lock() metodu null döndürecek. Dolayısıyla weak_ptr böylelikle aynı nesneye bakan işaretçilerden biri delete ile nesneyi sildiğinde diğer işaretçilerin anlamsız bir adrese bakmaları problemini (“dangling pointer” diye de bilinir) çözmüş oluyor. Aynı zamanda yukarıda bahsettiğimiz, shared_ptr'ler ile oluşan döngüsel sızıntı problemini de çözüyor.
Sonuç
Akıllı işaretçiler doğru kullanmayı öğrendiğinizde çok işinize yarayabilecek araçlardır. Bol bol kurcalamanız tavsiye olunur.