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.
Standart kütüphaneler bir çok nedenden ötürü kısıtlıdırlar; akla gelen her olanak standartta yer alamaz:
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 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.
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:
#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>
using namespace std;
/*
Bu islev lexical_cast'i tam adiyla kullaniyor
*/
void tamsayi(string const & yaziyla)
{
int sayi = boost::lexical_cast<int>(yaziyla);
cout << (sayi * 2) << '\n';
}
/*
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) << '\n';
}
/*
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 << '\n';
}
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.
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 = /* ... */
'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:
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:
ve
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.
scoped_array, scoped_ptr'ın 'new[]' işleciyle
alınan belleklerle çalışan türüdür. Ondan farkları
şunlardır:
1) Kendisi silinirken 'delete'i değil, 'delete[]'i
çağırır. Onun için, sorumluluğuna verilen
belleğin 'new[]' ile alındığından emin olmak
gerekir.
2) Kolaylıkla dizi gibi kullanılabilsin diye []
işlecini de sunar.
Aslında scoped_array yerine ondan daha becerikli
olan std::vector de kullanılabilir. Ancak, onun
gerekmediği durumlar için çok hızlı ve basit bir
topluluk olarak düşünülebilir.
Şimdi de scoped_array kullanan bir örnek:
Daha önce de değindiğim gibi, scoped_ptr ve
scoped_array tek bir kapsam içinde kullanılmak
üzere tasarlandıkları için, ne kopyalanabilirler
ne de atanabilirler.
Başka bir yazıda da değindiğim gibi, nesnelerin
standart topluluklarla kullanılabilmeleri için
kopyalanabilmeleri gerekir.[3] scoped_ptr ve
scoped_array bu özellikleri sağlamadıkları için
standart topluluk üyesi olarak kullanılamazlar.
Boost kütüphanesinin hem standart topluluklarla
kullanılabilecek hem de sorumlusu olduğu belleği
geri verebilecek akıllı göstergeleri shared_ptr ve
shared_array'dir.
scoped_ptr'ın kopyalanamadığı için standart
topluluklarla kullanılamamasının yanında, birden
fazla scoped_ptr'ın aynı nesneyi göstermesi de
olanaksızdır.
Dahası, scoped_ptr tasarımı gereği, sorumlusu
olduğu nesneyi kendi bulunduğu kapsam sonlanırken
siler.
scoped_ptr'ın karşılayamadığı akıllı gösterge
ihtiyaçları için shared_ptr kullanılır. 'Shared'in
Türkçe karşılığı 'paylaşılan'dır. Aynı nesneyi
gösteren shared_ptr'lar, tek bir nesneyi
gösterirler. Nesnenin silinmesi işinin
sorumluluğunu birlikte paylaşırlar.
Gösterdikleri nesneye ek olarak, o nesneyi toplam
kaç tane shared_ptr'ın gösterdiğini de bilirler.
Her shared_ptr kendisi silinirken, o toplamı bir
eksiltir. Bu eksiltme sırasında o sayı sıfıra
düştüğünde, eksiltmeyi yapan shared_ptr nesneyi de
siler. Yani nesneyi silme işi onu gösteren son
shared_ptr'a kalmıştır.
Benzer olarak, shared_ptr'ların kopyalanması
durumunda nesnenin kendisi kopyalanmaz; onu
gösteren shared_ptr'ların toplamı bir arttırılır.
Bu toplamın nasıl artıp azaldığını gösteren bir
örneği shared_ptr'ın benim bugüne kadar hiç
kullanmak zorunda kalmadığım use_count işlevi ile
göstermek istiyorum:
Bu program benim çalıştığım ortamda şu çıktıyı
verdi:
shared_ptr'ı şimdi de bir standart topluluk içinde
kullanmak istiyorum. Ben shared_ptr'ı zaten en çok
böyle durumlarda kullanıyorum.
Dikkat ederseniz, aşağıdaki program bellek
temizliğiyle hiç ilgilenmiyor bile. O iş
shared_ptr'lar tarafından hallediliyor.
Bu program, çokşekillilik anlatılırken çok
karşılaşılan 'şekil' tanımlama kavramının çok kaba
bir örneği oldu. Başka sınıflar türeterek
geliştirmek isteyebilirsiniz.
Programda benim ilgimi çeken bir nokta,
Cizgi::cizOzel_ islevini özyineleme kullanarak
yazmam oldu. O işlev her seferinde ekrana tek bir
nokta koymasına rağmen, özyineleme nedeniyle bütün
çizgi beliriveriyor.
Programı sırf bu özelliği yüzünden bile incelemek
isteyebilirsiniz.
scoped_ptr ile scoped_array ilişkisinde olduğu
gibi, shared_array'in shared_ptr'dan farkı,
sorumlusu olduğu nesneyi 'delete' yerine
'delete[]' ile silmesidir. Onun için 'new[]' ile
oluşturulan nesnelerin sorumluluğunu üstlenebilir.
Bu şablonu da kullanmaya karar vermeden önce, onun
yerine std::vector veya std::string gibi başka üst
düzey soyutlamaların daha uygun olup olmadığını
araştırmakta yarar vardır.
Küçük bir örnek:
Yukarıdaki programda açıklamalarda da değindiğim
gibi, yapılabiliyorsa std::string türünü kullanmak
genelde daha iyi bir seçimdir.
Bu tür göstergeler, bir veya daha fazla shared_ptr
tarafından sahiplenilen bir nesneyi, sahiplenme
işine karışmadan göstermek için
kullanılırlar. Adındaki 'zayıf' anlamına gelen
'weak', herhalde bu konuda zayıf kaldığı için
kullanılmıştır.
Gözlemci örüntüsünün (observer design pattern) bir
uygulamasıdır. shared_ptr'lar tarafından
gösterilen bir nesnenin silinmesi kararı
alındığında, o nesneyi gösteren weak_ptr'lar
kendiliklerinden sıfır değerini alarak geçersiz
hale gelirler.
Bir weak_ptr'ın, gösterdiği nesneyi gösteren diğer
shared_ptr'larla arasında şöyle bir "gözleme"
ilişkisi olduğunu düşünebilirsiniz: "Sizin
gösterdiğiniz nesne silineceği zaman bana haber
verin, ben de kendimi geçersiz hale getireyim".
Aşağıdaki program, bir nesnenin yaşam sürecinden
sorumlu üç tane shared_ptr'a karşılık bir tane
weak_ptr içeriyor. shared_ptr'lar silindiklerinde
weak_ptr geçersiz hale geliyor.
Dikkat ederseniz weak_ptr kullanan programların
onları kullanmadan önce, geçerli olup
olmadıklarını get() işlevi ile denetlemeleri
gerekir.
shared_ptr, nesneyi gösteren göstergelerin
sayısını dinamik bellekten ayırdığı bir yerde
tutar. Elimizde bu sayıyı tutacak zaten bir sayaç
bulunduğu durumlarda intrusive_ptr türünü
kullanabilir ve ayrıca bir sayaç ayrılmasını
engellemiş oluruz.
Normal olarak genelde shared_ptr kullanıldığı
halde, intrusive_ptr'ı şu durumlarda kullanmak
isteyebilirsiniz:
intrusive_ptr'ı bu yazının konusu dışında
bırakmaya karar verdim. Bir çok nedenim var: Zaten
öncelikle shared_ptr'ı yeğlemek gerekir; Nispeten
yeni olan intrusive_ptr'ı daha önce kendim hiç
kullanmadım; Yazı zaten yeterince uzadı;
boost.org'da yeterli örnekler bulamadım; daha
fazla yazmak istemiyorum :)
Akıllı göstergeler, C++'ın ayrılmaz bir parçası
olan RAII yönteminin dinamik bellek alanları ile
uygulanmalarıdır.
Boost'un akıllı göstergeleri, henüz standartta yer
almasalar da onun bir uzantısı olarak
görülebilirler:
Bu türleri kullanarak programlarımızda bellekleri
açıkça geri verme hatasına düşmekten kurtuluruz.
[1] Akıllı göstergelerle ilgili daha genel bilgi
edinmek için şu yazıdan yararlanabilirsiniz:
http://ceviz.net/index.php?case=article&id=263
[2] Boost'un regexp ve python kütüphanelerinin
baştan derlenmiş olmaları gerekir. Ancak, eğer
onları kullanmaya gerek duymuyorsanız, diğer
başlıkları kodunuza ekleyerek Boost
kütüphanelerini hemen kullanmaya
başlayabilirsiniz.
[3] Standart topluluklarla ilgili genel bilgi
edinmek için şu yazıdan yararlanabilirsiniz:
http://ceviz.net/index.php?case=article&id=184
[4] vector, map ve multi_map toplulukları ile
ilgili yazılara da şu sayfadan erişebilirsiniz:
http://ceviz.net/index.php?case=category&id=34
#include <iostream>
#include <boost/scoped_ptr.hpp>
using namespace std;
using namespace boost;
// Oylesine bir yapi
struct Yapi
{
int i;
double d;
};
// Yapi kullanan oylesine bir islev
void Yapi_yazdir(Yapi const * yapi)
{
cout << "(" << yapi->i
<< ", " << yapi->d << ')';
}
void Yapi_deneme()
{
/*
Bellekte bir Yapi olusturuyor ve onun
sorumlulugunu bir scoped_ptr'a veriyoruz:
*/
scoped_ptr<Yapi> gosterge(new Yapi);
/*
'gosterge'yi bir yalin gosterge gibi
kullanabiliriz. Oge erisim isleci olan -> ile:
*/
gosterge->i = 1;
gosterge->d = 1.1;
/*
Bir Yapi nesnesi adresi bekleyen bir isleve
gonderebiliriz.
Nesnenin adresini 'get' oge islevi ile ediniyoruz:
*/
Yapi_yazdir(gosterge.get());
cout << '\n';
/*
Gosterdigi nesneye * isleciyle erisebiliriz.
(Burada nesneyi anlamsizca kendisine atiyoruz;
ama olsun...)
*/
*gosterge = *gosterge;
/*
O nesneyi birakip yeni bir tanesini tutmasini
istiyoruz
*/
gosterge.reset(new Yapi);
// Simdi rastgele sayilar gorecegiz:
Yapi_yazdir(gosterge.get());
cout << '\n';
/*
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 << '\n';
}
int main()
{
Yapi_deneme();
int_deneme();
}
Yapi yapi;
int sayi;
#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());
}
scoped_array
#include <iostream>
#include <algorithm>
#include <iterator>
#include <boost/scoped_array.hpp>
using namespace std;
using namespace boost;
void deneme()
{
size_t const uzunluk = 10;
scoped_array<double> dizi(new double[uzunluk]);
for (size_t i = 0; i != uzunluk; ++i)
{
// Kolaylikla dizi gibi kullanabiliyoruz
dizi[i] = i * 1.1;
}
// Standart cikisa kopyaliyoruz
copy(dizi.get(), dizi.get() + uzunluk,
ostream_iterator<double>(cout, " "));
cout << '\n';
/*
Burada bellegi geri vermeye gerek yok;
'dizi', kendi yasami sona ererken sorumlusu
oldugu nesneleri de silecek.
*/
}
int main()
{
deneme();
}
shared_ptr
#include <iostream>
#include <boost/shared_ptr.hpp>
using namespace std;
using namespace boost;
class Hayvan
{
/*
Atama ve kopyalamayi yasaklamak icin bunlari
ozel erisim alaninda bildiriyoruz
*/
Hayvan(Hayvan const &);
Hayvan & operator= (Hayvan const &);
public:
Hayvan() { cout << "kuruldu\n"; }
~Hayvan() { cout << "silindi\n"; }
};
typedef shared_ptr<Hayvan> HayvanGostergesi;
void bilgiVer(HayvanGostergesi const & gosterge, char const * baslik)
{
cout << baslik << ": "
<< gosterge.use_count() << '\n';
}
void foo(HayvanGostergesi gosterge)
{
bilgiVer(gosterge, "foo'ya girildi");
HayvanGostergesi gosterge2 = gosterge;
bilgiVer(gosterge, "gosterge kopyalandi");
}
void bar(HayvanGostergesi gosterge)
{
bilgiVer(gosterge, "bar'a girildi");
foo(gosterge);
bilgiVer(gosterge, "foo'dan cikildi");
}
int main()
{
HayvanGostergesi gosterge(new Hayvan);
bilgiVer(gosterge, "bar cagrilacak");
bar(gosterge);
bilgiVer(gosterge, "bar'dan cikildi");
}
kuruldu
bar cagrilacak: 1
bar'a girildi: 2
foo'ya girildi: 3
gosterge kopyalandi: 4
foo'dan cikildi: 2
bar'dan cikildi: 1
silindi
#include <iostream>
#include <vector>
#include <iterator>
#include <boost/shared_ptr.hpp>
#include <math.h>
using namespace std;
using namespace boost;
char const bosYer = ' ';
char const kenar = '|';
char const altUstKenar = '-';
class Kagit;
/*
Bu siniftan tureyen her alt sinifin kendisine
has cizme isini goren cizOzel_ islevini
tanimlamasi gerekir
*/
class Sekil
{
/*
Sekli cizerken kullanilan karakter
*/
char karakter_;
/*
Sekli belirtilen kagida belirtilen
karakterle cizen islev
*/
virtual void cizOzel_(Kagit &, char karakter) const = 0;
public:
explicit Sekil(char karakter)
:
karakter_(karakter)
{}
/*
Sanal islevi olan her sinifin olmasi
gerektigi gibi bunun da bozucu islevini
virtual yapiyoruz
*/
virtual ~Sekil()
{}
void ciz(Kagit & kagit) const
{
cizOzel_(kagit, karakter_);
}
};
class Nokta : public Sekil
{
double satir_;
double sutun_;
virtual void cizOzel_(Kagit &, char) const;
public:
Nokta(double satir, double sutun, char karakter)
:
Sekil(karakter),
satir_(satir),
sutun_(sutun)
{}
/*
Kendisine verilen noktayla arasindaki
noktayi dondurur
*/
Nokta araNokta(Nokta const & diger, char karakter) const
{
return Nokta(fabs((satir_ + diger.satir_) / 2),
fabs((sutun_ + diger.sutun_) / 2),
karakter);
}
/*
Burada yaklasik bir is yapiyoruz. Noktalar
birbirlerine belirli bir miktar yakinda
olduklarinda esit kabul ediliyorlar.
*/
bool operator== (Nokta const & diger) const
{
double const satirFarki = satir_ - diger.satir_;
double const sutunFarki = sutun_ - diger.sutun_;
return sqrt((satirFarki * satirFarki)
+
(sutunFarki * sutunFarki)) < 0.5;
}
};
class Cizgi : public Sekil
{
Nokta bas_;
Nokta son_;
virtual void cizOzel_(Kagit & kagit, char karakter) const
{
/*
Cizgiyi kolay geldigi icin ozyineleme
yonteminden yararlanarak cizmeye karar
verdim. Mutlaka bundan daha iyi
yontemler vardir.
Buradaki amac, yalnizca cizginin iki
ucunun ortasinda kalan noktayi cizmek.
Ondan sonra, cizgiyi o noktadan ikiye
bolunmus gibi dusunerek yeni olusan iki
kisa cizginin de orta noktasini ciziyor
ve bu isi cizginin uzunlugu sifir olana
kadar yineliyoruz.
*/
Nokta const orta = bas_.araNokta(son_, karakter);
orta.ciz(kagit);
if (bas_ == son_) return;
Cizgi(bas_, orta, karakter).ciz(kagit);
Cizgi(orta, son_, karakter).ciz(kagit);
}
friend ostream & operator<< (ostream & os, Cizgi const &);
public:
Cizgi(Nokta const & bas,
Nokta const & son,
char karakter)
:
Sekil(karakter),
bas_(bas),
son_(son)
{}
};
typedef vector<Nokta> Noktalar;
typedef Noktalar::const_iterator NoktaErisici;
class Poligon : public Sekil
{
Noktalar noktalar_;
/*
Burada ardarda gelen her iki noktayi ('bas'
ve 'son') cizgi olarak goruyor ve teker
teker o cizgileri cizdiriyoruz.
*/
virtual void cizOzel_(Kagit & kagit, char karakter) const
{
if (noktalar_.size() < 2) return;
NoktaErisici const ilkNokta = noktalar_.begin();
NoktaErisici bas = ilkNokta;
for (NoktaErisici son = bas + 1;
son != noktalar_.end();
bas = son, ++son)
{
Cizgi const cizgi(*bas, *son, karakter);
cizgi.ciz(kagit);
}
/*
Sonuncu noktayi ilk noktaya bagliyoruz
*/
Cizgi const cizgi(*bas, *ilkNokta, karakter);
cizgi.ciz(kagit);
}
public:
Poligon(Noktalar const & noktalar, char karakter)
:
Sekil(karakter),
noktalar_(noktalar)
{}
};
/*
Sekillerin cizildigi kagidin bir satirini temsil
ediyor
*/
class Satir
{
vector<char> karakterler_;
friend ostream & operator<< (ostream &, Satir const &);
public:
explicit Satir(size_t uzunluk)
:
karakterler_(uzunluk, bosYer)
{}
size_t size() const
{
return karakterler_.size();
}
char & operator[] (size_t hangisi)
{
return karakterler_[hangisi];
}
};
ostream & operator<< (ostream & cikis, Satir const & satir)
{
cikis << kenar;
copy(satir.karakterler_.begin(),
satir.karakterler_.end(),
ostream_iterator<char>(cikis, ""));
cikis << kenar;
return cikis;
}
/*
Sekillerin cizildigi kagidi temsil ediyor
*/
class Kagit
{
vector<Satir> satirlar_;
friend ostream & operator<< (ostream &, Kagit const &);
public:
Kagit(size_t toplamSatir, size_t satirUzunlugu)
:
satirlar_(toplamSatir, Satir(satirUzunlugu))
{}
/*
Kagidin belirli bir noktasini boyama isini
goruyor. Kagidin disinda kalan noktalar
gozardi ediliyorlar.
*/
void boya(size_t satir, size_t sutun, char karakter)
{
if ((satir < satirlar_.size())
&&
(sutun < satirlar_[0].size()))
{
satirlar_[satir][sutun] = karakter;
}
}
};
/*
Bu islev kagidin sinirlarini gostermek icin
kullanilan yatay cizgi ciziyor
*/
void yatayCizgi(ostream & cikis, size_t uzunluk)
{
fill_n(ostream_iterator<char>(cikis, ""),
uzunluk , altUstKenar);
cikis << '\n';
}
ostream & operator<< (ostream & cikis, Kagit const & kagit)
{
size_t const cizgiUzunlugu = kagit.satirlar_[0].size() + 2;
yatayCizgi(cikis, cizgiUzunlugu);
copy(kagit.satirlar_.begin(),
kagit.satirlar_.end(),
ostream_iterator<Satir>(cikis, "\n"));
yatayCizgi(cikis, cizgiUzunlugu);
return cikis;
}
void Nokta::cizOzel_(Kagit & kagit, char karakter) const
{
kagit.boya(static_cast<size_t>(satir_),
static_cast<size_t>(sutun_),
karakter);
}
/*
Sonunda bu yazinin konusuyla dogrudan ilgili
olan noktaya gelebildik.
Bu programda 'Sekil'lerden olusan bir toplulukla
ilgileniyoruz (main'in icindeki 'sekiller'
toplulugu). O toplulugun aslinda ne tur
sekillerden olustuklari ile ilgilenmedigimiz
icin, onlari ancak dinamik bellekte yasayan
nesneler olarak gosterebiliriz.
Dinamik bellekten ayrilan yerlerde yasayan bu
nesnelerin yasam sureclerini belirleyebilmek ve
onlarla isimiz bittiginde otomatik olarak
silinmelerini saglamak icin shared_ptr turunu
kullaniyoruz.
Sinif sablonlarini kullanirken her zaman
yaptigimiz gibi'typedef' yoluyla okunakli adlar
takmak, burada da isimizi kolaylastiriyor.
Programin buradan sonrasinda soyut bir
'SekilGostergesi' turuyle ugrasacak ve onlarin
temizlikleriyle ilgilenmek zorunda kalmayacagiz.
*/
typedef shared_ptr<Sekil> SekilGostergesi;
typedef vector<SekilGostergesi> Sekiller;
typedef Sekiller::const_iterator SekilErisici;
/*
Bu uc islevi oylesine sekiller uretmek icin
kullaniyorum. Siz programi degistirerek baska
sekiller olusturmak isteyebilirsiniz.
*/
SekilGostergesi ucgen(char karakter)
{
Noktalar noktalar;
noktalar.push_back(Nokta(5, 15, karakter));
noktalar.push_back(Nokta(15, 30, karakter));
noktalar.push_back(Nokta(15, 15, karakter));
return SekilGostergesi(new Poligon(noktalar, karakter));
}
SekilGostergesi cizgi(char karakter)
{
Nokta const bas(8, 8, karakter);
Nokta const son(14, 2, karakter);
return SekilGostergesi(new Cizgi(bas, son, karakter));
}
SekilGostergesi nokta(char karakter)
{
return SekilGostergesi(new Nokta(5, 6, karakter));
}
int main()
{
Sekiller sekiller;
sekiller.push_back(nokta('@'));
sekiller.push_back(cizgi('*'));
sekiller.push_back(ucgen('.'));
Kagit kagit(20, 40);
for (size_t i = 0; i != sekiller.size(); ++i)
{
sekiller[i]->ciz(kagit);
}
cout << kagit << '\n';
}
shared_array
#include <iostream>
#include <string>
#include <boost/shared_array.hpp>
#include <ctype.h>
/*
Kullandigimiz bir kutuphanenin 'new[]' ile
aldigini belgeledigi bir bellek alani donduren
soyle bir islevi olsun
Not: Evet, burada std::string dondurmek daha
uygundur. Kutuphanenin herhangi bir nedenden
dolayi oyle yapmadigini varsayalim
*/
char * baskenti(std::string const & /* ulke */)
{
/*
... Burada bir arama yaptigimizi varsayalim
*/
char const baskent[] = "ankara";
char * sonuc = new char[sizeof(baskent)];
strcpy(sonuc, baskent);
return sonuc;
}
typedef boost::shared_array<char> BaskentGostergesi;
BaskentGostergesi basHarfiBuyukBaskent(std::string const & ulke)
{
/*
baskenti islevinin dondurdugu bellegi
'delete[]' ile geri vermek gerektigi icin,
burada shared_ptr degil, shared_array
kullaniyoruz.
Baska islem yapmadan once sorumlulugu
gecirmek, programin guvenligini arttirmis
oluyor.
*/
BaskentGostergesi baskent(baskenti(ulke));
baskent[0] = toupper(baskent[0]);
return baskent;
}
int main()
{
BaskentGostergesi gosterge = basHarfiBuyukBaskent("Turkiye");
std::cout << gosterge.get() << '\n';
}
weak_ptr
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
using namespace boost;
typedef shared_ptr<double> Gosterge;
typedef weak_ptr<double> ZayifGosterge;
/*
Bu islev, bir akilli gostergenin gosterdigi
nesnenin adresini yazdirmak icin kullaniliyor
*/
template <class T>
void adresBildir(T gosterge)
{
cout << static_cast<void *>(gosterge.get()) << '\n';
}
// weak_ptr'i kullanan bir islevimiz olsun
void kullan(ZayifGosterge zayif)
{
cout << "Zayif gostergenin gosterdigi nesnenin adresi: ";
adresBildir(zayif);
/*
weak_ptr'in gosterdigi nesnenin belirli bir
anda hala yasayip yasamadigini weak_ptr::get
islevi ile ogrenebiliriz
*/
if (zayif.get())
{
cout << "Asil nesne hala yasadigi icin kullanabilirim\n";
*zayif = 3.4;
}
else
{
cout << "Asil nesne silinmis; kullanamam!\n";
}
}
int main()
{
/*
Gostergelerden olusacak bir topluluk
olusturalim.
Topluluktaki ilk gostergeyi bellekten
ayirdigim bir nesneyi gosterecek sekilde
olusturuyorum.
Topluluktaki sonraki gostergeleri ise
birinci nesnenin kopyalari olarak ekliyorum.
*/
vector<Gosterge> gostergeler;
gostergeler.push_back(Gosterge(new double(1.2)));
gostergeler.push_back(gostergeler[0]);
gostergeler.push_back(gostergeler[0]);
cout << "Topluluktaki " << gostergeler.size()
<< " gostergenin gosterdikleri adresler sunlar:\n";
for_each(gostergeler.begin(),
gostergeler.end(),
adresBildir<Gosterge>);
/*
Simdi de onlarin gosterdigi nesneyi gosteren
bir de weak_ptr olusturuyoruz.
*/
ZayifGosterge zayif(gostergeler[0]);
kullan(zayif);
cout << "Toplulugu bosaltiyoruz...\n";
gostergeler.clear();
kullan(zayif);
}
intrusive_ptr
Özet
Referanslar
Ali Çehreli - acehreli@yahoo.com