471
C/C++ Kütüphaneler Selami KOÇLU

C++ 2 (Kitap - Selami KOÇLU)

Embed Size (px)

Citation preview

Page 1: C++ 2 (Kitap - Selami KOÇLU)

C/C++Kütüphaneler

Selami KOÇLU

Page 2: C++ 2 (Kitap - Selami KOÇLU)

1.BÖLÜM

Standart C++ kütüphanesi

Standart C++ kütüphanesi standart C kütüphanesinin tamamını içerdiğigibi, küçük ekleme ve değişikliklerle tip güvenliğini destekleyen kendineözgü kütüphaneyi de kapsar. Bu yüzden Standart C++ kütüphanesistandart C kütüphanesinden daha güçlüdür. Kitabımızda Standart C++kütüphanesini ayrıntıları ile inceleyeceğiz. Aslında Standart C++ için eniyi kaynak bizzat standardın kendisidir.Şimdi gelelim kitabımızda ele alacağımız konulara; birinci bölümde

Page 3: C++ 2 (Kitap - Selami KOÇLU)

Standart C++ string sınıfını ele alacağız. string sınıfı, metin süreçlerinigeliştirirken kullanılır. Kelime işlemlerinde string'e yoğun olarak rastlanır.C kodları ile gerçekleştirilen metin işlemleri, C++ da, string sınıfının üyeişlevleri tarafından da rahatlıkla yapılabilir. string sınıfının üye işlevleri; append( ), assign( ), insert( ), remove( ), resize( ), replace( ), copy(),find( ), rfind( ), find_first_of( ), find_last_of( ), find_first_not_of( ),find_last_not_of( ), substr( ), compare( ), at( ), capacity( ), empty( ),erase( ), length( ), max_size( ), size( ), ve swap( ) dir. Bunlardan başka atama(=), toplama (+=) ve köşeli ayraç işleçleri([]) debindirime uğratılarak işlemler gerçeklenebilir. Ayrıca uluslararasıkarakterleri desteklemek için "geniş" bir wstring sınıfı tasarlanmıştır. Hemstring hem de wstring (<string> biçiminde bildirilirler, C deki <string.h>ile karıştırmayın, bu C++ da <cstring> olarak kullanılır), basic_stringortak kalıp sınıfından oluşturulmuştur. Hemen bir konuyu belirtelim;string sınıfı iostream'lerle etle tırnak gibi tümleşiktir ve bu yüzdenstrstream kullanılmaz.Bir sonraki bölümde iostream kütüphanesi yer almaktadır. Tanı kütüphanesi. Buradaki öğeler sayesinde C++ programları hatalarıalgılar ve raporlar. <exception> başlığı, standart istisna sınıflarını bildirirve, <cassert> ise C dilindeki assert.h ile aynı işi görür. Genel yarara dönük kütüphane. Buradaki öğeler Standart C++kütüphanesinin diğer kısımlarıdır, ama isterseniz programlarınızdakullanabilirsiniz. !=, <=, >=, > işleçlerinin kalıp sürümleri bulunur(kısaltılmış tanımları engellemek için), tuple yapan kalıp işlevinibulunduran pair kalıp sınıfı, Standart Kalıp İşlevlerini (STL) desteklemekiçin işlev nesneleri kümesi, Standart Kalıp İşlevleri ile kullanmak için,saklama yeri yerleşim işlevleri. Bu sayede belleğe yerleşimmekanizmalarını kolaylıkla değiştirebilirsiniz.Yöreleştirme kütüphanesi. Bu kütüphaneleri kullanarak programlarınızıfarklı ülke koşullarına (alfabe, para,sayı, zaman v.d.) uyarlayabilirsiniz.Kap kütüphanesi. Standart Kalıp Kütüphanesinin içerdiği gibi, <bits> ve<bitstring> teki bits ve bitstring sınıflarını da kapsarYineleyici kütüphane. Standart Kalıp Kütüphane araçları ile beraber akışve akış tamponları yer alır.Algoritmalar kütüphanesi. Bunlar SKK (standart kalıp kütüphanesi)kaplarında yineleyicileri kullanarak işlem gerçekleştiren kalıp işlevleridir. Standart C++ kütüphanesi hedef kodların (.o uzantılı) programımızdakullanılmasına yardım etmektedir. SKK ise doğrudan kaynak kodlarıkullandırmaktadır. Bundan dolayı kalıplar üzerine ayrı bir bölüm açıp

Page 4: C++ 2 (Kitap - Selami KOÇLU)

konuyu ayrıntıları ile irdeleyeceğiz. Zira kaynak kodları (değiştirerekteolsa) doğrudan kullanabilmek, aynı tasarım düzenekleri (design patterns)gibi programcılıkta verimliliği üst düzeye çıkarır. Bunlardan başka kalıplarile üst programlama da yapılabilmektedir. Üst programlama C++ da (Cdilinde yok), derleyicinin yorumlayıcı gibi kullanıldığı bir uygulamadır.Biraz zor bir konu gibi görünse de, sağladığı olanaklar inanılacak gibideğildir. Üst program; programı programlayan program olup kod işlemler,uygulamada C++ yeni bir dil gibi davranır. Aslında bu konu bir kitapkaplayacak kadar girift bir mevzudur. Buradan da anlayacağınız gibi C++dili ile kalıplı (SKK yolu ile), nesnel yönelim ve yapısal (C dili yolu ile)paradigmaları uygulayabildiğimiz gibi, derleyici sayesinde üstprogramlama da yapılabilmektedir. Bu da C++ dilinin, kullanmayıbildikten sonra rakipsiz olduğunu göstermektedir. Ama hiçbir zamanunutulmaması gereken; ışık vermek için yanmalı. Tabii siz.

Page 5: C++ 2 (Kitap - Selami KOÇLU)

String'lerstring C dilinde üzerinde en fazla vakit harcanan ve yanlış anlamalara yolaçan konulardan biridir. C string kütüphane işlevleri, ünlü kelime işlem

Page 6: C++ 2 (Kitap - Selami KOÇLU)

programlarının vazgeçilmezidirler. C string işlevlerini C++ da kullanmakiçin #include <cstring> yazılır. C++ kütüphanesinin string sınıfını C++da kullanmak için ise #include <string> yazılır. C++ da kullanılan stringsınıfının çok sayıdaki yapıcı işlevi her türlü kelime işlem sorununuçözebilir. Gerek C dili string işlevleri gerekse C++ dilinin string sınıfıyapıcı işlevlerini ayrıntıları ile inceleyeceğiz.Önce string nedir onu açıklayalım; string C dilinde son öğesi sıfır (sıfırsonlandırıcı olarak adlandırılır) olan karakter dizisidir. Karakter dizileriharf ve rakamlardan oluşur. C stringleri ile C++ stringleri arasında iki farkvardır; birincisi, karakter dizisi olan C++ string nesneleri, yönetmek veişlemlemek için gereken işlevlere sahip string sınıfı tarafındanoluşturulurlar. Bir string, verilerin saklama alanı ve boyutu hakkında dabilgiler taşır. Özellikle, C++ string nesnesi bellekteki başlangıç yerini,içeriğini, karakter uzunluğunu, ve arttırılabilecek uzunluğu bilir. Bu daikinci farkı ortaya çıkarır; C++ string nesneleri C string karakter dizilerigibi sıfır öğe ile sonlanmaz. Genellikle açıklamalarda; C char dizi, C++string nesne kullanılır. C++ string nesneleri, C char dizilerindenkaynaklanan hataları en aza indirir. C stringlerinde rastlanan en berbathatalar; dizi üzerine yeniden yazma, diziye doğru değer atanmamış veyailk değer ataması (başlatması) yapılmamış göstergeçle erişmeye çalışma,bir dizi bellekten silinirken göstergeçin buraya o sırada yeniden yazımyapması (sallantıdaki göstergeç). Böyle C hataları, sistemi çökertirler.C++ dilinde string sınıfının bellek kullanım düzeni tam olarak bilinemez.Bu daha ziyade derleyici üreticisinin yaklaşımına bağlıdır. Yani bir stringnesne verilerinin nerede hangi koşullarda tutulduğu hususunda, öncedentam bilgi sahibi olunamaz. C dilinde string işlevlerini kullanmak için #include <string.h>, karakterişlevlerini kullanmak için #include <ctype.h>başlıkta yazılır. C++ dilinde, C dilinin bu işlevlerini kullanmak için#include <cstring> ve #include <cctype> başlıkları yazılır. Anlaşılacağıüzere bunlar C dilinden gelen işlevlerdir. Siz C++ programcısı olarakbunları da kullanabilirsiniz. Ama bizim önerimiz C++ dilinin kendistandart kütüphanesinde bulunan string sınıf işlevlerinin kullanılmasıdır.C++ string sınıf işlevlerini kullanmak için başlıkta #include <string>yazmak yeterlidir. (C++ string sınıf işlevleri, C dilinde kullanılamazhatırlatalım).Şimdi C++ da kullanılabilecek C string ve karakter işlevlerini verelim;

int isalnum(int ch) ; cctype

Page 7: C++ 2 (Kitap - Selami KOÇLU)

int isalpha(int ch) ; cctypeint isblank(int ch) ; cctypeint iscntrl(int ch) ; cctypeint isdigit(int ch) ; cctypeint isgraph(int ch) ; cctypeint islower(int ch) ; cctypeint isprint(int ch) ; cctypeint ispunct(int ch) ; cctypeint isspace(int ch) ; cctypeint isupper(int ch) ; cctypeint isxdigit(int ch) ; cctypevoid* memchr(const void* buffer, int ch, size_t count) ; cstringint memcmp(const void* buf1, const void* buf2, size_t count) ; cstringvoid* memcpy(void* to, const void* from, size_t count) ; cstringvoid* memmove(void* to, const void* from, size_t count) ; cstringvoid* memset(void* buf, int ch, size_t count) ; cstringchar* strcat(char* str1, const char* str2) ; cstringchar* strchr(const char* str, int ch) ; cstringint strcmp(const char* str1, const char* str2) ; cstringint strcoll(const char* str1, const char* str2) ; cstringchar* strcpy(char* str1, const char* str2) ; cstringsize_t strcspn(const char* str1, const char* str2) ; cstringchar* strerror(int errnum) ; cstringsize_t strlen(const char* str) ; cstringchar* strncat(char* str1, const char* str2, size_t count) ; cstringint strncmp(const char* str1, const char* str2, size_t count) ; cstringchar* strncpy(char* str1, const char* str2, size_t count) ; cstringchar* strpbrk(const char* str1, const char* str2) ; cstringchar* strrchr(const char* str, int ch) ; cstringsize_t strspn(const char* str1, const char* str2) ; cstringchar* strstr(const char* str1, const char* str2) ; cstringchar* strtok(char* str1, const char* str2) ; cstringsize_t strxfrm(char* str1, const char* str2, size_t count) ; cstringint tolower(int ch) ; cctypeint toupper(int ch) ; cctype

Burada cstring ve cctype açıklamalarını, ilgili işlevi C++ dilindekullanırken, yazılacak başlık biçimini hatırlatmak için verdik. Örnek;

Page 8: C++ 2 (Kitap - Selami KOÇLU)

#include <cstring>#include <cctype>

İşlevlerin kullanım ayrıntılarını, C standartlarının bulunduğu herhangi birkitapta bulabilirsiniz. Aslında yukarıdaki yazım biçimlerine bakarak birçoközelliği siz kendiniz de keşfedebilirsiniz. Biraz gayret.Burada bir konuyu yeri gelmişken belirtelim cstringte yer alan memcpy,memset gibi bellek yerleşim işlevleri bazen C++ daki new işleci yerinetercih edilebilmektedir. O nedenle bu tür bellek yerleşim işlevlerininayrıntılarını öğrenmeniz sizin yararınızadır. Burada C ile ilgili daha fazlaayrıntı vermememizin nedeni ise asıl hedefimizin C++ dili olmasıdır.

C++ standart kütüphane string'leri

Şimdi bu bölümde basic_string< > temel kalıp kütüphanesi ve onunözelleşmiş standardı string ile wstring'i inceleyeceğiz. Bilindiği gibi birsınıfı kullanabilmek için, o sınıfın public arayüzünü iyi bilmek gerekir.String sınıfının oldukça geniş yöntemler kümesi bulunmaktadır. Bunlarıniçinde çok sayıda yapıcı işlev, string atamaları için bindirilmiş işleçler,string birleştiricileri, karşılaştırıcıları ve her öğeye ayrı ayrı erişebilmeyöntemleri ve daha birçokları sayılabilir. Kısaca string sınıfı bir sürü işyapar.

string nesnesi oluşturmak

Önce string sınıfı yapıcı işlevlerini inceleyelim. Daha sonra, sınıfınnesnesi oluşturulurken en önemli şeyi dikkate alalım; sınıfla ilgiliseçenekler. String sınıfına ait 6 adet yapıcı işlev aşağıda ayrıntıları ileverilmiştir.

String sınıfının yapıcı işlevleri

Yapıcı işlev Açıklamalar 1-string(const char* s) string nesnesini SBSS s göstergeçi ile başlatır

Page 9: C++ 2 (Kitap - Selami KOÇLU)

2-string(size_type n, char c) n tane öğesi olan string nesnesi oluşturur, herbiri c karakteri ile başlar.3-string(const string& str, string string nesnesini str nesnesi ile baş-size_type pos=0, size_type n=npos) latır, str deki pos dan başlayarak str nin sonuna kadar gider veya n tane karakter kullanır. (hangisi önce ise)4-string( ) 0 büyüklüğündeki varsayılan string nesnesini oluşturur5-string(const char* s, size_type n) string nesnesini s nin gösterdiği SBSS ile başlatır, bu SBSS boyutu aşılsa bile sürer6-template<class Iter> string nesnesini [begin, end) arasıstring(Iter begin, Iter end) değerlerle başlatır. begin ve end göstergeç gibi davranır, yer belirler. Değerlere begin dahil ama end değil.

Not; SBSS= sıfır bayt sonlanmalı string, yani C-string demektir.

Bunlardan başka += bindirilmiş işleci stringleri birbirinin arkasına ekler,bindirilmiş = işleci bir stringi diğerine atar, bindirilmiş << işleci bir stringnesnesini göstertir, ve [] bindirilmiş işleci stringin herhangi bir üyesineerişimi sağlar. Şimdi yukarıda anlatılan yapıcı işlevler ve işleçlerinkullanılmasını örnekle gösterelim;

//:B01:string1.cpp//string sınıfına giriş#include <iostream>#include <string>//yapıcı işlevlerin kullanımıusing namespace std ;int main( ){ string bir(“Piyango!”) ; //1 numaralı yapıcı işlev cout<<bir<<endl ; //bindirime uğramış << string iki(10, '$') ; //2. yapıcı işlev kullanımı cout<<iki<<endl ; string uc(bir) ; //3. yapıcı işlev kullanımı cout<<uc<<endl ; bir+=”Vayy!” ; //+= bindirimi

Page 10: C++ 2 (Kitap - Selami KOÇLU)

cout<<bir<<endl ; iki=”Kusura bakma!” ; uc[0]='S' ; string dort ; //4. yapıcı işlev kullanımı dort=iki+uc ; //+ ve = bindirilmiş cout<<dort<<endl ; char hepsi[ ]=”iyi biten hersey iyidir” ; string bes(hepsi, 16) ; //5. yapıcı işlev kullanımı cout<<bes<<”!\n” ; string alti(hepsi+17, hepsi+20) ; //6. yapıcı işlev kullanımı cout<<alti<<”,” ; string yedi(&bes[17], &bes[20]) ; //yine 6. yapıcı işlev cout<<yedi<<”...\n” ;}///:~

Programın çıktısı aşağıdaki gibidir;

Piyango$$$$$$$$$$Piyango!Piyango! Vayy!Kusura bakma! Siyango!iyi biten herseyiyi, iyi

string ilk değer atama (=başlatma) sınırlamaları

stringler tek karakter, ASCII veya integer bir değerle başlatılamazlar.Örnek;

string str1(“a”) ; //olamaz, hata zira tek karakter string str2(0x40) ; //olamaz, hata zira integer

Bu başlatma koşulu atama ve kopya yapıcı işlevle yapılan ilk değeratamaları içinde geçerlidir. string sınıf girdileri

string sınıfı ile ilgili başka önemli bir konuda, girdilerle ilgili

Page 11: C++ 2 (Kitap - Selami KOÇLU)

seçeneklerdir. C biçimi string çağrımları için üç seçenek bulunur;

char bilgi[100] ;cin>>bilgi ; //bir kelime okucin.getline(bilgi, 100) ; //bir satır oku, gözardı et\ncin.get(bilgi, 100) ; //bir satır oku, kuyrukta bırak\n

string nesnesinin girdi seçenekleri neler?. String sınıfı önce >> işlecinibindirime uğratır. İlk nesne (cin) string nesnesi olmadığı için, >> işlecistring sınıfı yöntemi değildir. Onun yerine, istream nesnesini (cin) ilkdeğişkeni olarak, string nesnesini de ikinci değişkeni olarak alan genel birişlevdir. Bundan başka, C deki >> işlecinin stringlerle kullanımında tutarlıolmak adına, C++ stringleri de; tek kelime okur, boşluğa rastladığındagirişi sonlandırır, dosya sonunu algılar, veya string nesnesine saklanabilenen fazla sayıdaki karaktere ulaşabilir. İşlev öncelikle hedef stringiniçeriğini siler ve daha sonra her seferinde bir karakter olmak üzere okur veyazar. String nesnesine izin verilen karakter sayısından daha az sayıdagiriş yapılırsa, operator>>(istream&, string&) işlevi string nesnesininboyutunu otomatik olarak girişe uydurur. Özet olarak şunu söyleyebiliriz;>> işlecini C biçiminde olduğu gibi C++ da da aynen kullanabiliriz. Ayrıcadizi boyutunun aşılacağı hususunda korkmamız da gerekmez.

char dosya_ad[10] ;cin>>dosya_ad ; //9 karakterden büyük olduğunda sorun çıkar string lad ;cin>>lad ; //çok çok uzun kelimeleri okuyabilir

getline( ) işleçle gösterilemediği için getline( ) işlevinin eşdeğerinikoymak (operator>>( ) işlevinden farklı) biraz daha az şeffaftır, onunyerine üyelik gösterimi kullanılır.

cin.getline(dosya_ad, 10) ;

String nesnesini benzer biçimde yazabilmek için de istream sınıfına yenibir üye işlev eklenmesi gerekir. Tabii bu akıllıca olmazdı. Onun yerinestring kütüphanesinde üye olmayan getline( ) işlevi tanımlanır. Bu işlevilk değişken olarak istream nesnesi, ikinci değişken olarakta stringnesnesi alır. Böylece girişten string nesnesine bir satır okumak için ;

Page 12: C++ 2 (Kitap - Selami KOÇLU)

string tam_ad ;getline(cin, tam_ad) ; //cin.getline(dosya_ad, 10) yerine

Burada getline( ) işlevi önce varış stringini siler daha sonra girişsırasından her seferinde bir karakter okur ve bunu stringe ekler. Bu işlem,işlev satır sonuna gelene kadar, dosya sonu algılanana kadar, veya stringnesnenin kapasite sınırına kadar sürer. Eğer yeni satır (\n) karakterialgılanırsa, bu karakter okunur fakat string nesnesine eklenmez. Buradahemen bir şeyi belirtelim; istream sürümüne benzemeyen biçimde stringsürümde, okunacak karakterlerin maksimum sayısını veren boyutdeğişkeni bulunmaz. Bunun nedeni; string nesnesinin boyutu otomatikolarak ayarlamasıdır. Aşağıdaki örnek iki türlü giriş seçeneğinigöstermektedir;

//:B02: String2.cpp//string girişleri#include <iostream>#include <string>using namespace std ;int main( ){ string kelime ; cout<<”Bir satir gir: ” ; cin>>kelime ; while(cin.get( )!='\n') continue cout<<kelime<<”hepsi istenilenmi. \n” ; string satir ; cout<<”tam bir satir gir:” ; getline(cin, satir) ; cout<<”Satir: “<<satir<<endl ;}///:~

Bu program parçasının çıktısı ;

Bir satir gir: Paraların tamamı onunduParaların hepsi istenilenmitam bir satir gir: bu örnek programı herkes begenirdi degilmiSatir: bu örnek programı herkes begenirdi degilmi

Page 13: C++ 2 (Kitap - Selami KOÇLU)

Stringlerle öbür işlemler

Buraya kadar string nesnelerini oluşturmanın değişik yollarını öğrendik.String nesnesinin içeriğini göstermek, verileri string nesnesine okumak,string nesnesine ekleme yapmak, string nesnesine atama yapmak, vestring nesnelerini birbirine eklemek bunların arasındadır. Başka neleryapılabilir?.Stringleri karşılaştırabilirsiniz. Altı tane olan ilişkinlik işleçlerinin tamamı,string nesnelerince bindirime uğratılır. Bir string nesnesi diğerindenmakine metin karşılaştırmasında daha önce yer alırsa, ilki diğerinden dahaküçük kabul edilir. Makine metin karşılaştırma sırası ASCII kod ise,bunun anlamı; sayılar büyük karakterlerden daha küçük, büyükkarakterlerde küçük karakterlerden küçük demektir. Her ilişkinlik işleci üçtürlü bindirime uğrayabilir; ya bir string nesnesini başka bir string nesnesiile karşılaştırırsınız, ya bir string nesnesini C benzeri string ilekarşılaştırırsınız, ya da C benzeri stringi bir string nesnesi ilekarşılaştırırsınız.

string snake1(“cobra”) ;string snake2(“coral”) ;char snake3[20]=”anaconda” ;if(snake1<snake2) ; //operator<(const string&, const string&)..... if(snake1==snake3) ; //operator==(const string&, const char*) .....if(snake3!=snake2) ; //operator!=(const char*, const string&)

String büyüklüğü de saptanabilir. Stringte bulunan karakterlerin sayısı size( ) ve length( ) üye işlevleri bunu gerçekler.

if(snake1.length( )==snake2.size( )) cout<<”her iki string aynı boyda”<<endl ;

Gerek size( ) gerekse length( ) işlevleri aynı görevi yapmasına rağmenneden ikisi de var. Bunun nedeni; length( ) işlevinin string sınıfının ilksürümlerinde bulunması, size( ) işlevinin standart kalıp kütüphanesiuyumluluğu sağlamak için oluşturulmasıdır.Bir string içinde alt stringler veya karakterleri değişik yollarlaarayabilirsiniz. find( ) yönteminin dört değişik kullanım yani bindirim yolu

Page 14: C++ 2 (Kitap - Selami KOÇLU)

aşağıda gösterildi;

find( ) işlevinin bindirilmiş hali

Yöntem öntipi Açıklaması

size_type find(const string& str, Birinci str altstringini pos nok-size_type pos=0)const tasından aramaya başlayarak bulur.Bulduğu zaman altstring ilk karakter sırasını geri döndürür, aksi taktirde string::npos geri döner.

size_type find(const char* s, Birinci s altstringini pos nokta- size_type pos=0)const sından aramaya başlayarak bu- lur. Bulduğu zaman altstring ilk karakter sırasını geri dön- dürür, aksi taktirde string::npos geri döner.

size_type find(const char* s, Birinci s altstringinin ilk n ka-size_type pos=0, size_type n)const rakterini pos noktasından ara- maya başlayarak bulur. Bulduğunda altstring ilk ka- rakter sırasını geri döndürür. Aksi taktirde string::npos geri döner.

size_type find(char ch, Birinci ch karakterini pos nokta-size_type pos=0)const sından aramaya başlayarak bu- lur. Bulduğunda karakter sırası geri döndürülür. Aksi durumda string::npos geri döner.

Kütüphanede bunlardan başka, bindirilmiş find( ) yönteminin aynıkünyede (ayraçlar arasındakiler) benzer yöntemler bulunur; rfind( ),find_first_of( ), find_last_of( ), find_first_not_of( ), find_last_not_of( ).rfind( ) yöntemi altstring veya karakterin son kez oluşumunu bulur.

Page 15: C++ 2 (Kitap - Selami KOÇLU)

find_first_of( ) değişkende yer alan karakterlerden herhangi birininstringte ilk kez bulunduğu sırayı verir. Örnek;

int nerede=snake1.find_first_of(“mark”) ;

“cobra” da yer alan “r” harfinin sırasını bulur.(“cobra” nın üçüncü harfi“r”). Bunun nedeni “mark” karakterlerinden “cobra” da ilk rastlanankarakterin “r” olmasıdır. find_last_of( ) yöntemi de benzer fakat tersbiçimde çalışır. Yani sonuncu benzer karakterin olduğu sırayı verir.

int nerede=snake1.find_last_of(“mark”) ;

“mark” ın “cobra” da bulunan son karakteri “a” dır. “a” karakteri“cobra” nın dördüncü karakteridir ve yöntem bu sıra değerini geridöndürür. find_first_not_of( ) yöntemi ise değişkende olmayan stringinilk karakterini verir.

int nerede=snake1.find_first_not_of(“mark”) ;

“cobra” da “mark” ta bulunmayan ilk karakter “c” dir. “c” nin “cobra”daki yeri birinci sıradır, yani bir (1) döndürülür. find_last_not_of( )yöntemi ise değişkende olmayan stringin son karakter yerini verir.

int nerede=snake1.find_last_not_of(“mark”) ;

“cobra” da “mark” ta bulunmayan son karakter “b” dir. “b” nin “cobra”daki yeri üçüncü sıradır, yani üç (3) geri döndürür.

Daha çok sayıda yöntem bulunmaktadır, string yöntemlerinin adlarıkitabın başlangıcında ayrıntılara girmeden verilmişti. Bazılarınınayrıntılarını verelim;

//string işlemleri:

const charT* c_str( ) const;const charT* data( ) const;allocator_type get_allocator( ) const ;

//string karşılaştırmaları:

Page 16: C++ 2 (Kitap - Selami KOÇLU)

int compare(const basic_string& str) const;int compare(size_type pos1, size_type n1, const basic_string& str)const;int compare(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2) const;int compare(const charT* s) const;int compare(size_type pos1, size_type n1, const charT* s) const;

Page 17: C++ 2 (Kitap - Selami KOÇLU)

int compare(size_type pos1, size_type n1, const charT* s, size_type n2)const;basic_string& append(const basic_string& str);basic_string& append(const basic_string& str, size_type pos, size_typen);basic_string& append(const charT* s, size_type n);basic_string& append(const charT* s);basic_string& append(size_type n, charT c);

template<class InputIter>basic_string& append(InputIter first, InputIter last);void push_back(charT c);basic_string& assign(const basic_string& str);basic_string& assign(const basic_string& str, size_type pos, size_typen);basic_string& assign(const charT* s, size_type n);basic_string& assign(const charT* s);basic_string& assign(size_type n, charT c);template<class InputIter>basic_string& assign(InputIter first, InputIter last);basic_string& insert(size_type pos1, const basic_string& str);basic_string& insert(size_type pos1, const basic_string& str, size_typepos2,size_type n);basic_string& insert(size_type pos, const charT* s, size_type n);basic_string& insert(size_type pos, const charT* s);basic_string& insert(size_type pos, size_type n, charT c);iterator insert(iterator p, charT c);void insert(iterator p, size_type n, charT c);template<class InputIter>void insert(iterator p, InputIter first, InputIter last);basic_string& erase(size_type pos = 0, size_type n = npos);iterator erase(iterator position);iterator erase(iterator first, iterator last);basic_string& replace(size_type pos1, size_type n1, constbasic_string& str);basic_string& replace(size_type pos1, size_type n1, constbasic_string& str, size_type pos2, size_type n2) ;

basic_string& replace(size_type pos, size_type n1, const charT* s,size_type n2) ;

Page 18: C++ 2 (Kitap - Selami KOÇLU)

basic_string& replace(size_type pos, size_type n1, const charT* s) ;basic_string& replace(size_type pos, size_type n1, size_type n2, charTc) ;

basic_string& replace(iterator i1, iterator i2, const basic_string& str) ;basic_string& replace(iterator i1, iterator i2, const charT* s, size_typen) ;

basic_string& replace(iterator i1, iterator i2, const charT* s) ;basic_string& replace(iterator i1, iterator i2, size_type n, charT c) ;

template <class InputIterator>

basic_string& replace(iterator i1, iterator i2, InputIterator j1,InputIterator j2) ;

size_type copy(charT* s, size_type n, size_type pos=0) const ;void swap(basic_string& str) ;

String karakter karşılaştırmaları ile ilgili örneklerin bir kısmı ilk ciltteverilmişti. Dilerseniz bakabilirsiniz.

Bindirilmiş ilişkinlik işleçleri, stringlerin sayılar gibi işlenmesini sağlar.

While(tahminler>0 && atis!=hedef)

Bu, C stringlerindeki strcmp( ) işlevinin kullanılmasından daha kolaydır.Hemen bir şey daha belirtelim; npos string sınıfının static üyesidir. nposdeğeri string sınıf nesnesinde bulunan karakterlerin maksimum sayısıdır.Dizin sıfırdan başladığı için, olası en büyük dizinin 1 (bir) büyüğüdür.Bundan dolayı bir karakter veya stringi ararken hata göstermedekullanılabilir.

string nesne kullanımında C işlev bindirimistring nesnelerini karşılaştırırken bindirilmiş == işleci kullanılır. Fakatbüyük harf küçük harf ayırma özelliği nedeni ile, bu işleç bazı durumlardasorunlara neden olur. Yani büyük harf küçük harf ayrımı istenmediği zamanbaşka çözüm bulunması gerekir. Durumu bir örnekle açıklayalım;

Page 19: C++ 2 (Kitap - Selami KOÇLU)

#include <string> //string nesnesi için...string strA ;cin>>strA ; //kullanıcı selami adını girsinstring strB=”Selami” ; //belleğe konan sabitif(strA==strB){ cout<<”stringler ayni.\n” ;}else{ cout<<”stringler ayni degil.\n” ;}

selami'deki s ile Selami'deki S arasındaki farktan dolayı çıktı;

stringler ayni degil

olur. Peki biz büyük harf küçük harf farkını gözardı etmek istersek neyapmalıyız. Çok sayıda C kütüphanesinde, stricmp( ) ve _stricmp( )büyük küçük harfleri ayırmayan işlevler bulunur. Burada bir hatırlatmayapalım; bu işlevler C standardında bulunmamaktadır. O yüzden buişlevlerin size ait bindirilmiş sürümlerini yazmanız, ve onları programınızaeklemeniz gerekebilir. Şimdi yukarıdaki örneğin benzerini büyük harfküçük harf farkını gözardı ederek yazalım;

#include <cstring> //stricmp( ) işlevi için #include <string> //string nesneleri için...string strA ;cin>>strA ; //kullanıcının selami girdiğini kabul edelimstring strB=”Selami” ; //bellekteki sabitinline bool stricmp(const std::string& strA, const std::string& strB){ //bindirilmiş işlev return stricmp(strA.c_str( ), strB.c_str( ))==0 ; //C işlevi }bool bStringsAreEqual=stricmp(strA, strB) ;

Yukarıdaki basit yazım biçimi ile harf büyüklüklerini gözardı ederek,string özdeşliklerini sınayabilirsiniz.

Page 20: C++ 2 (Kitap - Selami KOÇLU)

Dizin yeri belli öğeyi, [] ve at( ) işlevi ile bulmak

C biçimi yazımda, dizide herhangi bir yerdeki karakteri bulabilmek için, []köşeli ayraçlar kullanılmaktaydı. C++ string sınıfı sayesinde at( ) işlevi ileaynı işlem kolayca gerçeklenebilir. Örnek;

string s(“abcdef”) ;cout<<s[2]<<” “ ;cout<<s.at(2)<<endl ;

çıktı;

c c

Evet, burada her iki kullanım aynı gibi gözükmesine rağmen, aslında [] ileat( ) işlevi arasında büyük bir fark bulunmaktadır. at( ) işlevi dizisınırlarının dışında bir öğeye erişilmek istendiği zaman istisnafırlatılmasını sağlar, [] (köşeli ayraçları) ise sizi bu durumda bir başınızabırakır. Örnek;

string s(“abcdef”) ;s[6] ; //çalışma sırasında hataya yolaçar, zira dizi dışı try{ //istisna fırlatma denemesi yapabilirsiniz. s.at(6) ;}catch(...){ cerr<<”dizi sınırları dışına çıkıldı”<<endl ;}

Programcılıkta istisnalar program kurgusunun dışında kalan değerleriiçerirler. Bu değerler aslında istenmeyenler olup, kullanılmaya kalkıldığızaman programın dengesini alt üst edecek sonuçlara yolaçabilirler. Onedenle istisna yönetimi birinci kitapta da belirttiğimiz gibi önemli birkonudur. Burada da at( ) gibi istisna fırlatmayı sağlayacak işlevi, [] yerinekullanmak oldukça yararlıdır. İstisnai durum ortaya çıktığı zaman, istisnayöneticisi, programın düzenini sürdürmek için, sonraki eylemleri ona göredüzenler. Yani siz programınızı ona göre geliştirirsiniz. Şimdi string yöntemlerinin kullanıldığı eğlenceli bir program yazalım.Programımız adam asma oyunun grafik uygulamasıdır. Program beş harflikelimelerden oluşan string nesneler dizisine sahiptir, ve gelişigüzel olarakbunların içinden birini seçer. Sizde bu kelimeyi tahmin etmeye çalışırsınız.

Page 21: C++ 2 (Kitap - Selami KOÇLU)

Kelimeyi harf tahminleri ile bulacaksınız.

//:B01:string3.cpp //string kullanılan eğlenceli bir adam asma programı#include <iostream>#include <string>#include <cstdlib>#include <ctime>#include <cctype>using namespace std ;const int NUM=10 ;const string kelime_list[NUM]={“izmir”, “bursa”, “sinop”, “siirt”, “sivas”, “corum”, “kilis”, “tokat”, “mugla”, “konya”};int main( ){ srand(time(0)) ; char oyna ; cout<<”kelime oyunu oynayalim?<y/n>” ; cin>>oyna ; oyna=tolower(oyna) ; while(oyna=='y'){ string hedef=kelime_list[rand( ) % NUM] ; int length=hedef.length( ) ; string attempt(length, '-') ; string badchars ; int tahminler=5 ; cout<<”gizli kelimeyi tahmin et. Uzunluk”<<length <<”tahmin edilen harfler\n” <<”her seferinde bir harf ile elindeki”<<tahminler <<”tahminde hata .\n” ; cout<<”kelimeniz: “<<attempt<<endl ; while(tahminler>0 && attempt!=hedef){ char harf ; cout<<”bir harf tahmin et: “ ;

cin>>harf ; if(badchars.find(harf) != string::npos ||attempt.find(harf) != string::npos){ cout<<”tahmin ettin, bir daha denermisin?.\n” ; continue ; } int loc=hedef.find(harf) ;

Page 22: C++ 2 (Kitap - Selami KOÇLU)

if(loc==string::npos){ cout<<”yanlis tahmin!\n” ; --tahminler ; badchars+=harf ; //stringe ekle } else{ cout<<”dogru tahmin !\n” ; attempt[loc]=harf ; loc=hedef.find(harf, loc+1) ; //harf yine belirirse while(loc!=string::npos){ attempt[loc]=harf ; loc=hedef.find(harf, loc+1) ; } } cout<<”kelimeniz: “<<attempt<<endl ; if(attempt!=hedef){ if(badchars.length( )>0) cout<<”berbat secimler: “<<badchars<<endl ; cout<<tahminler<<”berbat tahminler kaldi\n” ; } } if(tahminler>0) cout<<”tamam dogru.! \n” ; else cout<<”kusura bakma kelime: “<<hedef<<”.\n” ; cout<<”bir daha denermisin?.<y/n>” ; cin>>oyna ; oyna=tolower(oyna) ; } cout<<”elveda\n” ; return 0 ;}//:~

Programı Linux'ta derlediğinizde konsolda adam asma oynayabilirsiniz.

Başka neler var

String kütüphanesinde bulunan işlevler, başka deyimle yöntemler

Page 23: C++ 2 (Kitap - Selami KOÇLU)

kümesinin sağladığı kolaylıklar, yukarıdakilerle sınırlı değildir. Stringinbir kısmını veya tamamını silen, bir string ile diğer bir stringin birparçasını veya tamamını yerdeğiştiren, stringe başka unsurlar ekleyenveya silen, bir string ile başka bir stringin bir parçasını veya tamamınıkarşılaştıran, bir stringten bir alt stringi bulup çıkaran işlevlerbulunmaktadır. Bir işlevde, bir stringin bir parçasını diğer bir stringekopyalamaktadır. Ayrıca string içeriklerini değiş tokuş eden bir işlevdebulunmaktadır. Bu işlevler string nesneleri ile çalıştıkları kadar, bindirimeuğratılarak C benzeri stringlerle de, aynı biçimde çalışırlar. Burada birçokkısımda, stringleri char tiplermiş gibi ele alarak inceledik. Aslında dahaönce de belirttiğimiz gibi string sınıfı gerçekte kalıp (template) sınıfınayaslanarak geliştirilmiştir.

template<class charT, class traits=char_traits<charT>, class Allocator=allocator<charT>>basic_string{....} ;

Sınıfta aşağıdaki iki typedef bulunur;

typedef basic_string<char> string ;typedef basic_string<wchar_t> wstring ;

Son satır wchar_t tabanlı stringlerin kullanımını izin verir. Hatta siz bellikoşulları yerine getirip karakter benzeri unsurlar için bir sınıf geliştirebilirve basic_string kalıp sınıfını onunla kullanabilirsiniz. traits sınıfı seçilenkarakterlerin öne çıkan özelliklerini tanımlayan bir sınıftır, örnek olarakdeğerlerin karşılaştırma biçimini açıklaması gibi. char ve wchar_t tipleriiçin, char_traits kalıbının önceden tanımlanmış özellikleri bulunur. Vebunlar traits kalıbının varsayılanlarıdır. Allocator sınıfı, bellekyerleşimini yöneten sınıftır. char ve wchar_t tipleri için allocatorkalıbının önceden tanımlanmış özellikleri vardır. Bunlar da allocatorvarsayılanlarıdır. Genellikle new ve delete işleçleri bellek yerleşimindekullanılır. Ama isterseniz belleğin bir yerini kendi bellek yönetimbiçimleriniz için ayırıp kullanabilirsiniz. Bu da sizin yeteneklerinize bağlı.Aslında herşey sizin paradigmaları kavrayıp onları uygulama becerilerinizekalmıştır. Burada kurallar anlatılır birkaç örnekle uygulama gösterilir, gerikalan sahneleri ve oyunları siz oynamalısınız. Programcılık kuralları ilgilidil tarafından konmuş zeka sanatıdır. Notaları herkes öğrenebilir ama,tutulan besteyi herkes yapamaz.

Page 24: C++ 2 (Kitap - Selami KOÇLU)

ÖRNEKLER

1- Doğum tarihlerini okuyup onaylayan bir program örneği

//B:dogum tarihlerini okuyup onaylama#include <iostream> #include <string> using std::cout;using std::cin;using std::endl;using std::string;int valid_input(int lower, int upper, const string& description);int year();int month();int date(int month_value, int year_value); int main() { cout << "Enter your date of birth." << endl; int date_year = year(); int date_month = month(); int date_day = date(date_month, date_year); string months[] = {"January", "February", "March", "April", "May","June", "July", "August", "September", "October", "November","December" }; string ending = "th"; if(date_day == 1 || date_day == 21 || date_day == 31) ending = "st"; else if(date_day ==2 || date_day == 22) ending = "nd"; else if(date_day == 3 || date_day == 23) ending = "rd"; cout << endl << "We have established that your were born on " << months[date_month-1] << " " << date_day << ending << ", " << date_year << "." << endl; return 0;}// Upper ve lower arasında bir int değer okurint valid_input(int lower, int upper, const string& description) { int data = 0; cout << "Please enter " << description << " from " << lower << " to " << upper << ": "; cin >> data; while(data<lower || data>upper) { cout << "Invalid entry; please re-enter " << description << ": "; cin >> data; } return data;}

Page 25: C++ 2 (Kitap - Selami KOÇLU)

// Yılı okurint year() { const int low_year = 1850; // 155 yaşından büyük insan olmaz const int high_year = 2000; // 5 yaşından küçük olmaz... return valid_input(low_year, high_year, "a year");}// Ayı okurint month() { const int low_month = 1; const int high_month = 12; return valid_input(low_month, high_month, "a month number");}// Verilen ay ve yıldaki tarihi okurint date(int month_number, int year) { const int date_min = 1; const int feb = 2; // Ayların gün sayısı: Jan Feb Mar Apr May Jun Jul Aug Sep Oct NovDec static const int date_max[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,31}; // Yukarıdaki dizi static olarak belirtildiği için, işlev sadece ilk kez //çağrıldığı zaman oluşturulur. Tabii, bir kez çağrıldığı için hiçbir bilgi //bellekte saklanmaz. // Şubat artık yıllarda 29(feb 29) olur. Artık yıl 4 e bölünebilen yıldır // 100 e bölünebilenler hariç fakat 400 e bölünebilenler dahil. if( month_number == feb && year%4 == 0 && !(year % 100 == 0 && year%400 != 0)) return valid_input(date_min, date_max[month_number-1]+1, "a date"); else return valid_input(date_min, date_max[month_number-1], "a date");}///:~

2- String karakterlerini tersten yazan program

//B: string karakterlerini tersten yazma örneği/******************************************************************reverse() işlevi string tipinin değişkeni ile veya birC-biçimi yani '\0'sonlanan string ile çalışır.*******************************************************************/#include <iostream>#include <string>using std::cout;using std::cin;using std::endl;using std::string;string reverse(string str1);int main() { string sentence; cout << "Enter a sequence of characters, then press 'Enter': " << endl; getline(cin, sentence); cout << endl << "Your sequence in reverse order is: " << endl; cout << reverse(sentence) << endl; cout << "Here is a demonstration of reverse() working with a C-style string" << endl;

Page 26: C++ 2 (Kitap - Selami KOÇLU)

char stuff[] = "abcdefg"; // C-style string cout << endl << "The original string is: \"" << stuff << "\"" << endl << "Reversed it becomes: \"" << reverse(stuff) << "\"" << endl; return 0;}// stringi tersten yaz// Buradaki kod, değişkenin kopyası ile çalışıyor// o nedenle esas string etkilenmiyorstring reverse(string str) { char temp = 0; for(int i=0; i < str.length()/2; i++) { temp = str[i]; str[i] = str[str.length()-i-1]; str[str.length()-i-1] = temp; } return str;}///:~

3- Komut satırından girilenleri tersten yazan program

#include <iostream>#include <string>using std::cout;using std::endl;using std::string;string reverse(string str1);int main(int argc, char* argv[]) { switch(argc-1) { case 2: for(int i = 1; i < argc; i++) cout << "Argument " << i << " is " << argv[i] << endl; cout << "argument 2 reversed is : \"" << reverse(argv[2]) << "\"" << endl; break; default: cout << "You entered the incorrect number of arguments. " << endl << "Please enter 2 arguments. " << endl; } return 0;}// stringi tersten yaz // Buradaki kod değişkenin kopyası ile çalışıyor// o nedenle esas string etkilenmiyorstring reverse(string str) { char temp = 0; for(int i=0; i < str.length()/2; i++) { temp = str[i]; str[i] = str[str.length()-i-1]; str[str.length()-i-1] = temp; } return str;}///:~

4- Bir stringin altstringini arayıp bulma ve onu başka ikinci bir stringeyerleştirme.

//B: örnekte bir stringin altstringini bulup başka bir stringte yerleştirme.

Page 27: C++ 2 (Kitap - Selami KOÇLU)

#include <iostream>#include <string>#include <cctype>using std::cout;using std::cin;using std::endl;using std::string;int main() { string text; // aranan string cout << "Enter the text that is to be searched terminated by #:" << endl; std::getline(cin, text, '#'); string textcopy = text; // stringin kopyasını alma // Kopyayı küçük harfe çevirme for(int i = 0 ; i<textcopy.length() ; i++) textcopy[i] = std::tolower(textcopy[i]); string word; // Bulunacak kelime cout << "Enter a word that is to be found in the string : "; cin >> word; string wordcopy = word; // kelimenin kopyasını oluşturma // Kopyayı küçük harfe çevirme for(int i = 0 ; i<wordcopy.length() ; i++) wordcopy[i] = std::tolower(wordcopy[i]); // Kelimedeki harf sayısı kadar asteriksle (*) string oluştur string asterisks = word; asterisks.replace(0, asterisks.length(), asterisks.length(), '*'); cout << "Each occurrence of \"" << word << "\" will be replaced by " << asterisks << "." << endl;

// wordcopy'yi textcopy nesnesi içinde ara // ama text içinde asteriksle(=*) değiştir. int position = 0; while((position = textcopy.find(wordcopy, position)) != string::npos) { // Bulunan kelimeden önceki ve sonraki karakterlerin abecesel olmadığını //denetlemek zorundayız, ama dizi değerlerini kullanmamaya dikkat etmeli // zira geçersiz olur- böylece sadece ifler kullanılır. if(position==0) { if(!std::isalpha(textcopy[position+word.length()])) text.replace(position, word.length(), asterisks); }else if(position+word.length()==text.length()) { if(!std::isalpha(textcopy[position-1])) text.replace(position, word.length(), asterisks); } else { if(!std::isalpha(textcopy[position-1]) && !std::isalpha(textcopy[position+word.length()])) text.replace(position, word.length(), asterisks); } position += word.length(); } cout << endl << "After processing the original string is now:" << endl << text << endl; return 0;}///:~

5- Öğrencilerin notlarını bulunduran not defteri tasarımı

Page 28: C++ 2 (Kitap - Selami KOÇLU)

// öğrencilerin not defteri olarak iki dizi kullanımı #include <iostream>#include <string>using std::cout;using std::cin;using std::endl;using std::string;int main() { const int MAX_STUDENTS = 100; string student_names[MAX_STUDENTS]; double student_marks[MAX_STUDENTS] = {0}; int student_count = 0; double total_marks = 0; double class_average = 0; char answer = 'n'; // Veri giriş döngüsü. Bu döngü öğrenci notlarının öğretmen tarafından //girilmesini sağlar. do { cout << "Enter a student's name: "; cin >> student_names[student_count]; cout << "Enter " << student_names[student_count] << "\'s mark: "; cin >> student_marks[student_count]; total_marks += student_marks[student_count++]; cout << "Do you wish to enter another student's details (y/n): "; cin >> answer; } while (tolower(answer) == 'y' && student_count < MAX_STUDENTS); if(student_count == MAX_STUDENTS) cout << endl << "Maximum number of students reached." << endl; // Calculating the class average. class_average = total_marks / student_count; // Sınıf ortalamasını gösterme. cout << endl << "The class average for " << student_count << " students is " << class_average; cout << endl; // Öğrenci isim ve notlarını gösterme. for(int i = 0; i < student_count ; i++) cout << endl << student_names[i] << "\t\t" << student_marks[i]; cout << endl; return 0;}///:~

6- Nem değerlerinin ortalamasını alma, saklama, ve gösterme işlemleriniiki boyutlu dizi ile gerçekleştirme

//B: İki boyutlu dizi ile nem değerlerinin ortalamasını alma, saklama ve //gösterme #include <iostream>#include <string>using std::cout;using std::cin;using std::endl;using std::string;

Page 29: C++ 2 (Kitap - Selami KOÇLU)

int main() { const string days[] = {"Monday", "Tuesday", "Wednesday", "Thursday","Friday"}; const string times[] = {"morning", "midday", "evening"}; float humidity[sizeof days/sizeof days[0]][sizeof times/sizeof times[0]] ={0.0f}; float day_averages[sizeof days/sizeof days[0]] = {0.0f}; float week_averages[sizeof times/sizeof times[0]] = {0.0f}; // Nem bilgisinin girişi için döngüler kullanılır ve tamamı biriktirilir. for(int day = 0 ; day < sizeof days/sizeof days[0] ; day++) { cout << endl << days[day] << endl; for(int time = 0 ; time < sizeof times/sizeof times[0] ; time++) { cout << " Enter " << times[time] << " reading: "; cin >> humidity[day][time]; week_averages[time] += humidity[day][time]; // O an için toplamı //biriktir day_averages[day] += humidity[day][time]; // O gün için toplamı //biriktir. } } cout << endl; // Hergün için ortalamayı çıktıya gönderme for(int day = 0 ; day < sizeof days/sizeof days[0] ; day++) cout << "Average humidity for " << days[day] << ": " << day_averages[day]/(sizeof times/sizeof times[0]) << endl; cout << endl; // Her seferinde haftalık ortalamayı çıktıya gönderme for(int time = 0 ; time < sizeof times/sizeof times[0] ; time++) cout << "Average " << times[time] << " humidity: " << week_averages[time]/(sizeof days/sizeof days[0]) << endl;

return 0;}//:~

Yukarıdaki örnek çözümlerde “using std::cout;”, “using std::endl;”,vs..benzeri using bildirimlerini gördünüz. Burada sadece ilgili nesnenin (cout,endl, string .. vs.) standart isim alanları kabul edilmiş demektir. Yanistandart kütüphanenin tamamını içeren “using namespace std;” isim alanıyerine, tek tek ilgili nesnelerin standart isim alanları seçilmiştir. Böylecederleyicinin isimlerle ilgili kapsama alanı daraltılmıştır. Program dahaverimli kılınmıştır. using std::cout ve using std::cerr; vs benzeriaçıklamalar using bildirimi, using namespace std; ise using yönergesidir.Programlarınızda using bildirimlerinin sayısı yazılamayacak kadarçoğalırsa, bunların yerine “using namespace std; “ using yönergesinikullanmak daha elverişlidir.Burada bir farkı daha anlatalım: 'a' bir karakteri, “a” ise bir stringi gösterir.

Page 30: C++ 2 (Kitap - Selami KOÇLU)

2.BÖLÜM

GİRDİLER ve ÇIKTILAR

Programcılıkta girdi, programa programın herhangi bir anında giren bilgi,çıktı ise, programdan programın herhangi bir anında çıkan bilgidemektir.Giren bilgi yani girdi, çevresel birimden gelebildiği gibiprogramın içindeki bir başka dosyadan da olabilir. Çıktı ise programdanherhangi bir çevresele yapılabildiği gibi başka bir programa dahi olabilir.Burada çevreselden kastedilen; klavye, monitör, yazıcı vs. donanımlardır.C++ dilinin Girdi/Çıktı (=Input/Output) işlemlerini inceleme sırasındagenel bir sorunla karşılaşılır. Zira bir yandan, uygulamaların en basitindebile girdi/çıktı işlemlerinin olması, ve dil öğrenme sürecinde karşılaşılanilk görevin bunları öğrenme zorunluluğu, öbür yandan C++ nın girdi/çıktıişlemleri için oldukça ileri dil özellikleri kullanılmasıdır. Bu özelliklerarasında sınıflar, türetilmiş sınıflar, işlev bindirimi, sanal işlevler, çoklukalıtım ve kalıplar yer alır. Bundan dolayı C++ girdi/çıktı işlemlerini tamanlamı ile kavrayabilmek için, çokca C++ bilmek gereklidir. Başlangıçolarak, girdilerde kullanılan cin nesnesinin istream sınıfı, çıktılardakullanılan cout nesnesinin ise, ostream sınıf nesnesi olduğunu belirtelim.C++ dilinde C dilinden farklı olarak, girdi/çıktı işlemleri sınıflarayaslanarak geliştirilmiştir. Artık girdi/çıktı sınıflarını uzun uzun incelemenin zamanı geldi. Böylecebu sınıfların tasarım ve çıktı biçemlerini öğreneceğiz.

Page 31: C++ 2 (Kitap - Selami KOÇLU)

C++ da dosya girdi/çıktıları da, cin ve cout nesnelerinin yaslandığı aynısınıf tanımlarını temel alırlar. Önce girdi ve çıktı nedir kabaca anlatalım.Siz bir sürü odası ve kapıları olan bir eve giriyorsunuz. Her odanın dörtkapısı olsun (o kadar kapı olmaz ya neyse anlaşılsın diye kabul yapıyoruz).Siz bir odaya giriyorsunuz. O an da siz o odanın girdisisiniz. Daha sonraaynı odanın başka bir kapısından çıkıyorsunuz. O anda da siz aynı odanınçıktısısınız. Aslında aynı oda, aynı kişi, fakat zaman içinde yapılan eylemfarklı olduğu için girdi çıktı tanımları farklılaşıyor. Siz eski odadan yenibir odaya geçtiğinizde artık yeni odanın girdisisiniz. Yani bu durumdabirinci odanın çıktısı ikinci odanın girdisi oluyorsunuz. Aslındabilgisayarlardaki girdi çıktı kavramlarını da bu şekilde düşünmelidir.

C++ girdi ve çıktıları (input/output)

Pascal benzeri çok sayıda dil, girdi/çıktı işlemlerini doğrudan dilin içinekoyarlar. Bu tür dillerde print veya write benzeri deyimler, doğrudan dilinanahtar kelimeleri arasında yer alır. C/C++ dilleri böyle değildir. Gerek Cgerekse C++ dillerinin anahtar kelimelerine bakarsanız, if, for vs.. gibikelimeler bulunmasına rağmen, girdi/çıktı işlemleri ile ilgili bir anahtarkelimeye rastlayamazsınız. C, aslında girdi/çıktı işlemlerinin yerinegetirilmesini, derleyiciyi kullanana bırakır. Bunun bir nedeni; hedeflenenbilgisayarın donanımına en uygun girdi/çıktı işlevlerinin tasarımı için,kullananı özgür bırakmaktır. Uygulamada ise büyük çoğunluk, aslındaUNIX ortamı için geliştirilen kütüphane işlevlerinde yer alan girdi/çıktıişlevlerini esas alır. ANSI C dilinin resmiyet kazanan bu girdi/çıktı paketi,Standart Girdi/Çıktı paketi olarak adlandırılır. C++ dili bu paketin kendibünyesinde de kullanılmasına izin verir. Böylece C dilindeki stdio.h dayeralan işlevler hakkında bilgi sahibi iseniz, bunları C++ programlarınıniçinde kullanabilirsiniz. C++ da bunları kullanmak için başlık dosyasınacstdio eklemelisiniz. (yani #include <cstdio> )Herşeye rağmen C++, girdi/çıktı işlemlerinde kendi çözümlerini Cçözümlerine tercih eder. Bu çözümlerde iostream ve fstream başlıkdosyaları ile tanımlanan sınıflar kullanılır. Sınıf kütüphanesi C++ resmi diltanımının bir parçası değildir. Sadece nesne yönelimli özelliğindenkaynaklanan ve kabul edilmiş standart kütüphanedir. C dili ile C++ dilinibirbirinden ayıran temel özellik budur; C de sorun dile uyar, C++ da isedil soruna uyar. C++ da cin ve istream anahtar kelimeler değildir.Hepsinden öteye C++ bilgisayar programlama dili, sınıflar ve benzeriunsurların nasıl oluşturulacağının kurallarını koyar. Bu kuralların ardından

Page 32: C++ 2 (Kitap - Selami KOÇLU)

nelerin yapılması lazım geldiği ile uğraşmaz. C işlevlerin standartkütüphanesini, C++ ise sınıfların standart kütüphanesini kullanır.Şimdi artık standart C++ girdi/çıktısı için, düşünce çerçevesi oluşturupinceleyelim.

Akışlar (streams) ve Tamponlar (buffers)

C++ programlarında girdi ve çıktılar, baytların akışıdır. Girdide, programbaytları girdi akışından elde eder. Çıktıda ise program, baytları çıktıakışına yerleştirir. Metine yönelik bir programda, her bayt bir karakteritemsil eder. Daha genel bir deyişle baytlar, karakter veya sayısal verinin ikitabanlı sayılarla gösterimini sağlar. Girdi akışındaki baytlar klavyedengelebildiği gibi, herhangi bir bellek aracından (hard disk gibi), veya başkabir programdan da gelebilir. (girdi çıktı anlatımında yukarıdaki odalarbenzetimini hatırlayın). Benzer şekilde, çıktı akışındaki baytlar da ekranagittiği gibi, yazıcıya, bir bellek aracına veya başka bir programa dagidebilir. Akış, programla akışın kaynağı, veya akışın varış yeri arasındaarabulucu gibi davranır. Bu yaklaşım C++ programlarının, klavyeden girenbilgi ile, dosyadan giren bilgiye de aynı şekilde davranmasını sağlar. YaniC++ programı, baytların akışını kaynağına bakmaksızın gözönüne alır.Yine benzer şekilde, bir C++ programı baytların akışını, varacağı yerebakmaksızın inceler. O zaman girdi yönetimi iki aşamalıdır;

**girdi bulunduran akışı programa ilişkilendirmek**akışı dosyaya bağlamak

Başka deyişle bir girdi akışı, herbiri bir uçta olmak üzere, iki bağlantıyagerek duyar; dosya uç bağlantısı ve program uç bağlantısı. Dosya uçbağlantısı akışın kaynağını sağlar. Program uç bağlantısı ise akışıprograma boşaltır. Dosya uç bağlantısı dosya olabileceği gibi, bir araçtaolabilir, örnek; klavye gibi. Benzer şekilde çıktı yönetimi de, çıktı akışınıprograma bağlamayı, ve çıktı varış yerini akışla ilişkilendirmeyi kapsar.Aynı sıhhi tesisat düzeni gibi, yalnız burada su yerine baytlar akıyor. Tampon kullanarak girdi/çıktı işlemlerini gerçeklemek genellikle dahaetkili olmaktadır. Tampon büyük bir bellek parçası olup, bilginin biraraçtan programa veya programdan bir araca aktarımı sırasında kolaylıksağlayan, geçici olarak kullanılan saklama yeridir. Tipik olarak disksürücüsü gibi araçlar, bilgileri 512 baytlık (veya daha fazla) büyüklüklerhalinde aktarırlar. Programların bilgiyi işlemleme büyüklüğü ise, her

Page 33: C++ 2 (Kitap - Selami KOÇLU)

seferinde bir bayttır. İşte tamponlar bilgi aktarımdaki bu farklı hızları,birbiri ile uyumlu hale getirirler. Örnek olarak hard diskteki et (@)işaretlerini sayan bir programı ele alalım. Program dosyadan bir karakterokur işlemler, dosyadan bir sonraki karakteri okur işlemler, ve bu böylesürer gider. Hard diskte yer alan bu karakter için her seferinde dosyadanbir karakter okumak, çok fazla donanım etkinliği gerektirir ve yavaş işler.Tampon yaklaşımında ise hard diskten büyük bir alan toptan okunur, dahasonra bu okunan alan tampon da saklanır, ve sonra tampon her seferindebir karakter olmak üzere okunur. Bu tercihin nedeni; tampon bellektenokuma hızının, hard diskten okuma hızına göre çok fazla olmasıdır.Hatırlatalım tampon bellek RAM bellektir. Hard diski ise biliyorsunuz.Tabii tampon bellekteki bilginin sonuna gelindiğinde, program harddiskten bir sonraki bellek alanını okur. Tekrar tampon belleğe atar, böylesürer. Bu, suyu büyük bir depoda (tampon) toplayıp, daha sonra hızladamlatarak (baytları tek tek okuma) akıtmaya benzer.Çıktılarda da benzer şekilde program tamponu önce doldurur, daha sonratampondaki verinin tamamını hard diske aktarır, çıktının bir sonrakiverisini aktarabilmek için tampon silinir, bir sonraki veri tamponayerleştirilir, ve bu böyle sürer. Buna tamponun doldurulup boşaltılmasıdenilir. Veya siz, tampon tahmil ve tahliyesi de diyebilirsiniz.Klavyeden her seferinde bir karakter girilebildiğinden, klavye için tampongerekmez. Zira farklı veri aktarım hızları bulunmamaktadır. Bununlabirlikte tamponlanmış klavye girdisi, kullanıcıya programa gönderilmedenönce girdinin korunması ve düzeltilebilmesi olanağını verir.Bir C++ programı girdi tamponunu enter tuşuna basıldığında doldurur, bunedenle bilgisayarlarda klavyeden girdi yazarken enter tuşuna basıncayakadar programda işlemleme yapılmaz. Ekrana çıktı gönderebilmek için de,C++ programı tamponda yeni satır karakterini (\) görmeyi bekler. Tabiibunu sizin göndermeniz gerekir. Gelmekte olan girdi için programuygulamaya bağlı olarak önceki girdiyi boşaltabilir. Başka bir deyişleprograma girdi emri (bir girdi deyimi ile) verildiğinde, o anki çıktısınıherhangi bir çıktı tamponuna boşaltır. Bu bakımdan C++ programları,ANSI C dili ile tutarlılık içindedir.Ünlü emacs programı tampon bellek kullanımının en güzeluygulamalarından biridir. Emacs programında birden fazla uygulama aynıanda farklı tampon belleklerde işleme tabi tutularak çalıştırılabilir.İsterseniz bir editör, isterseniz bir dosya yöneticisi veya bir programgeliştirme ortamı olarak. Ayrıca başka amaçlarla da kullanabilirsiniz.Tampon bellek kullanımı yeterli RAM belleğiniz varsa, oldukça hızlıçalışan programlar geliştirmenizi sağlar.

Page 34: C++ 2 (Kitap - Selami KOÇLU)

iostream dosyası, akışlar ve tamponlar

Akışları ve tamponları yönetmek programcı için biraz karmaşık bir iştir.Bu yüzden iostream dosyasında bulunan, işlemleri bizim adımızagerçekleyen sınıflar kullanılır. C++ dilinin son Girdi/Çıktı (I/O) sürümlerichar ve wchar_t verilerini destekleyen sınıf kalıpları tanımlamıştır. Bukalıpların özel char uygulamaları içinde, typedef sayesinde C++ klasikkalıpsız girdi/çıktı işlemleri gerçeklenebilir. Bunlarla ilgili sınıflarınbazıları aşağıda verilmiştir;

**streambuf sınıfı tamponlar için bellek yeri temin eder. streambuf sınıfyöntemleri ile (yani yapıcı işlevleri ile) tamponlar doldurulur, tamponiçeriklerine erişilir, tampon boşaltılır ve tampon bellekler yönetilir.

**ios_base sınıfı akışın genel özelliklerini temsil eder. Akışın okumak içinaçık olup olmadığı ve metin ya da sayısal akış olup olmadığı hususlarıgibi.

**ios sınıfı ios_base sınıfını dayanır yani doğrudan ondan türetilir. iossınıfında, streambuf nesnesine göstergeç üye bulunur. Girdi ve çıktıişlemlerini gerçekleyen G/Ç kütüphanesinin her sınıfı, ios sınıfındantüretilmiş ve yeteneklerini ondan almıştır (dolayısı ile ios_basesınıfından). ios sınıfının önemli bir işlevi de akışlar tarafından kullanılan tamponlailetişimi tanımlamaktır. Tampon streambuf nesnesidir (veya streambufsınıfından türetilmiştir) . Asıl, girdi ve (veya) çıktıdan sorumlu olan odur.Bunun anlamı; iostream nesneleri girdi ve çıktı işlemlerini kendilerigerçeklemez demektir. Anılan işlemler ilgili tampon nesnelerinebırakılmıştır. Okuyucu bu bölümü okurken bunları aklındançıkarmamamlıdır.

**ostream sınıfı ios sınıfından türetilmiştir. Çıktı yöntemlerini (yaniyapıcı işlevlerini) bulundurur.

**istream sınıfı da ios sınıfından türetilmiştir. Girdi yöntemlerinibulundurur.

**iostream sınıf hem istream hem de ostream sınıfından türetilmiştir.

Page 35: C++ 2 (Kitap - Selami KOÇLU)

Yani çoklu kalıtım örneğidir. Bu yüzden girdi/çıktı yöntemlerini türetir.

Bu kolaylıkların kullanılabilmesi için uygun sınıf nesnelerioluşturulmalıdır. Örnek olarak; çıktıyı yönetmek için ostream sınıf nesnesicout kullanılması gibi. Böyle bir nesne oluşumu ile akış açılır, otomatikolarak tampon oluşur ve onu akışla ilişkilendirir. Ayrıca sınıf üye işlevleride hizmet vermeye hazırdır.

Örnek:

cout<<”Okullarda”<<talebe<<”bulunmakta\n” ;

Yukarıdaki örnekte derleyici, “talebe” değişkeninin tipini saptar vedeğerini cout iostream'inde, cümlenin uygun yerine yerleştirir.Benzer sonucu C dilinde elde etmek istersek, printf( ) işlevi kullanılır.İşlev değişkenlerinin belirtilen tipleri ile, aynı öğenin işleve gönderilen tipiarasında uyumsuzluk olduğu takdirde, derleyici sizi uyarmaktan başka biriş yapmaz. Yani C dilinde tip güvenliği tam olarak sağlanamamıştır. C++dilinde ise sağlanan tip güvenliği sayesinde, tip uyumsuzluğu baştanengellenmiştir. iostream kütüphanesinde akış nesneleri sınırlı bir role sahiptir. Bunlar biryandan, girdi veya çıktı nesneleri arasında arayüz oluştururken, öte yandanstreambuf, araçlara asıl girdi ve çıktılar sorumlusu olarak ilk adımdastreambuf nesnesini oluşturmuştu. Bu yaklaşım yeni bir araç çeşidi içinyeni bir çeşit streambuf oluşturmak amacı ile streambuf eski iyi dostlaristream veya ostream sınıfları ile birlikte kullanılır. Burada kavranmasıgereken en önemli şey; iostream nesnelerinin biçemleme yeteneği ile, biraraç için streambuf ta gerçeklenen tamponlama arayüzü arasındaki farktır.Yeni araçlara arayüz oluştururken (soket veya dosya belirteçleri gibi) farklıbir streambuf inşaası gerekir, ama yeni bir istream veya ostream nesnesigerekmez. Kaplayıcı (wrapper class)bir sınıf istream veya ostreamsınıfları etrafında inşa edilebilir, ama sadece belli bir araca kolaylıklaerişebilmek amacı ile. Stringstream sınıflarının inşa edilme biçimleribunlardı.

Page 36: C++ 2 (Kitap - Selami KOÇLU)

ios_base

ios

istream ostream

ifstream istringstream ofstream ostringstream

iostream

streambuf*filebuf*

stringbuf*

GİRDİrdbuf( ) streambuf*

ÇIKTI

Page 37: C++ 2 (Kitap - Selami KOÇLU)

Özel başlık dosyalarıiostream kütüphanesinde çok sayıda özel başlık dosyası tanımlanmıştır.Koşullara bağlı olarak aşağıdaki başlık dosyaları programlarınızaeklenebilir.

**#include <iosfwd> : iostream sınıfları için önceden bildirimgerektiği zaman bu önişlemleyici yönergesi kullanılmalıdır. Örnek olarakbir işlev ostream e bir dayanç değişkeni tanımlarsa, işlev kendinibildirirken, derleyici ostream in ne olduğunu tam olarak bilmesigerekmez. Başlık dosyasında böyle bir işlevin bildiriminde, ostreamsınıfının sadece bildirimi gerekir. Ama aşağıdaki yazımı kullanmazsınız;

class ostream ; //hatalı bildirimvoid birfunc(ostream &str) ;

Bunların yerine;

#include <iosfwd> //doğru bildirilmiş ostreamvoid birfunc(ostream &str) ;

kullanılmalıdır.

**#include <streambuf> : streambuf ve filebuf sınıfları kullanılırkenbu önişlemleyici yönergesi yazılır. **#include <istream> : istream sınıfları kullanırken veya hem girdihem de çıktı gerçekleyen sınıfları kullanırken lazım olur.

**#include <ostream> : ostream sınıfları kullanılırken buönişlemleyici yönergesi gerekir veya, hem girdi hem de çıktı gerçekleyensınıflarla kullanılır.

**#include <iostream> : global akış nesneleri (cin, cout benzeri)kullanılırken bu önişlemleyici yönergesi gerekir.

**#include <fstream> : dosya akış sınıfları kullanılırken gerekenönişlemleyici yönergesidir.

Page 38: C++ 2 (Kitap - Selami KOÇLU)

**#include <sstream> : string akış sınıfları kullanılırken gerekenönişlemleyici yönergesi.

**#include <iomanip> : değerleri belirlenmiş özelleştiricilerinkullanıldığı zaman gereken önişlemleyici yönergesi.

Ana sınıf: ios_base

Bütün girdi/çıktı işlemlerinin temelini ios_base sınıfı oluşturur. G/Çakışlarının durumunu denetlemek ve çıktı biçemlerini düzenlemek, onuntanımladıkları arasında yer alır. G/Ç kütüphanesinin her akış sınıfı, iossınıfı aracılığı ile bu sınıftan türetilir ve yetenekleri aktarılır.ios_base sınıfı C++ dilinde inşa edilen bütün G/Ç ların temeli olan sınıftır,bundan dolayı onu C++ G/Ç kütüphanesinin ilk sınıfı olarak ele alacağız.Burada yine belirtelim; C de olduğu gibi C++ da da G/Ç sınıfı, dilin resmiparçası değildir (C++ da ANSI/ISO standartı olmasına rağmen). Aslındaöntanımlı olarak verilen bütün G/Ç kütüphaneleri teknik olarak gözardıedilebilir, ama kimse bunu yapmaz, belirtilen G/Ç kütüphanesi C++ da defakto G/Ç standartı olmuştur. Burada hemen bir şeyi daha açıklayalım;iostream sınıfları girdi ve çıktıları kendileri gerçeklemez, bu işler içinyardımcı bir sınıfı görevlendirir, yani streambuf ile ondan türetilenleri.Açıklamalarımızı tamamlamak için şunu da ekleyelim; ios_base nesnesinidoğrudan inşa etmek mümkün değildir. ios_base nesneleri ios_basesınıfından türetilen sınıflar aracılığı ile inşa edilir. (ios_base::ios_baseyapıcı işlevlerini kullanarak).iostream hiyerarşisinde yeralan bir sonraki sınıf ios sınıfıdır. Akış (stream)sınıfları ios sınıfından türetildiği için (dolayısı ile ios_base den)uygulamada ios ile ios_base arasındaki farkı kavramak hayati önemdedir.Bundan dolayı, aslında ios_base tarafından sağlanan kolaylıklar ios sınıfkolaylıkları olarak incelenecektir.

streambuf nesnelerin arayüzü: ios sınıfı

ios sınıfı doğrudan ios_base sınıfından türetilir. Ve C++ dilinin bütün G/Çakış sınıflarının (stream sınıfları) altyapısını tanımlar.ios sınıf nesneleri doğrudan oluşturulabilmelerine rağmen, hemen hemenhiç uygulanmamıştır. ios sınıfının amacı; basic_ios sınıfının kolaylıklarıile beraber birçok yeni kolaylık ekleyerek, ios sınıf nesneleri tarafındanyönetilen streambuf nesnelerinin yönetimini sağlamaktır. Sağlanan

Page 39: C++ 2 (Kitap - Selami KOÇLU)

kolaylıkların tamamı streambuf yönetimi ile ilgilidir.Öbür akış sınıflarının tamamı, ya doğrudan ya da dolaylı olarak iossınıfından türetilir. Bunun anlamı; ios ve ios_base sınıfları tarafındansağlanan kolaylıkların hepsi, öbür bütün akış sınıfları için de geçerlidirdemektir. Şimdi ios sınıfının sağladığı kolaylıkları ele alalım.ios sınıfı çok sayıda üye işleve sahiptir. Bunların birçoğu biçemle ilgilidir.Diğer yaygın kullanılan üye işlevleri verelim:

streambuf *ios::rdbuf( ) ; Bu üye işlev, ios nesnesi ile araç arasında arayüz oluşturan streambufnesnesine göstergeç geri döndürür. Buradaki araç ios nesnesi ileiletişimdedir.

streambuf *ios::rdbuf(streambuf *new) ; Bu üye işlev ios nesnesini ikinci bir streambuf nesnesi ileilişkilendirmek amacı ile kullanılır. ios nesnesinin ana streambufnesnesine bir göstergeç döndürülür. Bu göstergecin gösterdiği nesne, akışnesnesi görüntü dışına çıktığı zaman silinmez, o zaman sahibi rdbuf( )işlevini çağırandır. ostream *ios::tie( ) ; Bu üye işlev ios nesnesine bağlanan ostream nesnesine göstergeç geridündürür. Dönen ostream nesnesi, tie( ) üye işlevin çağrıldığı iosnesnesine her bilgi girişinden veya çıkışından önce boşaltılır. 0 (sıfır)dönüş değeri ios nesnesine henüz ostream nesnesi bağlanmadığınıgösterir.

ostream *ios::tie(ostream *new) ; Bu üye işlev ile bir ios nesnesi ikinci bir ostream nesnesi ileilişkilendirilir. ios nesnesinin ana ostream nesnesine göstergeçdöndürülür.

Koşullar

Koşul, kendinden bool değeri üretilen program açıklaması demektir.Akışlar üzerinde yapılan işlemler birçok nedenle başarılı olamayabilirler.Akıştaki işlem başarılı olmadığı zaman, devam eden okuma ve yazmalarmuallakta kalır. Akışın koşul durumlarını bir programla incelemek olasıdır,böylece gereken tamirat yapılır ve program terkedilmemiş olur. Koşullar

Page 40: C++ 2 (Kitap - Selami KOÇLU)

aşağıdaki koşul bayrakları ile temsil edilirler;

ios::badbit : Bu bayrak yükseldiğinde, akış arayüzlerine streambuf seviyesindeuygunsuz bir işlemin istendiği anlaşılır.

ios::eofbit : Bu bayrak ortaya çıktığı zaman, ios nesnesi dosya sonuna gelindiğinianlar.

ios::failbit : Bu bayrak akış nesnesi tarafından gerçeklenen işlemin başarısızolduğunu gösterir. Bu durumda akış, kendinden istenen görevi yerinegetirememiş demektir.

ios::goodbit : Bu bayrak yükseldiğinde öbür üç bayrak inmiş demektir.

ios nesnelerinin durumlarını belirlemek veya değiştirmek için çok sayıdakoşul üye işlevleri bulunmaktadır.Bunlar;

ios::bad( ) : ios::badbit değeri 1 (bir) olduğunda bu üye işlev 1 (doğru=true)döndürür, aksi durumda 0 (yanlış=false) döndürür. Eğer doğru (true)döndürülmüşse akış arayüzlerine streambuf nesne seviyesinde uygunsuzbir işlemin istendiğini gösterir. Peki bunun anlamı ne?. streambufkendinden beklenmeyen şekilde davrandı demektir. Örneğe bakalım;

ostream::error(0) ;

Örnekte ostream nesnesi streambuf nesnesi ile çalışılmadanoluşturulmuştur. streambuf hiçbir zaman doğru çalışmadığında onunios::badbit i en baştan yükseltilir, böylece error.bad( ) 1 (=true)döndürür.

ios::eof( ) Dosya sonu algılandığı zaman (yani ios::eofbit yükseldiğinde) bu üyeişlev 1 (true) döndürür, aksi durumda 0 (false) döndürür.

Page 41: C++ 2 (Kitap - Selami KOÇLU)

ios::fail( ) : Bu üye işlev ios::eof( ) veya ios::bad( ) 1 (true) döndürdüğü zaman, 1(true) döndürür. Aksi durumda ise 0 (false) döndürür.

ios::good( ) : Bu üye işlev ios::goodbit bayrak değerini döndürür. Öbür koşulbayraklarının hiçbiri (ios::badbit, ios::eofbit, ios::failbit )yükselmediğinde bu 1 (true) döndürür. Aşağıdaki basit örneğe bakalım;

#include <iostream> #include <string> using namespace std ; void durum( ){ cout<<”\” “Bad: “<<cin.bad( )<<” “ “Fail: “<<cin.fail( )<<” “ “Eof: “<<cin.eof( )<<” “ “Good: “<<cin.good( )<<endl ; } int main( ){ string satir ; int x ; cin>>x ; durum( ) ; cin.clear( ) ; getline(cin, satir) ; durum( ) ; getline(cin, satir) ; durum( ) ; }///:~

Bu program iki satırdan oluşan bir dosyayı işlemlediğinde, (örnek: helloworld) ve ikinci satır \n ile sonlanmadığı zaman, aşağıdaki sonuçlar eledeedilir;

Bad:0 Fail:1 Eof:0 Good:0 Bad:0 Fail:0 Eof:0 Good:1 Bad:0 Fail:0 Eof:1 Good:0Böylece x girişi önce olmaz (good( ) 0 döndürür), daha sonra hata durumusilinir ve ilk satır başarılı bir biçimde okunur (good( ) 1 döndürür). Son

Page 42: C++ 2 (Kitap - Selami KOÇLU)

olarak ikinci satır okunur (tamamlanmadan):good( ) 0 döndürür, ve eof( ) 1döndürür.

**Akışların bool değerleri olarak yorumlanması: Akışlar beklentileri bool değeri olan açıklamalarda kullanılabilir.Örnek:

if(cin) //cin kendini burada bool değeri olarak yorumlamıştır. if(cin>>x) //cin girişinden sonra bool değeri olarak yorumlanmıştır. if(getline(cin, str) //getline cin döndürür

Bir akış bool, yani mantık değeri olarak yorumlandığı zaman, aslında not ios::fail( ) olarak yorumlanmış demektir. O zaman yukarıdakiörnekleri aşağıdaki şekilde yeniden yazmak mümkündür:

if(not cin.fail( )) if(not (cin>>x).fail( )) if(not getline(cin, str).fail( ))

Aşağıdaki üye işlevler ise hatalı durumları yönetirler.

ios::clear( ) Hatalı bir durum ortaya çıktığında, bu durum düzeltilebilir, bu yüzdendosyanın hatalı hali clear( ) işlevi çağrılarak temizlenebilir. Bindirilmişsürüm, durum bayraklarını işlemleyebilir, yani bayrakların o ankidurumlarını önce silinir sonra doğru değerleri yerleştirilir: ios::clear(int state)

ios::rdstate( ) Bu üye işlev bir ios nesnesinin o anki bayraklarını geri döndürür. Bellibir bayrağı sınamak için ikicil Ve (bitwise And) kullanılır:

if(iosObject.rdstate( ) & ios::good){ //durum iyi }

ios::setstate(int flags): Bu üye işlev belli bayrakların değerini 1 yapmak için kullanılır.Bilindiği gibi ios::clear( ) bütün bayrak değerlerini siler yani 0 (sıfır)

Page 43: C++ 2 (Kitap - Selami KOÇLU)

yapar. Tabii bayrakların sıfırlanması hatanın da otomatik silinmesianlamını taşımaz. Bunun için takip edilmesi gereken yol:--hatalı durum algılanmalı--hata düzeltilmeli--ios::clear( ) çağrılmalı C++ dilinde istisnai durumları yönetmek için istisna mekanizmasıbulunmaktadır. İstisnalar ANSI/ISO standartlarına göre akış nesneleri ilekullanılabilir. Bu konunun ayrıntıları istisnalar bölümünde verilecektir.

Girdi ve Çıktının biçimlendirilmesi

Bilginin akışa yazım (veya akıştan okuma) düzeni biçimlendirmebayrakları tarafından belirlenir. Biçimlendirme, çıktı alanının veya girditamponunun genişliğini denetlemek gerektiğinde kullanılır. Biçimlendirmegösterilecek değerin gösterilme biçimini belirlemekte de kullanılabilir(noktadan sonra kaç hane rakam kullanılacağı gibi). Biçimlendirmeninbüyük çoğunluğu çıktı akışları ile kullanılmasına rağmen, uygulanmasıbüyük oranda ios sınıfına aittir. Biçimlendirme ios sınıfı tarafındantanımlanan bayraklar tarafından düzenlendiği için, onu ios sınıfının kendisiile incelemek en iyi yoldur.Biçimlendirme, biçimlendirme bayrakları ile düzenlenir. Bu bayraklar ikiyolla değiştirilebilir: özel üye işlevler kullanarak veya doğrudan akış içineözelleştiriciler (manipulators) yerleştirerek. Özelleştiriciler doğrudan iossınıfına uygulanmaz, yerleştirme işleci kullanımı lazımdır. Birazdanayrıntıları vereceğiz.

Biçimlendirme bayrakları

Biçimlendirme bayraklarının büyük çoğunluğu bilginin çıktıyagönderilmesi ile ilgilidir. Bilgi çıktı akışlarına iki türlü yazılabilir; ikili(binary) çıktı bilgiyi doğrudan çıktı akışına yazar, bu tür insan tarafındanokunabilir bir biçem (format) değildir. Örnek olarak int değerinin 4 byteolarak yazılması gibi. İkinci tür olarakta insanların okuyabilmesi içinbiçimlendirilmiş çıktı, bilgisayar belleğinde byte halinde bulunan değerleriASCII kodlara çevirir. Biçimlendirme bayrakları bu çevrim biçimlerini düzenleyebilir. Örnekolarak çıktı akışına yazdırılacak karakter sayısını dentlemek gibi.Şimdi aşağıdaki biçimlendirme bayraklarını inceleyelim:

Page 44: C++ 2 (Kitap - Selami KOÇLU)

ios::adjustfield : Bayrak düzenleme ile kullanılan maske değeri, geniş alanda değerlerinayarlama biçimini tanımlar. (ios::left, os::right, ios::internal). Örnek; 10karakterli alanda 10 değerini sola göre düzenleme:

cout.setf(ios::left, ios::adjustfield) ; cout<<”'”<<setw(10)<<10<<”'”<<endl ;

ios::basefield Bayrak düzenleme ile kullanılan maske değeri çıktıya gönderilen intsayının kullanılacağı sayı tabanını belirler. (ios::dec, ios::hex, ios::oct).Örnek 55000 sayısının hex biçiminde yazılması;

cout.setf(ios::hex, ios::basefield) ; cout<<55000<<endl ; //veya özelleştirici kullanalım: cout<<hex<<55000<<endl ;

ios::boolalpha Bool değerleri metin olarak göstermek amacı ile kullanılır. Bu işlemyanlış mantık değerleri için 'false', doğru mantık değerleri için 'true'metinlerinin yazılması demektir. Varsayım olarak bu bayrak ayarlanmaz.Karşılık gelen özelleştiriciler; boolalpha ve noboolalpha. Örnek olarakbool değerini 1 yerine 'true' yazdıralım:

cout<<boolapha<<(1==1)<<endl ;

ios::dec Sayıları 10 tabanına göre okuyup yazmayı sağlar. Bu varsayım olarakher zaman bulunur. setf ile birlikte ios::basefield maske değeri mutlakasağlanmalıdır. Karşılık gelen özelleştirici; dec.

ios::fixed Bilimsel yazımların tersine sayıları sabit bir gösterimle belirtir. Örnek;12.34. Eğer gösterimde bir değişiklik istenirse setf( ) işlevi ile kullanıldığızaman mutlaka ios::floatfield maske değeri olmalıdır. Örnek olarakaşağıdaki ios::scientific bakınız. ios::fixed in başka bir kullanımı, yazdırılan float ve double sayılarınnoktadan sonraki hane sayısını belirlemektir. Aşağıdaki ios::precision a

Page 45: C++ 2 (Kitap - Selami KOÇLU)

bakınız.

ios::floatfield Bayrakla birlikte kullanılan maske değeri gösterilecek gerçek sayılarıdüzenler (ios::fixed, ios::scientific). Örnek;

cout.setf(ios::fixed, ios::floatfield) ;

ios::hex: Sayıları hex (16 tabanına göre) olarak okuyup yazmayı düzenler. setf( )işlevi ile birlikte mutlaka ios::basefield maske değeri sağlanmalıdır.Karşılık gelen özelleştirici; hex.

ios::internal: Negatif sayıların eksi işareti ile sayının kendisi arasına doldurucukarakterler ekler. (varsayım olarak burası boştur). setf( ) işlevi ile birliktemutlaka adjustfield maske değeri sağlanmalıdır. Karşılık gelenözelleştirici; internal.

ios::left: Değerleri gösterirken alanlar gerekenden fazla olduğunda sola yaslama.Varsayım ise sağa yaslar. setf( ) işlevi ile birlikte adjustfield maske değerimutlaka temin edilmelidir. Karşılık gelen özelleştirici; left.

ios::oct: Sayıları oktal olarak (8 tabanına göre) göstermek içindir. setf( ) işlevi ilebirlikte ise mutlaka ios::basefield sağlanmalıdır. Karşılık gelenözelleştirici; oct.

ios::right: Değerleri gösterirken alanlar gerekenden fazla olduğunda sağa yaslama.Bu ayar varsayım olarak kabul edilir. setf( ) işlevi ile birlikte ikenadjustfield maske değeri mutlaka sağlanmalıdır. Karşılık gelenözelleştirici; right.

ios::scientific:

Gerçek değerleri bilimsel biçimde (örnek: 1.23+e02) göstermek içinkullanılır. setf( ) işlevi ile birlikte iken floatfield maske değeri mutlaka

Page 46: C++ 2 (Kitap - Selami KOÇLU)

temin edilmelidir. Karşılık gelen özelleştirici; scientific.

ios::showbase: Sayıların belirtildiği taban değerleri gösterir. Heksadesimal tabanlarda0x öneki, oktal tabanlarda O öneki kullanılır. Varsayım olduğu için ontabanlı sayılarda herhangi bir önek kullanılmaz. Karşılık gelenözelleştiriciler; showbase ve noshowbase dir.

ios::showpoint: Gerçek sayılar gösterilirken ondalık noktasını ve takip eden sıfırlarıbelirtir. Bu bayrak 1 yapıldığı zaman, bir çıktı;

cout<<15.0<<”, “<<15.1<<”, “<<15<<endl ;

sonuç: 15.0000 15.1000 15

Son sayı gerçek sayıdan ziyade bir int tiptir, ondalık noktasıgösterilmemiştir. Bundan dolayı ios::showpoint etkisi olmaz.ios::showpoint kullanılmazsa takip eden sıfırlar otomatik olarak göz ardıedilir. Karşılık gelen özelleştirici; showpoint.

ios::showpos: Pozitif sayılarda + işaretinin gösterilmesini sağlar. Karşılık gelenözelleştirici; showpos.

ios::skipws: Akışlardan bilgi elde edilmesi için kullanılır. Bu bayrak 1 olduğu zaman(varsayılan budur) öndeki boşluklar (blanks, newlines, tabs vs) akıştan birdeğer elde edilene kadar atlanır. Bayrak eğer 1 değilse o zaman bu beyazboşluklar atlanmaz.

ios::unitbuf: Her çıktı işleminden sonra akışı yeniden dolması için boşaltmak.

ios::uppercase:

Değerleri büyük harflerle göstermek (Hex veya bilimsel biçimli olanlarda)

Page 47: C++ 2 (Kitap - Selami KOÇLU)

Biçem değiştiren üye işlevler

G/Ç ları biçimlendirmek çok sayıda üye işlev bulunmaktadır. Çoğu kere debunlara karşılık gelen özelleştiriciler vardır. Yerleştirme ve elde etmeişleçleri kullanılarak doğrudan akışlara yerleştirme veya akışlardan eldeetme gerçekleştirilir. Bunlar daha ileride anlatılacak. Üye işlevler;

ios::copyfmt(ios &obj): Bu üye işlev bütün biçem tanımlarını obj dan o anki ios nesnesinekopyalar.

ios::fill( ) const: O anki doldurma karakterini char olarak döndürür. Varsayılan iseboşluktur.

ios::fill(char padding): Doldurma karakterini yeniden tanımlar. Bir önceki doldurma karakterinichar olarak döndürür. Karşılık gelen özelleştirici; setfill( ).

ios::flags( ) const: Çağrılan üye işlevin, akış biçem durumlarını denetleyen o an kibayraklarını döndürür. Belli bir bayrağı incelemek için ikili sayı sistemi veişleçler kullanılmalıdır. Örnek;

if(cout.flags( ) & ios::hex){ //hex çıktı sayıları }

ios::flags(fmtflags flagset): Önceki bayrakları döndürür ve o anki bayrakları flagset olarak tanımlar.Bunlar iki tabanlı sayılar ve işleçlerle birleştirilmiş olup biçimlendirmebayraklarınca tanımlanmışlardır. Burada hemen şunu belirtelim; bu üyeişlevi kullanarak bayrakları düzenlemek için, önceki bayrak değerlerininortadan kaldırılması gerekir. Örnek olarak bu üye işlevi, cout nesnesindeon tabanlı sayıdan onaltı tabanlı sayıya çevriminde kullanalım;

cout.flags(ios::hex | fis::flags( ) & ~ios::dec) ;

Veya aşağıdaki seçeneklerden herhangi bir olabilir;

Page 48: C++ 2 (Kitap - Selami KOÇLU)

cout.setf(ios::hex, ios::basefield) ; cout<<hex ;

ios::precision( ) const: Gerçek sayı değerini çıktıya gönderebilmek için sayıda yer alan rakamsayısını (varsayılan 6) döndürür. (int olarak).

ios::precision(int signif): Gerçek sayı değerini çıktıya gönderebilmek için sayıda yer alan rakamsayısını yeniden tanımlar ve önceki sayıda yer alan rakam sayısınıdöndürür. (int olarak). Karşılık gelen özelleştirici; setprecision( ). Örnek;on tabanlı sayıyı noktadan sonra hep 4 haneli olarak olarak yazdırmak.

cout.setf(ios:.fixed) ; cout.precision(4) ; cout<<4.0<<” “<<4.01<<” “<<4.001<<” “<<4.0001<<endl ; cout<<4.00005<<” “<<4.00006<<” “<<4.00007<<endl ;

4.00005 sayısı 4.0001 sayısına yuvarlanır. Aynı şekilde -4.00005 sayısıda -4.0001 sayısına yuvarlanır.

ios::setf(fmtflags flags): Bir önceki bayrakların hepsini döndürür ve bir veya daha fazla bayrağıbelirler. (ilgili bayraklar bit düzenli | ( ) işleçler ile biraraya getirilir, diğerbayraklar etkilenmez). Karşılık gelen özelleştiriciler; setiosflags veyaresetiosflags.

ios::setf(fmtflags flags, fmtflags mask) ; Önceki bütün bayrakları döndürür, mask değişkenindeki bayrakları silerve flags te belirtilenleri ise düzenler. En iyi bilinen mask değerleri;ios::adjustfield, ios::basefield ve ios::floatfield tir. Örnek;

setf(ios::left, ios::adjustfield) kendi bölgelerinde sola yaslı genişdeğerlerde kullanılır. (ios::right ve ios::internal başka seçenekler olarakkullanılabilir.) setf(ios::hex, ios::basefield) sayıların onaltı (hex) tabanına göregösterimi sağlanır. (ios::dec ve ios::oct diğer seçenekler olabilir.) setf(ios::fixed, ios::basefield) gerçek sayıların sabit sayıda ondalıkla

Page 49: C++ 2 (Kitap - Selami KOÇLU)

yazılmasını sağlar. (ios::scientific başka bir seçenektir.)

ios::unsetf(fmtflags flags) : Önceki bayrakların tümünü döndürür, belirli bayrakları siler ve gerikalanları değiştirmeden bırakır. Varsayılan etkin bayrağın değerinindüzenlenmemesi herhangi etki oluşturmaz. Yani örnek;cout.unsetf(ios::dec).

ios::width( ) const : O anki çıktı alan genişliğini int olarak döndürür. Varsayılan ; 0, yanideğeri yazmak için gerektiği kadar karakter anlamını taşır. Karşılık gelenözelleştirici; setw( ).

ios::width(int nchars) : Bir önce kullanılan çıktı alan genişliğini int olarak döndürür. Bir sonrakiçıktı işlemi için nchar a değeri yeniden tanımlar. Her çıktı işlemindensonra alan genişliği 0 a ayarlanır ve width( ) işlevinin char* veya stringmetinlerde herhangi bir etkisi olmaz. Karşılık gelen özelleştirici; setw(int).

ÇIKTI

C++ çıktısı temelde ostream sınıfına dayanır. ostream sınıfı bilgiyi akışayerleştirmek için gereken işleç ve üyeleri tanımlar; yerleştirme işleci (<<)ve ostream::write( ) gibi özel üyeler ile akışlardan biçimlenmemiş bilgiyiyazdırmak için kullanılırlar.ostream sınıfından çok sayıda başka sınıf türetilir; bu sınıflar kendiözellikleri ile birlikte ostream sınıfının işlevikliğini de taşırlar. Çıktıüzerine olan, bir sonraki bölümde; ostream sınıfının çıktı için sağladığı kolaylıklar. ostream sınıfının yazmak için dosya açması. (C deki fopen(filename,“w”) ile karşılaştırılabilir) ostringstream sınıfının dosya (akışlar) yerine bilgiyi belleğe yazması. (Cdeki sprintf( ) işlevi ile karşılaştırılabilir). Anlatılacktır.

Ana çıktı: ostream

Temel çıktı araçlarını tanımlayan sınıf ostream sınıfıdır. cout, cerr ve clog

Page 50: C++ 2 (Kitap - Selami KOÇLU)

nesneleri hep ostream sınıfının nesneleridir. Burada hemen belirtelim;kalıtım dolayısı ile çıktı ile ilgili ios sınıfında tanımlanmış ne kadarkolaylık varsa, aynen ostream sınıfında da bulunur. Ostream nesnelerini aşağıdaki ostream yapıcı işlevi ile oluşturabiliriz;

ostream object(streambuf* sb) ;

Bu yapıcı işlev önceden varolan bir dosyaya arayüz olabilecek, var olanstreambuf etrafında örtücü (wrapper) inşa etmek için kullanılabilir.Buradan ortaya çıkan; basit bir ostream nesnesinin inşa edilmesi mümkündeğildir. cout veya onun benzerleri kullanıldığı zaman, aslında bizim içinoluşturulmuş olan öntanımlı ostream nesnesini kullanıyoruz, yani asılarayüzün streambuf (öntanımlı) nesnesini kullanan (örnek olarak) standartçıktı akışının arayüzü.C++ da ostream sınıfını kullanmak için #include <ostream>önişlemleyici yönergesi mutlaka programa eklenmelidir. Öntanımlıostream nesnelerini kullanmak için ise, #include <iostream>önişlemleyici yönergesi mutlaka ilave edilmelidir.

ostream nesnelerine yazım

ostream sınıfı hem biçimlenmiş, hem de ikici (binary) çıktı düzenlerinidestekler. Yerleştirme işleci (<<) ostream nesnesine değerleri tip güvencesindeyerleştirir. Biçimlenmiş çıktı; bilgisayar belleğinde bulunan ikicidüzendeki değerlerin, belli biçimleme kurallarına uyarak insanınokuyabileceği ASCII kodlarına çevrilmesidir.Yerleştirme işleci (<<) yerleştirilecek olan bilgiyi ostream nesnesine işareteder. İşlecin (<<) bağlantısı her halükarda değişmeden kalır. Aşağıdaki birdeyimle karşılaşılırsa;

cout<<”merhaba”<<”dunya” ;

önce en soldaki iki öğe (cout<<”merhaba”) işlemlenir, ve ostream&object yani asıl cout nesnesi geri döner, artık şimdi deyim

cout<<”dunya” ;

ya indirgenmiş olur ve ikinci string te (dunya) çıktıya gönderilir.

Page 51: C++ 2 (Kitap - Selami KOÇLU)

(<<) işlecinin çok sayıda bindirilmiş çeşidi bulunur. Bu sayede çok sayıdadeğişken tipi (int, char, string, double v.s.) ostream nesnesinegönderilebilir.Aşağıdaki ostream nesne üye işlevleri ikili (binary) dosyaları yazmaktakullanılabilir;

ostream& ostream::put(char c): Bu üye işlev çıktı akışına tek bir karakter yazar. Bu karakter bir baytolduğu için bu üye işlev ayrıca bu karakteri metin dosyasına yazımda dakullanılabilir.

ostream& ostream::write(char const* buffer, int length) : Bu üye işlev char const* buffer da saklı en çok len baytı yazar, aynıtamponda saklı oldukları şekilde biçem değişikliği olmadan yazılır. Yazımbiçiminden göreceğiniz gibi ilk değişken char const*, bu yüzden ötekitiplerde yazım için tip dökümü gerekir. Örnek olarak int değişkeni baytdeğerlerin biçemsiz düzeninde yazarsak;

int i ; out.write(reinterpret_cast<char const*>(&i), sizeof(int)) ;

ostream yerleşim düzenleme

Yeniden yerleşimi her ostream nesnesi düzenlememesine rağmen, çoğubu işi yapar. Bunun anlamı; daha önceden yazılmış olan akışın bir bölümüyeniden yazılabilir. Veritabanındaki veriye gelişigüzel erişmek mecburiyetizorunlu olduğu yerlerde, yeniden yerleşim düzenleme veritabanlarındasıklıkla kullanılır.Aşağıdaki üyeler yerlaşim düzenlemelerde kullanılır;

pos_type ostream::tellp( ): Bu işlev o an bulunulan yeri (mutlak) döndürür, akışa yazılacak birsonraki yerde ortaya çıkar. Uygulamada pos_type, unsigned long olarakdüşünülebilir.

ostream& ostream::seekp(off_type step, ios::seekdir org) : Bu üye işlevi akışın yerleşimini yeniden düzenlemek için kullanabiliriz.İşlev org tan gidecek olan baytlar halindeki adım büyüklüğünü, yani biroff_type step bekler. off_type pratikte long olarak alınabilir. step in

Page 52: C++ 2 (Kitap - Selami KOÇLU)

kaynağı olan org, ios::seekdir değeridir. Olası değerleri ise aşağıdaverilmiştir;

ios::beg : org akışın başlangıcına göre adım büyüklüğü olarak yorumlanır. orgbelirlenmemişse ios::beg kullanılır.

ios::cur : org o anki yere göre (akışın tellp( ) işlevi tarafından döndürülür) adımbüyüklüğü olarak yorumlanır.

ios::end : org akışın o an ki son yerine göre adım büyüklüğü olarak yorumlanır.Dosya sonunun ötesi de aranabilir. EOF tan öteye bayt yazmak ASCII-Zdeğerlerin yerleştirilmesine yolaçar: boş baytlar (null-bytes). Dosyabaşından önce arama yapmaya izin verilmez. ios::beg ten önce aramayapılırsa ios::fail bayrağı 1 olur.

ostream boşaltma

ios::unitbuf bayrağı 1 değilse ostream nesnesine yazılı bilgi fiziki akışahemen yazılmaz. Yazma işlemleri daha ziyade iç tampona yapılır, bu dadolduğu zaman boşaltılır.İç tampon program denetiminde boşaltılır.

ostream& ostream::flush( ) : Bu üye işlev tampondaki bilgiyi ostream nesnesine yazar. flush( ) işlevi --çıkışa son verir. --ostream nesnesine flush veya endl özelleştiricileri yerleştirir. --ostream kökenli (ofstream gibi) akış kapatılır.anlamlarını taşır.

Dosyalara çıktı: ofstream sınıfı

ofstream sınıfı ostream sınıfından türetilmiştir. Bu yüzden ostreamsınıfının yetneklerine sahiptir, ek olarakta dosyalara erişir ve yazmak içindosyalar açabilir.C++ programlarında ofstream sınıfını kullanabilmek için, önişlemleyiciyönergelerine #include <fstream> mutlaka eklenmelidir. fstream

Page 53: C++ 2 (Kitap - Selami KOÇLU)

eklenince cin cout gibi nesneler otomatik olarak bildirilmiş olmaz onlariçin iostream sınıfı eklenmelidir.ofstream sınıfının yapıcı işlevleri şunlardır;

ofstream nesne : Temel yapıcı işlevdir. Daha sonra karşılaşılacak olan dosya ile ilgiliofstream nesnesi oluşturur. open( ) üye işlevi burada kullanılabilir.

ofstream nesne(char const* name, int mode): Bu yapıcı işlev, mode çıktı modunu kullanarak name adındaki dosyayıofstream nesnesine ilişkilendirmek için kullanılır. Çıktı modu ios::outolarak varsayılır. Öbür çıktı modları ise ileride anlatılacak.Aşağıdaki örnekte, yeni oluşturulmuş bir dosya (/tmp/yeni/), ofstream outnesnesi ile ilişkilendirilerek inşa edilmiştir.

ofstream out(“/tmp/yeni/”) ;

Burada hemen belirtelim bir ofstream i dosya betimleyici (file descriptor)ile açmak mümkün değildir. Bunun nedeni; her işletim sisteminde dosyabetimleyicilerin bulunma zorunluluğu olmamasıdır. Allahtan dosyabetimleyiciler (dolaylı olarak) streambuf nesneleri ile kullanılabilirler(bazı uygulamalarda filebuf nesneleri ile de, böylece dolaylı olarakstreambuf nesneleri ile). Bunlarda birazdan anlatılacak.Dosyayı doğrudan ofstream nesnesine ilişkilendirmek yerine, nesne önceinşa edilir ve daha sonra açılır.

void ofstream::open(char const* name, int mode, int perm): ofstream nesnesi inşa edilirken asıl dosya ile ofstream nesnesi, open( )üye işlevi kullanılarak ilişkilendirilir.

ofstream::close( ) : Önceki işlemin tersine bu sefer ofstream nesnesini kapatmak için close( ) üye işlevi kullanılır. İşlev, kapanan nesnenin ios::fail bayrağını 1yapar. Dosyanın kapanması tamponlanmış bilgiyi ilgili dosyaya boşaltır.İlgili akış nesnesi çıkışı kestiği zaman bir dosya otomatik olarak kapanır.Bu ayrıntıdaki incelik aşağıda verilmiştir; aslında bir dosyayailiştirilmemiş olan bir akış inşa ettiğimizi düşünelim, Örnek; ofstream ostrnin çalıştığı kabul edildi. Şimdi duruma good( ) ile bakalım , sıfır olmayanbir değer döner.(yani herşey tamam). Buradaki good( ) durumu ile, akış

Page 54: C++ 2 (Kitap - Selami KOÇLU)

nesne inşasının başarılı olduğunu gösterir. Ama bu, dosyanın ayrıcaaçılmış olduğu anlamına gelmez. Akışın aslında açık olup olmadığınısınamak için ofstream::is_open( ) kullanılmalı. Bu sınama sonucu doğruise (true), o zaman açık demektir. Örnek;

#include <iostream> #include <fstream> using namespece std ; int main( ){ ofstream of ; cout<<”of un acikdurumu ”<<boolalpha<<of.is_open( )<<endl ; of.open(“/dev/null/”) ; //Linux sistemlerinde cout<<”of un acik durumu<<of.is_open( )<<endl ; }///:~

//çıktı: of un acik durumu false of un acik durumu true//

Akış nesnelerini açan modlar

Aşağıdaki dosya modları veya dosya bayrakları ofstream nesneleriniaçmak veya inşa etmek için tanımlanmışlardır. Değerler ios::openmodetipindedir.

ios::app : Her çıktı komutundan önce dosya sonuna yeniden yerleşir. Varolandosya içeriği korunur.

ios::ate : Önce dosya sonunda başlatır. Varolan dosya içeriği korunur. ios::binary : İkici bir dosya açar. (metin ve ikici sistemler arasında fark oluşturmakiçin kullanılır MS-DOS ile MS_WINDOWS benzeri gibi)

ios::in : Okumak için dosya açar. Dosya mutlaka önceden bulunmalıdır.

Page 55: C++ 2 (Kitap - Selami KOÇLU)

ios::out : Dosya açar. Dosya yoksa dosyayı oluşturur. Dosya varsa yenidenyazılır.

ios::trunc : Başlangıçta boş dosya ile başlar. Dosyanın önceden varolan içeriğikaybolur.

Aşağıdaki dosya bayrak bileşimleri özel anlamlar taşır :

out | app : Yoksa dosya oluşturulur, bilgi her zaman akış sonunayerleştirilir.

out | trunc : Dosya yazım için boş olarak (veya yeniden) oluşturulur.

in | out : Akış okunur ve yazılır. Bunun yanında, dosya mutlakaolmalıdır.

in | out | trunc : Akış okunur ve yazılır. Önce boş olarak (veya yeniden)oluşturulur

Belleğe çıktı: 'ostringstream' sınıfı

Belleğe bilgi yazmak için stream unsurları kullanılır. Bunlardanostringstream nesneleri kullanılabilir. Nesneler ostream nesnelerindentüretilir. Sağlanan yapıcı işlevler ve üyeler aşağıda verilmiştir;

ostringstream ostr(string const &s, ios::openmode mode) : Bu yapıcı işlev kullanıldığı zaman, son veya her iki değişken atlanabilir.Bu yapıcı işlev ayrıca sadece openmode değişkene sahiptir. Stringbelirtilmiş ve ios::ate ise, ostringstream nesnesi string s ile başlatılır vegeri kalan yerleştirmeler ostringstream nesne içeriğine konur. String ssağlanmışsa, başka seçeneği olmaz, nesneye konmuş herhangi bir bilgidinamik yerleşimli bellekte saklanır, ostringstream nesnesi görüntü dışınaçıktığında silinir.

string ostringstream::str( ) const : Bu üye işlev ostringstream nesnesinde saklı stringi döndürür.

Page 56: C++ 2 (Kitap - Selami KOÇLU)

ostringstream::str(string) : Bu üye işlev ostringstream nesne içeriğini yeni değeri ile yenidenbaşlatır.

ostringstream sınıfının kullanımlarını aşağıdaki örneklerde göreceksiniz;değişik değerleri nesneye saklayacaksınız. Daha sonra saklı metin stringeyerleştirilecek, uzunluğu ve içeriği yazdırılacak. Bu tür ostringstreamnesneleri çoğu kez tipi stringe çevirme yaparken kullanılır. Biçimlendirmekomutları ostream nesneleri için olduğu kadar, stringstream ler için dekullanılabilir.İşte ostringstream nesne kullanımını gösteren bir örnek;

#include <iostream> #include <string> #include <sstream> #include <fstream> using namespace std ; int main( ){ ostringstream ostr(“hello”, ios::ate) ; cout<<ostr.str( )<<endl ; ostr.setf(ios::showbase) ; ostr(ios::hex, ios::basefield) ; ostr<<12345 ; cout<<ostr.str( )<<endl ; ostr<<”--” ; ostr.unsetf(ios::hex) ; ostr<<12 ; cout<<ostr.str( )<<endl ;}///:~

//hellohello 0x3039hello 0x3039 –12//

GİRDİ

Page 57: C++ 2 (Kitap - Selami KOÇLU)

C++ dilinde girdi işlemleri, istream sınıfına dayanır. istream sınıfıakışlardan bilginin elde edilmesini sağlayan işleç ve üyeleri tanımlar. Eldeetme işleci (>>) ve istream::read( ) benzeri özel işlevler ile akışlardanbiçimlenmemiş bilgi okunur.istream sınıfından çok sayıda yeni sınıf türetilir, bunların hepsi istreamsınıfının işlevikliğine ve kendilerine özgü başka özelliklere sahiptir.Birazdan

**istream sınıfının girdiyi gerçekleştiren yeteneklerini**Dosya okumak için açılan ifstream sınıfını **Dosyada saklanmayan bellekteki metinleri okumak için istringstreamsınıfı, ayrıntıları ile incelenecek.

Ana girdi; 'istream' sınıfı

Temel G/Ç yeteneklerini tanımlayan sınıf istream sınıfıdır. Kaynakkodlarda görünen #include <iostream> önişlemleyici yönergesi ileistream nesnesi olan cin nesnesi bildirilir. Kalıtımdan dolayı girdi ile ilgilibütün ios sınıf tanımları aynen istream sınıfı içinde geçerlidir.istream sınıf nesneleri aşağıdaki istream yapıcı işlev ile inşa edilir.

istream nesne(streambuf* sb)

Bu yapıcı işlev varolan streambuf tan kaynaklanan, varolan açık akışaörtü inşa etmek için kullanılabilir ve bu da dosyalara arayüz olur.

C++ kaynak kodlarında istream sınıfını kullanabilmek için, #include <istream> önişlemleyici yönergesi programa eklenmelidir.Öntanımlı istream sınıf nesnesi olan cin i kullanmak için ise, #include <iostream> önişlemleyici yönergesi eklenmelidir.

istream nesnelerinden okuma

istream sınıfı hem biçimlenmiş hem de biçimlenmemiş ikici (binary)girdiyi destekler. Elde etme işleci (operator>>( )), istream nesnelerindendeğerlerin tip güvenliği ile elde edilmesini sağlar. Biçimlenmiş girdiinsanların okuyabileceği ASCII kodlardan, belli biçimleme kurallarınauyularak, bilgisayar belleğinde saklanan ikici (binary) değerlere çevrilmesi

Page 58: C++ 2 (Kitap - Selami KOÇLU)

demektir. Elde etme işleci (>>) yeni değerler almak zorunda olan nesneleri veyadeğişkenleri gösterir.Aşağıda bulunan deyimle karşılaşıldığında

cin>>a>>b ;

(>>) işlecinin anlamı hiçbir zaman değişmez yani önce en soldakiler önceişlemlenir (cin>>a), sonra cin nesnesinin aynı olan istream& nesne sinedönülür, Böylece deyim aşağıdaki hale indirgenir;

cin>>b ;

ve daha sonra b elde edilir. Elde etme işlecinin (>>) çok sayıda bindirimli türü bulunur ve çok sayıdadeğişken tipi istream nesnesinden elde edilebilir. Bu bindirimli türlerarasında yerleşik olan tipler için olduğu kadar kullanıcı tipler de bulunur.Akışlarda biçimlenmiş girdiler için kolaylıklar bulunmaz. (yani C dekiscanf( ) ve vscanf( ) işlevleri C++ da bulunmaz) . Akışlar dünyasındabulunmayan anılan kolaylıkları sağlamak o kadar zor olmasa da -scanf( )benzeri işlevler C++ programlarında hemen hemen hiç gerekmez. Daha daötesi aslında, tip güvencesi sağlanmaması yüzünden bu işlevlerdensakınmak en iyisidir. İkici (binary) bir dosya okunmak zorunda olduğunda, bilginin normaldebiçimlenmemiş olması lazımdır; bir int değer ardışık baytlar dizisi olarakokunur, yani ASCII 0-9 sayı değerleri olarak okunmaz. istreamnesnelerinden bilgileri okumak için aşağıdaki üye işlevler kullanılır;

int istream::gcount( ) ; Bu işlev girdi akışından aslında okuma yapmaz, sadece sonbiçimlenmemiş girdi işlemi sırasında girdi akışından yapılmış olanokumadaki karakter sayısını döndürür.

int istream::get( ) ; EOF (dosya sonunu) döndürür veya bir sonraki tek karakteri int olarakokur ve döndürür.

istream& istream::get(char& c) ; Bir sonraki tek karakteri girdi akışından c ye okur. Dönüş değeri akışın

Page 59: C++ 2 (Kitap - Selami KOÇLU)

kendisidir, dönüş değeri eldenin başarılı olup olmadığını sınamak içinsorgulanabilir.

istream& istream::get(char* tampon, int len [,char delim]); Bu işlev len-1 karakteri girdi akışından okuyup tampondan başlayandiziye okur. Dizi en azından len uzunluğunda olması lazımdır. En fazlalen-1 karakter tampona okunur. delim varsayım olarak yeni satırkarakteridir ('\n'). Sınır kaldırıcı (delim) girdi akışından kendi kendisinikaldıramaz.Karakterler tampona okunduktan sonra, tampona son karakterden sonrakiyere bir ASCII-Z karakteri yerleşir. len-1 karakter okunmadan önce sınırkaldırıcı (yeni satır= '\n') ya rastlanmazsa, eof( ) ve fail( ) işlevleri 0değrini döndürür. Daha da ötesi bir ASCII-Z karakter sınır kaldırıcı olarakkullanılabilir; Bu yolla ASCI-Z karakteri ile sonlanan string ler ikili(binary) dosyalardan okunabilir. Bu get işlevini kullanan program kaç tanekarakter okunacağını peşinen bilmesi lazımdır.

istream& istream::getline(char* tampon, int len [, char delim]) ; Bu işlev önceki işlevle benzer davranış gösterir. Yalnız gerçekten sınırkaldırıcı ile karşılaştığında, delim akıştan kaldırılır. En fazla len-1 bayttampona yazılır ve takip eden ASCII-Z karakteri okunan string sonunaeklenir. Sınır kaldırıcı kendisini tampona saklamaz. Sınır kaldırıcıya(delim) rastlanmazsa (len-1 karakter okunmadan önce) o zaman fail( ) veolasıdır ki eof( ) işlevleri true (=1) döndürür. Burada hemen belirtelimstd::string sınıfı da getline( ) işlevine destek vermektedir. Ayrıca budestek şimdi anlattığımız istream::getline( ) işlevinden daha fazlakullanılmakta ve bilinmektedir.

istream& istream::ignore(int n, int delim) ; Bu işlevde iki tane (seçenekolarak) değişken bulunur. Eğer değişkensizçağrı olursa girdi akışından bir (1) karakter atlanır. Bir değişken ileçağrılırsa n karakter atlanır. Seçenek olan ikinci değişken sınır kaldırıcıdır;n atlamadan veya sınır kaldırıcıdan (delim) sonra (hangisi önce gelirse)işlev döner.

int istream::peek( ) ; Bu işlev bir sonraki karakteri döndürür, ama girdi akışından karakterigerçekten kaldırmaz.

Page 60: C++ 2 (Kitap - Selami KOÇLU)

istream& istream::putback(char c) ; Akıştan son olarak okunmuş olan c karakteri bir sonraki karakter olaraktekrar okunmak için girdi akışına 'geri itilir'. Eğer izin verilmezse EOF(dosya sonu) döner. Normalde her zaman tek karakter geri itilir. Buradahemen belirtelim; c mutlaka akıştan okunan son karakter olmakzorundadır. Başka bir karakteri geri itmeye çalışmak mümkün değildir.

istream& istream::read(char* tampon, int len); Bu işlev girdi akışından (en fazla) len baytı tampona okur. EOF a dahaönce rastlanırsa daha az bayt okunur, ve eof( ) üye işlevi true (1)döndürür.Aslında bu işlev ikili (binary) dosyaları okumak için kullanılır.ifstream sınıfını anlatan bölümde bu üye işlevle ilgili örnek verilecek.gcount( ) üye işlevi read( ) üye işlevi tarafından sağlanmış olan karaktersayısını saptamada kullanılır.

istream& istream::readsome(char* tampon, int len) ; Bu işlev girdi akışından (en fazla) len baytı tampona okur. Var olanbütün karakterler tampona okunur. Ama EOF a daha önce rastlanırsa dahaaz bayt okunur. Bunlar ios_base::eofbit veya ios_base::failbit 1(=set)yapışmadan gerçeklenir.

istream& istream::unget( ) ; Akışa okunmuş olan son karakteri geri itmek amacı ile yapılan eylem.Normalde eğer okuma işleminden sonra sadece bir kez istenirse başarılıolur. Tabii putback( ) işlevi ile.

'istream' yeri düzenleme

Her istream nesnesi yeniden yer düzenleme yeteneğine sahip olmasa da,bazıları bunu yapabilir. Bu şu anlama gelir; akışın aynı bölümünü tekrartekrar okumak mümkündür. Yer düzenleme (veya yer belirleme), bilgileregelişi güzel erişmek zorunluluğu olduğunda, veri tabanı uygulamalarındasıklıkla kullanılır. Bu işlere uygun üyeleri inceleyelim;

pos_type istream::tellg( ) ; Bir sonraki okuma işleminin akışa yapıldığı o anki (mutlak yer) yeridöndürür. Bütün pratik uygulamalarda pos_type, unsigned long olarakalınır.

Page 61: C++ 2 (Kitap - Selami KOÇLU)

istream& istream::seekg(off_type step, ios::seekdir org) ; Bu üye işlev, akışın yeniden yerleşiminde kullanılabilir. İşlev off_typestep i bekler, org tan adım büyüklüğü bayt olarak gider. Bütün pratikuygulamalarda burada pos_type, long olarak düşünülebilir. Step inkaynağı olan org, ios::seekdir değeridir. Olası değerleri ise;

ios::beg ; org akış başlangıcına göre adım büyüklüğü olarak yorumlanır. Eğer orgbelirtilmemişse o zaman ios::beg kullanılır.

ios::cur ; org o anki yere göre adım büyüklüğü olarak yorumlanır. (akış tellg( )tarafından döndürülür).

ios::end ; org, akışın o anki son yere göre adım büyüklüğü olarak yorumlanır.

Dosya sonundan öteye okumaya kalkıldığında, doğal olarak okumagerçekleşmez. Dosya başlangıcından önce aramaya izin verilmez. ios::begten önce arama, ios::fail bayrağını 1 (set) yapar.

Akışlardan girdi; 'ifstream' sınıfı

ifstream sınıfı istream sınıfından türetilmiştir. istream sınıfınınyeteneklerine sahiptir. Ama sadece erişilen dosyaları okumak içinkullanılabilir. Bunun için böyle dosyalar mutlaka bulunmalıdır.ifstream sınıfını C++ kaynak kodlarında kullanabilmek için, mutlaka#include <ifstream> önişlemleyici yönergesi eklenmelidir.ifstream nesneleri için aşağıdaki yapıcı işlevler kullanılabilir;

ifstream nesne; Temel yapıcı işlevdir. open( ) üyesini kullanarak, daha sonraki asıldosya ile ilişkili nesneyi oluşturur.

ifstream nesne(char const* name, int mode) ; Bu yapıcı işlev mode girdi modunu kullanarak, bir ifstream nesnesininame adındaki dosya ile ilişkilendirir. Girdi modu varsayım olarak ios::indir. (diğer modlar için; akış nesnelerini açan modlar bölümünü

Page 62: C++ 2 (Kitap - Selami KOÇLU)

inceleyebilirsiniz.) Aşağıdaki örnekte okumak için bir ifstream nesnesiaçılır. Yalnız böyle bir dosya mutlaka olmalıdır.

ifstream in(“/tmp/ilkten”) ;

ifstream nesnesini dosya ile doğrudan ilişkilendirmek yerine, öncenesne oluşturulup sonra da açılabilir.

void ifstream::open(char const* name, int mode, int perm) ; ifstream nesnesi oluşturulurken open( ) üye işlevi ifstream nesnesi ileasıl dosyayı ilişkilendirir.

ifstream::close( ) ; Açmanın tersine, bir ifstream nesnesi close( ) üye işlevi ile kesin olarakkapanabilir. İşlev, kapanan nesnenin ios::fail bayrağını 1 (set) yapar. Birdosya, ilişkili ifstream nesnesi çıkmak için sonlandığında, otomatik olarakkapanır.Anlatacağımız inceliğe dikkat edin; bir akışın inşa edildiğini kabul edelim,ama herhangi bir dosya ile ilişkili olmasın, Örnek; ifstream ostr deyimiçalıştırılmış olsun. good( ) işlevi ile durumuna baktığımızda, sıfır olmayanbir değer döner (örnek olsun diye). Buradaki good( ) durumu bize akışındüzgün inşa edildiğini gösterir. Dosyanın açıldığını göstermez. Akışıngerçekten açılıp açılmadığını sınamak için; ifstream::is_open( ) kullanılır.Bunun sonucu true (doğru) ise açılmış demektir.İkili (binary) dosyadan okuma örneği için; aşağıdaki örnekte bir doubledeğer ikili biçimde dosyadan okunmakta.

#include <fstream> using namespace std ; int main(int argc, char** argv){ ifstream f(argv[1]) ; double d ; //binary biçimde doble değer okunur f.read(reinterpred_cast<char*>(&d), sizeof(double)) ; }///:~

Bellekten girdi; 'istringstream' sınıfı

Bellekten bilgi okumak için akışların kolaylıklarından yararlanılır. Bunun

Page 63: C++ 2 (Kitap - Selami KOÇLU)

için istringstream nesneleri kullanılabilir. Bu nesneler istreamnesnelerinden türetilmiştir. Yapıcı işlevler ve üyeler şunlardır;

istringstream istr ; Yapıcı işlev boş bir istringstream nesnesi oluşturur. Nesne daha sonraelde edilcek olan bilgi ile ilerde doldurulabilir.

istringstream istr(string const& text) ; Yapıcı işlev istringstream nesnesini, string metin içerikleri ilebaşlatarak oluşturur.

void istringstream::str(string const& text) ; Bu üye işlev string metin içeriklerini istringstream nesnesine (o aniçinde varolanların üzerinede yazabilir) saklar. O an ki içerik üzerineyazabilir.

istringstream nesnesi genel olarak ASCII metinleri ikici (binary)karşılıklarına çevirmek için kullanılır (C deki atoi( ) işlevinin yaptığı gibi).Aşağıda vereceğimiz örnek, istringstream sınıfının kendisine ait seekg( )üye işlevi ile kullanışını gösterecektir.

#include <iostream> #include <string>#include <sstream>using namespace std ;

int main( ){ istringstream istr(“123 345”) ; //bir miktar metin yükle int x ; istr.seekg(2) ; //”12” yi atla istr>>x ; //int elde edilir cout<<x<<endl ; //x dışarı yerleştirilir istr.seekg(0) ; //baştan yeniden sağlanır istr>>x ; //int elde edilir cout<<x<<endl ; //dışarı yazılır istr.str(“111”) ; //başka bir metin yazılır istr>>x ; //int elde edilir cout<<x<<endl ; //dışarı yerleştirilir}///:~

Page 64: C++ 2 (Kitap - Selami KOÇLU)

çıktı //3123111//

Özelleştiriciler

Özelleştiriciler aslında işlevdir. İstenirse yeni özelleştiriciler inşa edilebilir.Burada C++ standart G/Ç kütüphanesinde bulunan özelleştiricileriinceleyeceğiz. Özelleştirircilerin çoğu biçim bayraklarını etkiler. Bu konudaha önce işlenmişti. Özelleştiricilerin çoğunda değişken bulunmayabilir.Değişken beklentisi olan kaynaklar mutlaka

#include <iomanip> yapılmalıdır.

std::boolalpha ; Bu özelleştirici ios::boolalpha bayrağını 1 yapar. sdt::dec ; Sayıların onluk tabana göre işlemlenmesini sağlar. Çevirme işlemleriözelleştiriciler yerleştirildikten sonra yapılır. Örnekte std::hex ve std::octaynı ortamda gösterildi;

cout<<16<<”, “<<hex<<16<<”, “<<oct<<16<<endl ;çıktı;

16,10, 20 std::endl ; Çıktı tamponuna yeni satır karakteri ekler ve hemen sonra çıktıtamponunu boşaltır.

std::ends ; çıktı tampon sonuna string sonlanma karakteri ekler

Page 65: C++ 2 (Kitap - Selami KOÇLU)

std::fixed ; ios::fixed bayrağını 1 yapar.

std::flush ; çıktı tamponunu boşaltır.

std::hex ; okuma ve yazmanın hex formatında olmasını sağlar.

std::internal ; ios::internal bayrağını 1 yapar.

std::left ; özelleştirici değerleri sol yana yaslar.

std::noboolalpha ; ios::boolalpha bayrağı 0 (sıfır) yapılır yani silinir.

std::noshowpoint ; ios::showpoint bayrağı silinir.

std::noshowpos ; ios::showpos bayrağı silinir.

std::noshowbase ; ios::showbase bayrağı silinir.

std::noskipws ; ios::skipws bayrağı silinir.

std::nounitbuf ; Her yazma işleminden sonra çıktı akışının boşaltılması durdurulur.Şimdi akış endl, flush, unitbuf veya kapatıldığında boşaltılır.

std::nouppercase ; ios::uppercase bayrağı silinir.

std::oct ; Özelleştirici sayıların sekiz tabanlı sayı biçimlerine göre gösterilmesini

Page 66: C++ 2 (Kitap - Selami KOÇLU)

ve okunmasını sağlar.

std::resetiosflags(flags) ; std::resetf(flags) işlevi çağrılarak gösterilen bayrakların sıfırlanmasısağlanır.

std::right ; Değerlerin sağa yaslanmasını sağlar.

std::scientific ; ios::scientific bayrağını 1 yapar.

std::setbase(int b) ; int değerlerin 8, 10 ve 16 tabanlarına göre gösterilmesini sağlar. Hex,dec ve oct özelleştiricilerine bir alternatiftir.

std::setfill(int ch) ; Çok küçük değerlere sahip sayıların belirtilen alan büyüklüklerinitamamlamak için kullanılan doldurma karakterlerini tanımlar. Varsayımolarak boşluk kullanılır.

std::setiosflags(flags) ; Bu özelleştirici belirtilen bayrak değerlerini 1 yapmak için std::setf(flags) işlevini çağırır.

std::setprecision(int width) ; float veya double tipler gösterilirken hassasiyeti ayarlar. std::fixed ilebirlikte float veya double tipin noktadan sonraki kısmını belli biruzunlukta gösterir.

cout<<fixed<<setprecision(4)<<4.0<<endl ;

çıktı://4.0000//

std::setw(int width) ; Burada özelleştirici bir sonraki yerleştirilecek veya elde edilecek olanın

Page 67: C++ 2 (Kitap - Selami KOÇLU)

genişliğini değişken olarak alır. Özelleştiricimiz yerleştirmede maksimumyerleştirilecek karakter sayısını belirttiği gibi, elde etme esnasında karakterdizisindeki maksimum sayıyı da bildirebilir. cin de elde sırasında, dizisınırlarını aşıp üzerine yazmayı engellemek için setw( ) işlevikullanılabilir.

cin>>setw((sizeof(array))>>array ;

cin den uzun bir string elde edilirken en fazla sizeof(array)-1 karakterdealt string lere bölünür ve sonuna otomatik olarak ASCII-Z karakteriyapıştırılır. Akılda kalması gerekenler;

setw( ) bir sonraki yer için geçerlidir. Bu çıktı akış durumunu değiştirenhex benzeri davranış göstermez. setw(sizeof(Bir_dizi)) kullanıldığı zaman Bir_dizi gerçekten dizi olmalıve bundan emin olmalısınız, dizi göstergeci olmamalıdır.

std::showbase ; ios::showbase bayrağı ayarlanır (1 yapılır).

std::showpoint ; ios::showpoint bayrağı ayarlanır (1 yapılır)

std::showpos ; ios::showpos bayrağı ayarlanır. (1 yapılır)

std::skipws ; ios::skipws bayrağı ayarlanıp 1 yapılır.

std::unitbuf ; Her yazma işleminden sonra çıktı akışı boşaltılır.

std::uppercase ; ios::uppercase bayrağı ayarlanıp 1 yapılır.

std::ws ; Okuma durumunda girdi tamponundaki o anki boşluk karakterlerininhepsini kaldırır.

Page 68: C++ 2 (Kitap - Selami KOÇLU)

'streambuf' Sınıfı

streambuf sınıfı akışlar tarafından işlemlenen, girdi ve çıktı karakterdizilerini tanımlar. ios nesnesine benzer şekilde streambuf nesneleri de,doğrudan inşa edilmezler. Ama başka sınıfların nesneleri tarafından,streambuf sınıf özelliği taşıyan nesneler oldukları açıklanır.Sınıf, C++ dilinin ilk ANSI-ISO standart uygulamalarına uzantı olarakyerleştirilmiş olanaklarda, önemli bir rol oynamıştır. Sınıf doğrudankullanılmamasına rağmen, üyelerini burada anlatacağız, zira streambufsınıfını tanıtmak için en mantıklı yer, bu bölümdür. Hemen bir şeyi dahaaçıklayalım; sizin çokbiçimliliği bildiğinizi kabul ediyoruz. Çokbiçimlilikhakkında kendinizi yeterli görmüyorsanız, süreklilik için bu bölümüatlayabilirsiniz. (çokbiçimlilik konusu birinci cildimizde ele alındı).streambuf sınıfının temel varlık nedeni; akış sınıflarını üzerinde işlemyaptıkları araçlardan ayırmaktır. Burada akılcı olan; ek bir yazılımkatmanının, araçla iletişimimizi sağlayan sınıflar ile, yazılım ile araçlarınkendi iletişimlerinin arasında bulunmasıdır. Bu da bize, yazılımtasarımında düzenli olarak rastlanan komut zinciri oluşturur. Yenidenkullanabilen yazılım inşası için komut zinciri, generik düzenektir (genericpattern). Transfer control protocol ve internet protocol (TCP/IP) yığıtıörnek verilebilir. Bir streambuf, başka bir komut düzeneği zincir örneğiolarak düşünülebilir; burada program stream nesneleri ile konuşmaktadır,sıra ile istemleri streambuf nesnelerine iletmektedirler, sıra ile araçlarlailetişimde bulunmaktalar. Böylece gördüğünüz gibi, önceden daha masraflısistem çağrıları ile yapılabilenler, ucuz kullanıcı yazılımı ile gerçekleniyor.streambuf sınıfında public yapıcı işlev bulunmaz, ama çok sayıda publicüye işlev bulunur. Bu public üye işlevlere ek olarak, sınıflarda sadeceözellik arttıran çok sayıda üye işlev de bulunur. Bu protected üye işlevler,kullanabilmeniz için birazdan verilecek. Burada incelenen streambufsınıfının bütün public üye işlevleri, aynen filebuf içinde geçerlidir. streambuf sınıfına özel davranışlar kazandıran inşa süreci ayrı birbölümde incelenmelidir ve streambuf nesnelerinin ifade ettikleri de özelörneklerle açıklanmalıdır. Bunları daha sonraki bölümlerde ele alacağız.Şimdi stream nesnelerinin streambuf üye işlevleri kullanılarak akışlarınokunmaları, yazılmaları, başka bir yere yönlendirilmeleri vekopyalanmaları örneklerle incelenecek;streambuf sınıfında aşağıdaki public üye işlevler bulunur; aşağıdaki type

Page 69: C++ 2 (Kitap - Selami KOÇLU)

streamsize bütün pratik uygulamalarda unsigned int olarak alınabilir.Girdi işlemleri için public üyeler;

streamsize streambuf::in_avail( ) ; Bu üye işlev hemen okunabilecek karakter sayısının alt sınırınıdöndürür.

int streambuf::sbumpc( ) ; Bir sonraki karakteri veya EOF döndürür. Karakter, streambufnesnesinden silinir. Girdi olmazsa sbumpc( ), yeni karakterleri temin içinuflow( ) (protected) işlevini çağırır. Eğer başka karakter yoksa EOFdöner.

int streambuf::sgetc( ) ; Bir sonraki karakteri veya EOF döndürür. Karakter streambufnesnesinden silinmez.

int streambuf::sgetn(char* buffer, streamsize) ; Girdi tamponundan n tane karakter okur ve bunları tampona (buffer)saklar. Okunan asıl karakter sayısı döndürülür. İstenen karakter sayısınıtemin etmek için ise, bu üye işlev xsgetn( ) (protected) üyesini çağırır.

int streambuf::xnextc( ) ; Girdi tamponundan o anki karakteri siler, bir sonraki karakteri veyaEOF döndürür. Karakter streambuf nesnesinden kaldırılmaz.

int streambuf::sputback(char c) ; streambuf nesnesinden okumak için bir sonraki karakter olarak c yiyerleşirir. Bu işlev kullanılırken çok dikkatli olmalıdır, çoğu kezkonulabilecek en fazla bir karakterlik yer bulunur.

int streambuf::sungetc( ) ; Bir sonraki girdi işleminde tekrar okunacak olan, girdi tamponunaokunan, son karakteri döndürür. Bu işlev de kullanılırken de çok dikkatliolmalıdır.

Çıktı işlemleri için kullanılan public üyeler;

int streambuf::pubsync( )

Page 70: C++ 2 (Kitap - Selami KOÇLU)

streambuf tamponunda yer alan muallaktaki bilgileri araca yazaraktamponu uyumlu hale getirir (örnek; tamponu boşaltır). Normalde sadecesınıfları özelleştirmek için kullanılır.

int streambuf::sputc(char c) ; Bu üye işlev streambuf nesnesine c yi yerleştirir. Karakter yazımındansonra eğer tampon tamamen dolarsa, işlev protected üye işlev overflow( )çağırarak tamponu araca boşaltır.

int streambuf::sputn(char const* buffer, streamsize n) ; Tampondan streambuf nesnesine n tane karakteri yerleştirir.Yerleştirilen asıl karakter sayısı döner. Bu üye işlev istenen sayıdakarakteri yerleştirebilmek için de (protected) üye olan xsputn( ) üyeişlevini çağırır.

Farklı işlemler için kullanılan public üyeler;

pos_type streambuf::pubseekof(off_type offset, ios::seekdir way,ios::openmode mode=ios::in | ios::out) offset e okunacak veya yazılacak bir sonraki karakterin kaymasınısıfırlar. Bu işlem aramanın doğrultusunu gösteren standart ios::seekdirdeğerlerine göre yapılır. Normalde sınıfları özel kullanımlara hazırlamakiçin kullanılır.

pos_type streambuf::pubseekpos(pos_type pos, ios::openmodemode=ios::in | ios::out) ; pos a okunacak veya yazılacak bir sonraki karakterin mutlak yerinisıfırlar. Normalde sınıfları özel kullanımlara hazırlamak için yararlanılır.

streambuf* streambuf::pubsetbuf(char* buffer, streamsize n) ; buffer i streambuf nesnesi tarafından kullanılacak şekilde tanımlar.Normalde sınıfları özel kullanımlara hazırlamak için yararlanılır.

Protected streambuf üyeler

Normalde streambuf sınıfının protected üyelerine erişilemez. Bununlabirlikte streambuf sınıfından türetilmiş olan özel sınıflar, bu üyelereerişilebilir. Bahsi geçen üyeler streambuf sınıfının anlaşılmasında ve

Page 71: C++ 2 (Kitap - Selami KOÇLU)

kullanılmasında çok önemlidir. Genellikle streambuf sınıfında hem veriüyeler, hem de üye işlevler arasında protected tanımlanmış üyelerbulunur. Kullanımı sarmalama ilkesini ihlal eden öteki veri üyeleri, buradaşimdilik incelenmeyecek. Üye işlevlerin streambuf sınıfına sağladığıyetenekler oldukça fazladır, ve büyük olasılıkla doğrudan veri üyelerininkullanımını gerektirir. Bu bölümde streambuf sınıfının bütün protectedüyeleri gösterilmeyip, sadece sınıf özelliklerini inşa edebilecekleranlatılacaktır.

streambuf sınıfının girdi/çıktı tampon göstergeçleri.

Input

put back

retrieve

eback( ) gptr( )

Output

pbase( )insert

pptr( )epptr( )

Page 72: C++ 2 (Kitap - Selami KOÇLU)

Protected yapıcı işlevler;

streambuf::streambuf( ) ; streambuf sınıfının varsayılan (protected) yapıcı işlevi.Protected üye işlevlerin çoğu, girdi işlemleri ile alakalıdır. Virtual (sanal)olarak belirtilmiş olan üye işlevler streambuf sınıfından türetilmişsınıflarda yeniden tanımlanabilir. Böyle durumlarda, yeniden tanımlanmışolan işlevler, bu tür türetilmiş sınıf nesnelerinin adreslerini almış olani/ostream nesneleri tarafından çağrılır. Burada hatırlatalım; virtual işlevlerC++ da çokbiçimliliğin temel unsurudur. İşte protected üyeler;

char* streambuf::eback( ) ; Girdi tamponunda streambuf sınıfı üç tane göstergeçle iş yapar; eback( ) işlevi putback sonunu işaret eder; karakterler bu pozisyonda eskiyerlerine konur. Yukarıdaki şekile baktığınızda, eback( ) in girditamponunun başlangıcını gösterdiğini görürsünüz.

char* streambuf::egptr( ) ; Girdi tamponu için streambuf sınıfı üç göstergeç kullanır; egptr( )işlevi elde edilmiş olan son karakterin ötesini işaret eder. gptr( ) işlevi ileegptr( ) işlevi birbirine eşitse, tampon mutlaka yeniden doldurulmalıdır.Bu, underflow( ) işlevi çağrılarak gerçeklenir. Aşağı bakınız.

void streambuf::gbump(int n) ; Girdi gösteregecini n öteler.

char* streambuf::gptr( ) ; Yeniden elde edilecek olan bir sonraki karakteri gösterir.

virtual int streambuf::pbackfail(int c) ; streambuf sınıfına özellikler kazandırılması ile yeniden tanımlanan buüye işlev eski yere yerleştirme karakteri c görev yapmadığı zaman akıllıbazı işler yapacaktır. Burada dikkate alınması gereken bir şey de; girditamponunun başlangıcına erişildiği için, eski yere yerleştirme karakteri cgörev yapmadığı zaman, eski okuma göstergeci yenilenir. Bu üye işleveski yere yerleştirme veya elde edilmeme bir karakter görevini yerinegetirmediği zaman çağrılır.

virtual streambuf::setg(char* beg, char* next, char* beyond) ;

Page 73: C++ 2 (Kitap - Selami KOÇLU)

Girdi tamponunu başlatır; beg girdi tampon alanının başlangıcını işareteder, next elde edilecek olan bir sonraki karakteri işaret eder, beyond girditamponunun son karakterinden sonrasını gösterir. next genellikle enazından, eski yere yerleşmeye izin vermek için, beg+1 değerindedir. Buüye işlev sıfır (0) değişkenle çağrıldığı zaman, girdi tamponu kullanılmaz.

virtual streamsize streambuf::showmanyc( ) ; s ve c harflerini çıkarınca how many kaldığı görülür. Bu da, kaç tanedemektir. streambuf sınıfına özellikler kazandırılması sayesinde, bu üyeişlev yeniden tanımlanır. Bu, uflow( ) veya underflow( ) işlevleri EOFdöndürmeden önce, araçtan okunabilen karakter sayısının alt sınırınıgüvenceye alan değeri döndürür. Varsayım olarak sıfır (0) döner.(varsayılan değerin anlamı; sonraki iki işlev EOF döndürmeden önce en azsıfır (0) karakter döner demektir.)

virtual int streambuf::uflow( ) ; Bu üye işlev girdi tamponuna yeniden yeni karakterler yüklemek içinstreambuf sınıfı özelleştirilerek yeniden tanımlanır. Varsayılan uygulamaunderflow( ) işlevini çağırmak (aşağıya bakınız) ve okuma göstergecinigptr( ) arttırmak içindir.

virtual int streambuf::underflow( ) ; Bu üye işlev, araçtan başka bir karakter okumak için streambuf sınıfıözelleştirilerek yeniden tanımlanır. Varsayılan uygulama EOF döndürür.Tampon kullanıldığında, çoğunlukla bütün tampon yeniden tazelenmez, buda yeniden yüklemeden hemen sonra karakterlerin eski yerlerineyerleştirilmelerini imkansız kılar. Bu sistem, girdi tamponunun sadece birparçası yeniden yüklendiğinde, dağıtık tampon (split buffer) olarakadlandırılır.

virtual streamsize streambuf::xsgetn(char* buffer, streamsize n) ; Bu üye işlev, araçtan n tane karakteri tekrar almak için streambuf sınıfıözelleştirilerek yeniden tanımlanır. Varsayılan uygulama, sbumpc( )işlevini her bir karakter için çağırmaktır. Böylece bu işlemle, underflow( )işlevi her bir karakter için çağrılır.

Şimdi de çıktı işlemlerinde kullanılan protected üye işlevleri inceleyelim.Girdi işlemlerindekilere benzer olarak çıktı işlemlerinde kullanılan bazıprotected üye işlevler virtual dır. Bunlar türetilmiş sınıflarda yeniden

Page 74: C++ 2 (Kitap - Selami KOÇLU)

tanımlanırlar.

virtual int streambuf::overflow(int c) ; Bu üye işlev çıktı tamponundaki karakterleri araca boşaltmak içinstreambuf sınıfı özelleştirilerek yeniden tanımlanır. Ve daha sonra tamponboş olması için çıktı tampon göstergeçleri sıfırlanır. Çıktıda tamponlamakullanılmazsa her karakterin streambuf nesnesine yazılması için overflow( ) işlevi çağrılır. Bunu gerçeklemek için de tampon göstergeçlerisıfıra (örnek; setp( ) kullanılırak) ayarlanır. Varsayılan uygulama EOFdöndürür. Bu da bize araca hiçbir karakter yazılmayacağını gösterir.

char* streambuf::pbase( ) ; Çıktı tamponu için streambuf sınıfı üç tane göstergeci işleme koyar;pbase( ) çıktı tampon alanının başlangıcını gösterir. Yukarıdaki şekilebakabilirsiniz.

char* streambuf::epptr( ) ; epptr( ) yazılabilecek son karakterin ötesindeki yeri işaret eder. pptr( )işlevi ile epptr( ) işlevleri eşitse o zaman tampon mutlaka boşaltılmalıdır.Bunu gerçeklemek için de overflow( ) işlevi çağrılır.

void streambuf::pbump(int n) ; Bu işlev çıktı göstergecini n kadar öteye götürür.

char* streambuf::pptr( ) ; Yazılacak bir sonraki karakterin yerini işaret eder.

void streambuf::setp(char* beg, char* beyond) ; Bu üye işlev çıktı tamponunu başlatır; beg çıktı alanının başlangıcınıbeyond ise çıktı alanınındaki son karakterden ötedeki yeri işaret eder.Tamponlanma istenmediğini belirtirken, değişkenler için sıfır (0) değerikullanılır. Bu durumda araca her bir karakteri yazmak için overflow( )işlevi çağrılır.

streamsize streambuf::xsputn(char const* buffer, streamsize n) ; Bu üye işlev araca n karakteri hemen yazmak için streambuf sınıfıözelleştirilerek yeniden tanımlanır. Varsayılan uygulamada her karakteriçin sputc( ) işlevi çağrılır, bundan dolayı yeniden tanımlama, sadece dahafazla uygulama gerektiğinde yapılır.

Page 75: C++ 2 (Kitap - Selami KOÇLU)

Tampon yönetiminde ve yerleşim düzenlemesinde kullanılan protectedüyeler:

virtual streambuf* streambuf::setbuf(char* buffer, streamsize n) ; Bu üye işlev tampon yüklemek için streambuf sınıfı özelleştirilerekyeniden tanımlanır. Varsayılan uygulama; hiçbir şey yapmamaktır.

virtual pos_type streambuf::seekoff(off_type offset, ios::seekdir way, ios::openmode mode=ios::in | ios::out) Bu üye işlev yeni ilişkili bir yere göre bir sonraki göstergeci sıfırlamakiçin streambuf sınıfı özelleştirilerek yeniden tanımlanır. Hatayı göstermekiçin varsayılan uygulama -1 döndürür. Örnek olarak tellp( ) veya tellg( )çağrıldığı zaman işlev çağrılır. streambuf özelleştirmesi aramayıdesteklediğinde, o zaman işlev tanımlaması, ayrıca özelleştirmenin yenidenyer düzenleme (veya tellp/g( )) istemini yerine getirmesi için yapılır.

virtual pos_type streambuf::seekpos(pos_type offset, ios::openmodemode= ios::in | ios::out) ; Bu üye işlev, yeni mutlak yer girdi veya çıktısının bir sonrakigöstergecini sıfırlamak için (reset etmek), streambuf sınıfını özelleştirerekyeniden tanımlar. Varsayılan uygulamada -1 döndürülerek hata gösterilir.

virtual int sync( ) ; Bu üye işlev, çıktı tamponunu bir araca boşaltmak için, veya girdiaracını tüketilen son karakterin yerine ayarlamak (reset etmek) için,streambuf sınıfını özelleştirerek yeniden tanımlar. Varsayılan uygulamada(tampon kullanılmadan) sıfır (0) döndürülür, bu da bize başarılı uyumolduğunu ifade eder. Üye işlev tamponda hala bulunan karakterin aracayazılmasını güvenceye alır veya, streambuf nesnesi çıkışı kestiği zamanaraçta kullanılmamış karakterleri saklamayı sürdürür.

Birazda iyi haber: araçlardan bilgi okuma amacı ile özelleştirmeler içinunderflow( ) işlevini yeniden tanımlarken, ve araçlara bilgi yazma amacıile özelleştirmeler için overflow( ) işlevini yeniden tanımlarken de oldukçaaz iş yapılır. Tabii bunlar streambuf sınıfının özelleştirme tasarımlarısırasında gerçeklenir.fstream sınıf nesneleri birleşik girdi/çıktı tamponları kullanırlar. Bu,istream ve ostream sınıflarının sanal olarak ios sınıfından, ki streambuf ı

Page 76: C++ 2 (Kitap - Selami KOÇLU)

da içerir, türetilmesinden kaynaklanır. istream ve ostream sınıflarındantüretilen sınıflar da, onların streambuf göstergeçlerini paylaşırlar. Farklıtamponlarda hem girdi hem de çıktıyı destekleyen bir sınıf inşa etmek içinise; streambuf kendisini içsel olarak iki tamponda tanımlamadır. Okumakiçin seekoff( ) işlevi çağrıldığı zaman, mode değişkeni ios::in değerineayarlanır, öbür halde ise ios::out olur. Bu şekilde streambuf özelleştirmesiokuma tamponu veya yazma tamponundan hangisine erişeceğini bilir. Pektabii olarak underflow( ) ve overflow( ) işlevleri de, hangi tamponlardagörev yapacaklarını kendileri bilirler.

filebuf sınıfı filebuf sınıfı, dosya akış sınıfları tarafından kullanılan bir streambufözelleştirmesidir. streambuf sınıfı tarafından sağlanan public üyelerdenbaşka aşağıdaki ek public üyelerde bulunmaktadır;

filebuf::filebuf( ) ; Sınıf bir yapıcı işleve sahip olduğu için streambuf sınıfından farklıdır.Ayrıca bu yüzden filebuf nesnesi oluşturulabilir. Böylece henüz akışabağlanmamış basit filebuf nesnesi tanımlanmış olur.

bool filebuf::is_open( ) ; Bu üye işlev filebuf gerçekten açık bir dosyaya bağlanırsa doğru (true)döndürür. Aşağıdaki open( ) üye işlevine bakınız.

filebuf* filebuf::open(char const* name, ios::openmode mode) ; Bu üye işlev adı belli bir dosya ile filebuf nesnesini ilişkilendirir. Dosyaios::openmode tarafından sağlanan duruma göre açılır.

filebuf* filebuf::close( ) ; Bu üye işlev filebuf nesnesi ile onun dosyası arasındaki bağlantıyıkapatır. filebuf nesnesi çıkışı sona erdirdiği zaman, bağlantı otomatikolarak kapatılır.

filebuf nesneleri tanımlanmadan önce aşağıdaki önişlemleyici yönergesimutlaka belirtilmelidir;

#include <fstream>

Page 77: C++ 2 (Kitap - Selami KOÇLU)

İleri konular

Akışları kopyalamak

Dosyalar genellikle kaynak kodlar ya karakter karakter, ya da satır satırokunarak kopyalanır. Dosyaları işlemlemedeki temel kalıp aşağıdakigibidir; -- sonsuz döngüde; 1- bir karakter oku 2- eğer okunmazsa (yani fail( ) işlevi true döndürür) döngüden çık 3- karakteri işlemle

Önemle vurgulamamız gereken; sınamanın okumadan sonra gelmekmecburiyetidir. Ancak sadece o zaman okuma işleminin başarılı olupolmadığı saptanabilir. Bu iş için değişik uygulamaların olması gayetdoğaldır; getline(istream&, string&) işlevi istream& in kendisinidöndürür, böylece hem okuma hem de sınama işlemi tek açıklama ilegerçeklenmiş olur. Bütün bunlara rağmen yukarıdaki kalıp genel durumuaçıklar. Aşağıdaki örnek programda cin, cout a kopyalanır;

#include <iostream>using namespace std ;int main( ){ while(true){ char c ; cin.get(c) ; if(cin.fail( )) break ; cout<<c ; } return 0 ;}///:~

get( ) işlevi if deyimi ile beraber kullanılarak getline( ) benzeri sonuçalmakta olasıdır.

Page 78: C++ 2 (Kitap - Selami KOÇLU)

if(!cin.get(c)) break ;

Bununla birlikte her halükarda temel kural hep aynı; “önce oku, sonrasına”Bu tür basit dosya kopyalamaya pek sık rastlanmaz. Daha sık rastlanandosya kopyalama türünde; bir dosya belli bir noktasına kadar işlemlenir,kalan noktadan sonrasında dosyanın geri kalan kısmı değiştirilmedenkopyalanır. Aşağıdaki örnek program bu durumu göstermektedir; ignore( )işlevi ilk satırı atlamak için çağrılır, (örneğin güvenliği açısından ilk satırın80 karakterden uzun olmadığı kabul ediliyor), ikinci deyimde özelbindirilmiş <<- işleci kullanılarak bir streambuf göstergeçi ikinci akışayerleştiriliyor. rdbuf( ) üye işlevi bir streambuf* döndürür, ve bununüzerine hemen cout a yerleştirilir. Bu, cin in geri kalan kısmını hemencout a kopyalar.

#include <iostream>using namespace std ;int main( ){ cin.ignore(80, '\n') ; //ilk satırı atla cout<<cin.rdbuf( ) ; //geri kalanı streambuf* yerleştirerek kopyala }///:~

Burada dikkat edilmesi gereken; yöntemin streambuf nesne varlığınıkabul etmesi ve bunun streambuf özelleştirmelerinin tamamı içinçalışmasıdır. Sonuç olarak; streambuf sınıfı belli bir araç içinözelleştirilmişse, başka diğer bir akışa da bu yöntem kullanılarakyerleştirilebilir.

Bağlantılı akışlar

ostream ler ios nesnelerine tie( ) üye işlevi kullanılarak bağlanabilir.ostream nesnesinin bağlı olduğu ios nesnesinde herhangi bir zaman dagerçeklenen girdi veya çıktı işlemleri esnasında, bu işlev ostreamnesnesinin bütün tamponlanmış çıktılarını boşaltır (flush( ) işlev çağrısıile). Varsayım olarak cout cin e bağlıdır (cin.tie(cout)). Yani cin üzerindeherhangi bir işlem talep edildiği zaman, önce cout boşaltılır demektir.

Page 79: C++ 2 (Kitap - Selami KOÇLU)

Bağlantıyı koparmak için ise, ios::tie(0) üye işlevi çağrılabilir.Daha yararlı ama varsayılan olmayan, akış bağlamalarına başka birörnekte; cerr i cout a bağlamaktır. Bu yolla standart çıktı ve hata iletileribirbirleri ile uyumlu olarak ekranda görünürler.

#include <iostream>using namespace std ;int main( ){ cout<<”cout a giden ilk (tamponlanmis) satir\n” ; cerr<<”cerr e giden ilk (tamponlanmamis) satir\n” ; cerr.tie(&cout) ; cout<<”cout a giden ikinci (tamponlanmis) satir\n” ; cerr<<”cerr e giden ikinci (tamponlanmamis) satir\n”;}///:~

//üretilen çıktı:cerr e giden ilk (tamponlanmamis) satircout a giden ilk (tamponlanmis) satircout a giden ikinci (tamponlanmis) satircerr e giden ikinci (tamponlanmamis) satir //

Akışları bağlamanın başka bir yolu da, stream lerin aynı streambufnesnesini kullanmalarını sağlamaktır. Bunu gerçeklemek için, ios::rdbuf(streambuf*) işlevini kullanmak lazımdır. Bu yolla iki akış(örneğin kendi biçimlendirmeleri) girdi için bir akışı, çıktı için de ötekiakışı kullanabilir, daha ziyade işletim sistem çağrıları tarafındangerçeklenen yeni yönlendirmeler iostream kütüphaneleri kullanılarakyapılır. Örnekler için aşağıya bakınız.

Akışların yönlendirilmeleri

Akışlar streambuf nesnelerini paylaşmak için ios::rdbuf( ) üyesinikullanırlar. Bunun anlamı; bir akışa yazılan bilgi aslında ikinci bir akışayazılmaktadır demektir, bu olay 'yönlendirme' (redirection) diyeadlandırılmaktadır. Yönlendirme normalde işletim sistemleri tarafındangerçeklenir, ama bazı durumlarda C++ dilinin sağladığı olanaklar dagerekebilir.Yönlendirmenin istendiği standart durum; hata iletilerinin standart hata

Page 80: C++ 2 (Kitap - Selami KOÇLU)

dosyası yerine (dosya belirtme sayısı 2 olan), herhangi bir dosyaya yazılmadurumudur. Bu Unix işletim sisteminde bash kabuğu kullanılarakaşağıdaki şekilde gerçeklenir;

program 2>/tmp/error.log

Bu komut sayesinde program tarafından yazılan herhangi hata iletileriekran yerine /tmp/error.log dosyasında saklanır. Şimdi bunun streambuf nesneleri kullanılarak, nasıl gerçekleştirileceğinebakalım. Program, hata iletilerinin yazılacağı dosya adını, bir yardımcıdeğişken olarak bekler; böylece program aşağıdaki gibi çağrılabilir

program /tmp/error.log

Şimdi yönlendirmeyi gösteren program örneği verelim;

#include <iostream>#include <fstream>using namespace std ;int main(int argc, char** argv){ offstream errlog ; //1 streambuf* cerr_buffer=0 ; //2 if(argc==2){ errlog.open(argv[1]) ; //3 cerr_buffer=cerr.dbuf(errlog.rdbuf( )) ; //4 } else{ cerr<<”bilinmeyen log dosya adi\n”; return 1 ; } cerr<<”stderr dosyasina iletiler, msg 1\n” ; cerr<<”stderr dosyasina iletiler, msg 2\n” ; cout<<”icerik incelemesi ”<< argv[1]<<”....[Enter]” ; cin.get( ) ; //5 cerr<<”stderr dosyasina iletiler, msg 3\n” ; cerr.rdbuf(cerr_buffer) ; //6 cerr<<”tamam\n”; //7}///:~

Page 81: C++ 2 (Kitap - Selami KOÇLU)

//üretilen çıktı argv[1]

cin.get( ) te stderr dosyasina iletiler, msg 1 stderr dosyasina iletiler, msg 2 program sonunda stderr dosyasina iletiler, msg 1 stderr dosyasina iletiler, msg 2 stderr dosyasina iletiler, msg 3//

-1. ve 2. satırlarda yörel değişkenler tanımlanır; errlog hata iletilerininyazıldığı ofstream dir, cerrbuffer streambuf a göstergeçtir, ki esas cerrtamponunu işaret eder. Aşağıda ayrıntılar;-3. satırda yeni yeni hata akışı açıldı.-4. satırda yönlendirme gerçekleşti; şimdi cerr errlog tarafındantanımlanmış streambuf a yazar. Önemli olan cerr tarafından kullanılanesas tamponun aşağıda anlatıldığı gibi saklanmasıdır. -5. satırda artık biraz dinleniriz, yani teneffüs. Bu noktada iki satır yenihata dosyasına yazılmış olmalıydı. Şimdi onun içeriğine bakma şansınasahibiz; evet iki satır dosya içinde duruyordu.-6. satır, yönlendirmenin bittiği yerdir. errlog nesnesi main( ) işlevininsonunda ortadan kaldırılır. Bu çok önemlidir. cerr tamponu ortadankaldırılmamış olsaydı, o zaman orada cerr olmayan streambuf nesnesinedayanak olacaktı. Tabii bu da beklenmeyen neticelere yol açacaktı. Esasstreambuf ın yönlendirmeden önce saklanması görevi programcınınsorumluluğundadır. Bu yönlendirme sona erdiği zaman da yenidendüzenlenir.-7. satırda son olarak, yönlendirme bitirilirken yapılanlar ekrana yineyazdırılır.

Akışları okuma VE yazma

Bir akışta hem okuyup hem de yazmak için mutlaka fstream nesnesioluşturulmalıdır. Yapıcı işlev, ifstream ve ofstream nesnelerinde olduğugibi, açılacak dosya adını alır.

fstream inout(“girdiciktidosyasi”, ios::in | ios::out) ;

Page 82: C++ 2 (Kitap - Selami KOÇLU)

Buradaki ios::in ve ios::out girdi çıktı sabitleri dosyanın aynı anda hemgirdi hemde çıktı için açılacağını gösterir. Çok sayıda mod, ikici (binary)veya '|' işleci ile ardarda dizilerek kullanılabilir. ios::out a seçenek olarakios::app dosya sonunda yazılmak sureti ile kullanılabilir. Bir şekilde, bazen dosyaya yazım ve okuma başarısız olur; örnek olarakdosya henüz ortada olmayabilir, eğer varsa üzerine tekrar yazılmamalımı?İşte çözüm;

#include <iostream>#include <fstream>#include <string>using namespace std ;int main( ){ fstream rw(“fname”, ios::out | ios::in) ; if(!rw){ rw.clear( ) ; rw.open(“fname”, ios::out | ios::trunc | ios::in) ; } if(!rw){ cerr<<” dosya 'fname' berbat acildi”<<endl ; return 1 ; } cerr<<rw.tellp( )<<endl ; rw<<”selam evren”<<endl ; rw.seekg(0) ; string s ; getline(rw, s) ; cout<<”OKU: “<<s<<endl ;}///:~

Yukarıdaki örnekte, 'fname' dosya adına rastlamadan, yapıcı işlevgörevini yerine getiremez. Bununla beraber bu durumda, open( ) işlevigörevini yerine getirir. Ama dosya varsa asıl görev yerine getirilir. rw ınbaşlangıçtaki inşası gibi ios::ate bayrağı belirlenmiş olsaydı okuma/yazmaetkinliği varsayım olarak EOF ta olurdu. Ayrıca ios::ate, ios::appdeğildir bundan dolayı seekg( ) veya seekp( ) kullanarak rw yerleşiminidüzenlemek hala olasıdır. Akışın açıldığı zaman boş olarak veya boş olmadan oluşturulmaları veyayeniden oluşturulmalarını, güvenceye almak için dosya bayrakları fstream

Page 83: C++ 2 (Kitap - Selami KOÇLU)

nesneleri ile kullanılırlar.Okumak ve yazmak için dosya bir kez açıldığında << işleci, bilginindosyaya yerleştirilmesi için kullanılır, >> işleci ise dosyadan bilginin eldeedilmesini sağlar. Bu işlemler gelişigüzel sırada olabilir. Aşağıdaki kodparçası bir dosyadan boşlukla ayrılan kelimeleri okumaktadır ve dahasonra dosyaya bir string yazmaktadır. string okumasının sonlandığı yerinhemen ötesinde, yazımın sonlandığı yerin sonra başka bir string okumasıtakip eder.

fstream f(“dosya_ismi”, ios::in | ios::out | ios::trunc) ; string str ; f>>str ; //ilk kelimeyi oku f<<”selam evren” ; //metin yaz f>>str ; //tekrar oku

<< ve >> işleçleri fstream nesneleri ile birlikte kullanılabilirler. Şimdihemen hem << hem de >> işleçleri aynı deyim içinde kullanılabilirmi diyesorabilirsiniz. Hepsinden öteye f>>str, fstream& üretmesi lazımmı, yoksalazım değilmi?. Yanıt: hayır. Zira >> elde etme işleci ile derleyici fstreamnesnesini ifstream nesnesine döndürür, << yerleştirme işleci ile defstream nesnesini ofstream nesnesine döndürür. Buradaki döndürme, tipdeğişimi olan döküm anlamındadır. Bundan dolayı aşağıdaki tarzdayazılmış deyimlerde;

f>>str<<”dede”>>str ;

derleyici aşağıdaki hata iletisini verir;

no match for 'operator<<(class istream, char [8])'

Derleyici istream sınıfı ile sorun yaşadığından elde etme işleci ile beraber,fstream nesnesi ifstream nesnesi olarak düşünülür. Tabii, gelişigüzel elde etme ve yerleştirme nadiren kullanılır. Çoğunluklayerleştirme ve elde etme, dosyada belli yerlerde gerçeklenir. Böyledurumlarda yapılmak zorunda kalınan yerleştirme ve elde etme, seekg( ) vetellg( ) üye işlevlerince denetlenir ve yansıtılır. Dosya sonundan ötesini okuma, dosya sonuna erişme, veya dosyabaşlamadan yer belirleme gibi benzeri hata koşulları clear( ) üye işlevincetemizlenir. Yani hata koşulları silinir. clear( ) işlevini takiben süreç devam

Page 84: C++ 2 (Kitap - Selami KOÇLU)

edebilir. Örnek;

fstream f(“dosya_adi”, ios::in | ios::out | ios::trunc) ; string str ; f.seekg(-15) ; //çalışmaz f.clear( ) ; //süreç devam edebilir artık f>>str ; //ilk kelimeyi oku

Yaygın olarak hem okuma hem de yazma işlemlerinin gerçekleştiğidurumlar, veri tabanı programlarında rastlanır. Veri tabanları bellibüyüklükte kayıtlardan oluşan dosyaların ve bilgi parçalarının büyüklük veyerleri olarak bilinir. Aşağıdaki örnek dosyaya (olması muhtemel) metinsatırları ekler ve belli bir satırı geri alır, bu işlemi dosyadaki düzene göregerçekler. Satırın ilk bayt yerini elde etmek için index ikici (binary)dosyası kullanılır;

#include <iostream>#include <fstream>#include <string>using namespace std ;void err(char const* msg){ cout<<msg<<endl ; return ;}void err(char const* msg, long value){ cout<<msg<<value<<endl ; return ;}void read(fstream& index, fstream& strings){ int idx ; if(!(cin>>idx)) //indexi oku return err(”beklenen satir no'su”) ; index.seekg(idx*sizeof(long)) ; //index kaymasına git long offset ; if(!index.read(reinterpret_cast<char*>(&offset), sizeof(long))) //satir kaymasını oku return err(”satir kaymasi yok”, idx) ; if(!strings.seekg(offset)) //satir kaymasina git return err(“satir kaymasi elde edilemedi”, offset) ;

Page 85: C++ 2 (Kitap - Selami KOÇLU)

string line ; if(!getline(strings, line)) //satiri oku return err(“satir yok”, offset) ; cout<<”elde edilen satir”<<line<<endl ; //satiri göster}void write(fstream &index, fstream &strings){ string line ; if(!getline(cin, line)) //satiri oku return err(satir yok”) ; strings.seekp(0, ios::end) ; //stringe index.seekp(0, ios::end) ; //indexe long offset=strings.tellp( ) ; if(!index.write(reinterpret_cast<char*>(&offset), sizeof(long))) //offset indexe yaz err(“indexe yazim olmadi”, offset) ; if(!(strings<<line<<endl)) //satirin kendisini yaz err(“satir yazimi olmadi”) ; cout<<”kaymaya yazimi onayla”<<offset<<”satir: “<<line<<endl ; //satira yazimi onayla}

int main( ){ fstream index(“index”, ios::trunc | ios::in | ios::out) ; fstream strings(“strings”, ios::trunc | ios::in | ios::out) ; cout<<”okuma satir <sayisi> na 'o<sayisi>' gir veya” “bir satir yaza “y<sayi>\n” “veya q yazip cik\n” ; while(true){ cout<<”o <say>, yaz <satir>, q? “ ; string cmd ; cin>>cmd ; if(cmd==”q”) return 0 ; if(cmd==”o”) read(index, strings) ; else if(cmd==”y”) write(index, strings) ; else cout<<”bilinmeyen komut”<<cmd<<endl ; }

Page 86: C++ 2 (Kitap - Selami KOÇLU)

}///:~

Dosya yazılması ve okuması ile ilgili olarak aşağıdaki örneğebakabilirsiniz. Burada ASCII_Z sınırlaması gözönüne alınmıştır.

#include <iostream>#include <fstream>using namespace std ;int main( ){ fstream(“hello”, ios::in | ios::out | ios::trunc) ; f.write(“hello”, 6) ; //2-ascii-z f.write(“hello”, 6) ; f.seekg(0, ios::beg) ; //dosya başlangıcını resetle char buffer[100] ; //veya char* buffer=new char[100] char c ; cout<<f.get(buffer, sizeof(buffer), 0).tellg( )<<endl ; //ilk hello oku f>>c ; //ascii-z oku cout<<f.get(buffer+6, sizeof(buffer)-6, 0).tellg( )<<endl ; //ikinci helloyu oku buffer[5]=' ' ; //ascii-z i, ' ' e döndür cout<<buffer<<endl ; //2 defa hello yaz}///:~

//üretilen çıktı511hello hello//

Akışlara hem okuma hem de yazmayı, yukarıdakilerden tamamen başka birşekilde gerçekleştirmek için, akış nesnelerinin streambuf üyelerikullanılır. Bu uygulamada daha önceki bütün kabuller geçerlidir. Yazmaişlemini takip eden bir okuma işleminden önce mutlaka seekg( ) işlevikullanılmalıdır, ve ayrıca okuma işlemini takip eden bir yazma işlemindenönce de seekp( ) işlevi kullanılmalıdır. Akışın streambuf nesnelerikullanıldığı zaman, ya bir istream nesnesi başka bir ostream nesnesininstreambuf nesnesi ile bağlantılıdır, veya tersi, ya da bir ostream nesnesibaşka bir istream nesnesinin streambuf nesnesi ile bağlantılıdır. Birazönceki programın bağlantılı akışlarla yazılmış hali aşağıda verilmiştir.

Page 87: C++ 2 (Kitap - Selami KOÇLU)

#include <iostream>#include <fstream>#include <string>using namespace std ;void err(char const* msg){ cout<<msg<<endl ; return ;}void err(char const* msg, long value){ cout<<msg<<value<<endl ; return ;}void read(istream& index, istream& strings){ int idx ; if(!(cin>>idx)) return err(“beklenen satir no”) ; index.seekg(idx* sizeof(long)) ; //index kaymasina git long offset ; if(!index.read(reinterpret_cast<char*>(&offset), sizeof(long))) return err(“satirda kayma yok”, idx) ; if(!strings.seekg(offset)) //satir kaymasina git return err(“string kaymasi elde yok”, offset) ; string line ; if(!getline(strings, line)) return err(“satir yok”, offset) ; cout<<”satır elde”<<line<<endl ; //satiri gösteren}void write(ostream& index, ostream& strings){ string line ; if(!getline(cin, line)) return err(“satir yok”) ; strings.seekp(0, ios::end) ; index.seekp(0, ios::end) ; long offset=strings.tellp( ) ; if(!index.write(reinterpret_cast<char*>(&offset), sizeof(long))) err(“index yazimi olmadi”, offset) ; if(!(strings<<line<<endl)) //satirin kendine yaz err(“stringlere yazim olmadi”) ; cout<<”kaymaya yaz”<<offset<<”satir: ”<<line<<endl ;

Page 88: C++ 2 (Kitap - Selami KOÇLU)

}int main( ){ ifstream index_in(“index”, ios::trunc | ios::in | ios:out) ; ifstream strings_in(“”strings”, ios::trunc | ios::in | ios::out) ; ostream index_out(index_in.rdbuf( )) ; ostream strings_out(strings_in.rdbuf( )) ; cout<<”satir <sayisi> okumak için 'o <nosu>veya” “satir yazmak icin y <satir> \n” “veya terketmek icin 'q'.\n” ; while(true){ cout<<”o <no>, y <satir>, q ?.” ; //isteği göster string cmd ; cin>>cmd ; if(cmd==”q”) return 0 ; if(cmd==”o”) read(index_in, strings_in) ; else if(cmd==”y”) write(index_out, strings_out) ; else cout<<”bilinmeyen komut”<<cmd<<endl ; }}///:~

Unutulmaması gereken notlar;

Varolan akışların streambuf nesneleri ile bağlantılı akışları, ifstream veyaofstream nesneleri değillerdir. (veya bundan dolayı istringstream veostringstream nesneleri de değillerdir). Ama temel istream ve ostreamnesneleridir.streambuf nesnesi ifstream veya ofstream nesnesinde tanımlanmakzorunda değildir; aşağıdaki yapı kullanılarak akışların dışındatanımlanabilir.

filebuf fb(“index”, ios::in | ios::out | ios::trunc) ; istream index_in(&fb) ; ostream index_out(&fb) ;

Normal olarak dosya yazımlarında kullanılan akış modları, ifstream nesneinşasında kullanılabilir. Tersine de, normalde dosya okumada kullanılan

Page 89: C++ 2 (Kitap - Selami KOÇLU)

akış modları, ofstream nesnelerinin inşasında kullanılabilir.

istream ve ostream ortak streambuf ile bağlanmışsa, o zaman okuma veyazma göstergeçleri aynı yerleri gösterir (veya göstermesi lazımdır).Yanisıkı dostlar.

Öntanımlı fstream nesnesinde farklı streambuf lar kullanmanınüstünlüğü; özel görevleri olan streambuf a sahip akış nesnelerininkullanılabilmesidir. Böylece streambuf nesneler, özel araçlara arayüzoluşturmak için inşa edilebilirler.

ÖRNEKLER

Page 90: C++ 2 (Kitap - Selami KOÇLU)

1-write( ) işlevinin kullanıldığı basit bir örnek

//:01:yaz.cpp#include <iostream>using namespace std ;#include <cstring>

int main( ){ const char* durum1=”Malatya” ; const char* durum2=”Manisa” ; const char* durum3=”Istanbul” ; int len=strlen(durum2) ; cout<<”artan dongu dizini: \n” ; for(int i=0 ; i<=len ; i++){ cout.write(durum2, i) ; cout<<”\n” ; } cout<<”azalan dongu dizini:\n” ; for(i=len ; i>0 ;i--) cout.write(durum2, i)<<”\n” ; cout<”string uzunlugunu gecis:\n” ; cout.write(durum2, len+5)<<”\n” ; return 0 ; //sisteme dönüş return 0 ifadesini //sürekli yinelememek için yazmayabiliriz. }///:~

//çıktı:artan dongu dizini:MMaManManiManis Manisa azalan dongu dizini:ManisaManis Mani

Page 91: C++ 2 (Kitap - Selami KOÇLU)

Man Ma Mstring uzunlugunu gecis:Manisa Ista //

2-Biçim özelleştiricilerinin kullanıldığı bir örnek:

//:02:Bicim.cpp#include <iostream>using namespace std ;int main( ){ cout<<”int bir sayi gir :” ; int n ; cin>>n ; cout<<”n n*n\n” ; cout<<n<<” “<<n*n<<”(onluk)\n” ;//hex modu ayarla cout<<hex; cout<<n<<” “; cout<<n*n<<”(hexadecimal)\n” ; //octal modu ayarla cout<<oct<<n<<” “<<n*n<<”(octal)\n” ; //özelleşitiriciyi çağırmanın başka bir yolu dec(cout) ; cout<<n<<” “<<n*n<<” (decimal)\n” ; return 0 ;}///:~

//çıktı:int bir sayi gir :13n n*n13 169 (onluk)d a9 (hexadecimal)15 251 (octal)13 169 (decimal)//

3- Biçim alanlarında karakterleri fill( ) işlevi ile değiştirmek.

Page 92: C++ 2 (Kitap - Selami KOÇLU)

//:03:Doldur.cpp#include <iostream>using namespace std ;int main( ){ cout.fill('$') ; char* aday[2]={“Cafer Mehmet”, “Mehmet Cafer”} ; long armagan[2]={900, 1350} ; for(int i=0 ; i<2 ; i++){ cout<<aday[i]<<”: *”; cout.width(7) ; cout<<armagan[i]<<”\n” ; } return 0 ;}///:~

//çıktı:Cafer Mehmet: *$$$$900Mehmet Cafer: *$$$1350//

4-Hassasiyet ayarlama ve noktadan sonra gelen rakam sayısını belirleme.

//:04:showpt.cpp#include <iostream>using namespace std ;int main( ){ float fiyat1=20.50 ; float fiyat2=1.9+8.0/9.0 ; cout.setf(ios_base::showpoint) ; cout<<”\”dostlar1 \“ $”<<fiyat1<<”!\n” ; cout<<”\”dostlar2\” $”<<fiyat2<<”!\n” ; cout.precision(2) ; cout<<”\”dostlar1 \” $”<<fiyat1<<”!\n” ; cout<<”\”dostlar2 \” $”<<fiyat2<<”!\n” ; return 0 ;}///:~

//Çıktı:

Page 93: C++ 2 (Kitap - Selami KOÇLU)

“dostlar1” $20.5!“dostlar2” $2.78889!“dostlar1” $20.!“dostlar2” $2.8!//

5-Geçerli rakam denetlemesinin yapıldığı, aksi durumlarda programdançıkılan örnek:

//:05:denetle.cpp#include <iostream>using namespace std ;int main( ){ cout.precision(2) ; cout<<showpoint<<fixed ; cout<<”bir sayi giriniz: “ ; double toplam=0.0; double input ; while(cin>>input){ toplam+=input ; } cout<<”son giren deger= “<<input<<”\n” ; cout<<”Toplam= “<<toplam<<”\n” ; return 0 ;}///:~

//Çıktı:Bir sayi giriniz:100.01.0E1 -40 -10Z 90son giren deger:-10.00 /*anlamlı son değer, zira Z harfi var.*/Toplam=60.00//

6-Girdilere hırsızlama bakmak.

//:06:Dikizci.cpp#include <iostream>using namespace std ;#include <cstdlib>

Page 94: C++ 2 (Kitap - Selami KOÇLU)

int main( ){ //girdiyi oku ve # işaretine kadar yansıt char ch ; while(cin.get(ch)){//EOF ta sonlanır if(!='#') cou<<ch ; else{ cin.putback(ch) ; //karakteri yeniden yerleştir break ; } } if(!cin.eof( )){ cin.get(ch) ; cout<<'\n'<<ch<<”bir sonraki karakter .\n” ; } else{ cout<<”dosya sonuna erisildi.\n” ; exit(0) ; } while(cin.peek( ) != '#'){ cin.get(ch) ; cout<<'\n'<<ch<<”bir sonraki karakter: \n“ ; } else cout<<”dosya sonuna erisildi. \n” ; return 0 ;}///:~

//Kullandığım defter sayısı #5 ama gereken adet #3.Kullandığım defter sayısı # bir sonraki karakter.5 ama gereken adet # bir sonraki karakter.//

7- Dosyada saklama yapma işlemi #include <iostream>#include <fstream>using namespace std ;

Page 95: C++ 2 (Kitap - Selami KOÇLU)

int main( ){ char filename[20] ; cout<<”yeni dosya icin bir isim girin: ” ; cin>>filename ;//yeni dosya için çıktı akış nesnesi oluşturun ve fout adını verin ofstream fout(filename) ; fout<<”sadece sensin!\n” ; //dosyaya yazın cout<<”gizli rakaminizi girin: \n” //ekrana yazın float gizli ; cin>>gizli ; fout<<”gizli rakaminiz: “<<gizli<<”\n” ; fout.close( ) ; //dosyayi kapat//yeni dosya için girdi akış nesnesi oluşturun ve adını fin koyun ifstream fin(filename) ; cout<<”iste icindekiler: “<<filename<<”:\n” ; char ch ; while(fin.get(ch)){//dosyadan karakter oku cout<<ch ; //karakteri ekrana yaz cout<<”tamam\n” ; fin.close( ) ; return 0 ;}///:~

//çıktı:yeni dosya icin bir isim girin: canavargizli rakaminizi girin: 10.00iste icindekiler canavarsadece sensingizli rakaminiz: 10.00tamam//

8-Dosyalar listesinde karakter sayımı.

//:08:say.cpp#include <iostream>using namespace std ;#include <fstream>#include <cstdlib>

Page 96: C++ 2 (Kitap - Selami KOÇLU)

int main(int argc, char* argv[]){ if(argc==1){//değişken yoksa çık cerr<<”kullanım: ”<<argv[0]<<”dosya_ismi[s]\n” ; exit(1) ; } ifstream fin ; //akışı aç long say ; long toplam=0 ; char ch ; for(int dosya=1 ; dosya<argc ; dosya++){ fin.open(argv[dosya]) ; //akışı argv[dosya] ya bağla say=0 ; while(fin.get(ch)) say++ ; cout<<say<<”karakterler”<<argv[dosya]<<”\n” ; toplam+=say ; fin.clear( ) ; //bazı uygulamalar için gerekli fin.close( ) ; //dosyayı ayır } cout<<toplam<<”dosyalardaki toplam karakter sayisi\n” ; return 0 ;}///:~

//program ilgili işletim sisteminde derlenir. Örnek; dos ta say.exe halineçevrilir. Daha sonra komut satırından:c>say dosya_adı1 dosya_adı2 vs. yazılır. Buradan her dosyanın karaktersayısı ve bütün dosyaların toplam karakter sayısı sıra ile elde edilir.//

9- Bir dosyayı başka bir dosyaya, işletim sistemlerine özgü API lere bağlıkalmaksızın kopyalamak.

//:09:Kopya.cpp//bir akıştan diğerine kopyalama yapılır: tamponlanmış akış kopyalaması.#include <iostream>#include <fstream>const static int BUF_SIZE=4096 ;using std::ios_base ;int main(int argc, char** argv){

Page 97: C++ 2 (Kitap - Selami KOÇLU)

std::ifstream in(argv[1], ios_base::in | ios_base::binary) ; //bütün dosya içeriklerini okumak için ikici (binary) mod kullanıldı std::ofstream out(argv[2], ios_base::out | ios_base::binary) ;//akışların açıldığından emin olun char buf[BUF_SIZE] ; do{ in.read(&buf[0], BUF_SIZE) ; //en fazla n bayt tampona oku out.write(&buf[0], in.gcount( )) ; //tamponu çıkışa yaz }while(in.gcount( )>0) ;//akışları sorunlara karşı denetle in.close( ) ; out.close( ) ;}///:~

//Bu yöntemle çok hızlı dosya kopyalama yapmak mümkündür. Karakterlertek tek değil tamponlar kullanılarak toptan yapıldığı için bugerçekleşmektedir.//

10- Verilen bir metni tersten yazdıran program

#include <iostream>void print_tersten(std::ifstream& fin, std::ofstream& fout){ char ch ; if(fin.get(ch)){ print_tersten(fin, fout) ; fout<<ch ; }}int main( ){ std::ifstream input('in.txt') ; std::ofstream output('out.txt') ; print_tersten(input, output) ;}///:~

Page 98: C++ 2 (Kitap - Selami KOÇLU)
Page 99: C++ 2 (Kitap - Selami KOÇLU)

3.BÖLÜM

KALIPLAR (templates)

C++ dili sadece nesne yönelimli bir dil değildir. Birinci kitabımızdabelirttiğimiz gibi C++ çok paradigmalı bir dildir. Kalıplı paradigmabunlardan biridir. Kalıp özelliği bu paradigmanın C++ ya uygulanmasını,ve dilin gücünün ortaya çıkmasını sağlar. Kalıplar, C++ standart

Page 100: C++ 2 (Kitap - Selami KOÇLU)

kütüphanesinin kalbinde yer alan; stringler, G/Ç akışları (I/O stream),kaplar (container), yineleyiciler (iterator), algoritmalar ve diğerleridir.Kalıplar, derleme esnasında kalıp örnekleri olarak, sınıf veya işlevoluşturulmasını sağlayan düzeneklerdir. Bu, çalışma zamanında sınıflardannesne oluşturulurken, sınıfın oynadığı role benzer. Bir kalıpta bir veyadaha fazla değişken bulunabilir. Bir sınıf veya işlev anını elde etmek içinbu değişkenlere mutlaka doğru veriler (tip, değer veya kalıp)aktarılmalıdır. Yani olay delillendirilmelidir. Zira bu delillere (yaniaktarılan verilere) göre sınıfların veya işlevlerin uygulama ve davranışlarıdeğişir. İşte programlamanın bu çeşidi Kalıpla Programlama (Generikprogramlama) diye adlandırılır. Yani elde hazır bir işlev veya sınıf kalıbıvar, siz bu kalıbın değişkenlerine programlarınıza uygun veriler (bunlartip, herhangi bir değer veya başka bir kalıp dahi olabilir) aktararaksorunlarınızı çözüyorsunuz.Kalıplar ayrıca derleme esnasında kod seçiminde de kullanılabilir. Böyleceüretilen kodun davranışlarını etkilemek ve politikalarını kurgulamakmümkündür. Bu tür programlama, kalıp üst programlama (templatemetaprogramming) olarak adlandırılır.Kalıpla programlama, gelenekçi nesne yönelimli programlamayabenzemez. Nesne yönelimli programlama tip çokbiçimliliği (tippoliformizm) odaklıdır. Yani sınıflar, nesneler ve virtual işlevlergereklidir. Kalıp tabanlı programlama ise, değişken çokbiçimliliği(parametrik poliformizm) odaklıdır. Bir sınıf veya işlev, değişkenlerindenbağımsız olarak tanımlanabilir.Bir işlev bildirimi veya tanımı, bir sınıf bildirimi veya tanımı kalıpbildirimi olabilir. Kalıp bildiriminde bir veya daha fazla değişkenbulunabilir. Bu değişkenler değer, tip, veya başka sınıf kalıpları olabilir.Kalıp uygulamalarının çoğunda, kalıba basit bir ad verilerek ve kalıpdeğişkenlerine de; sabit açıklamalar, tipler veya kalıp dayançlarıyerleştirilerek işlem yapılır. İşte bu, kalıbın o anki değeridir, ya da kalıbınanlık durumudur (instantiating template). Bir kalıbın anlık durumu onunkullanım noktasında elde edilir veya, başka bir sınıf ya da işlev bildirimiolarak başka bir durum anında bildirilebilir. İşlev kalıp örneği işlevoluşturur, sınıf kalıp örneği ise içindeki bütün üyeleri ile birlikte bir sınıfoluşturur.

Kalıp İşlevleri

Yukarıda kalıplar üzerine bir giriş yaptık, şimdi kalıp işlevleri konusunda

Page 101: C++ 2 (Kitap - Selami KOÇLU)

ayrıntıya dalacağız. Aslında konu başlığı işlev kalıpları olmalıydı ama biz,onun sonuçları olan kalıp işlevleri demeyi uygun gördük. Örnek vererekkalıp işlevini anlatalım; iki int sayıdan daha küçük olanı bulan bir işlevyazalım;

int min(int a, int b){ return (a<b) ? a : b ;}//ilk örnek işlev

Şimdi de iki double sayıdan daha küçük olanı bulan işlevi yazalım;

double min(double a, double b){ return (a<b) ? a : b ;}//ikinci örnek işlev

Görüldüğü üzere işlev tipi değişmesine rağmen işlev gövdesi aynı kaldı.Bu kadar basit işlevler için kopyala yapıştır tekniği ile tipideğiştirebilirsiniz. Fakat karmaşık işlevlerde farklı tipler için kopyalayapıştır çözüm olmaz. O yüzden aşağıdaki kalıp, örneğimiz için çözümolur;

template <typename T>T min(T a, T b){ return (a<b) ? a : b ;}//Örnek işlevler için Kalıp işlevi

Bunu belirttikten sonra artık aşağıdaki uygulamayı gerçekleştirebilirsiniz;

c=min<double>(a, b) ;//kalıp işlevi kullanılarak ikinci örnek işlev //elde edilir

Siz üstte double yerine float veya int tipini de yazabilirsiniz, bu sizinkoşullarınıza bağlıdır.Kalıp işlevi olarak verdiğimiz örnekte aşağıdaki özellikleri hemenfarkedebilirsiniz;

--Kalıp tanım veya bildirimi, template anahtar kelimesi ile başlar.--template anahtar kelimesini köşeli ayraç işaretleri arasındaki liste takipeder. Bahsi geçen listeye kalıp değişken listesi denir. Bu listede birdenfazla öğe varsa aşağıdaki gibi yazılır;

Page 102: C++ 2 (Kitap - Selami KOÇLU)

typename T1, typename T2--Kalıp değişken listesinde şekli tip adı olarak T yi (aslı Type ama bizkısaca T dedik) görürüz. Bu tip adı, işlev tanımında yer alan şekli değişkenadına benzer. Şimdiye kadar şekli değişken adlarına sadece işlevlerderastladık. Değişkenlerin tipleri her zaman işlev tanımlanırken belirlenirdi.Kalıplar, şekli adlar konusunu bir adım daha ilerletti. Tip adlarını belli birşekle soktu. Burada typename tarafından belirtilen T, şekli tip adıdır vekalıp değişken listesinde kullanılır. T gibi şekli tip adına kalıp tipdeğişkeni (template type parameter) denir. Bundan başka tipi olmayan(=tipsiz) kalıp değişkenleri de (template non-type parameter) olabilir.Birazdan onları da ele alacağız. Yeri gelmişken hemen belirtelim; bazıkitaplarda template <typename T> açıklaması template <class T>biçiminde yazılmaktadır. Yani typename yerine class yazılmaktadır. Biztercihimizi typename olarak kullandık. Zira bu ifadelerde class açıklamasıson dönemlerde tamamen terkedilmiştir. --İşlev başlığı; kalıp tip değişkenlerinin bulunması yanında, normal işlevbaşlığının benzeridir. İşlev çağrıldığında o anki asıl değişkenlerin asıltipleri kullanılarak derleyici kalıp işlevini belirler. İşte bu noktada (yaniişlevin çağrıldığı) derleyici kesin işlevi belirler. Bu sürece anlık durum(instantiation) adı verilir. İşlev başlığı, dönüş tipini gösteren şekli tip adıkullanabilir. Bu özellik min işlevimizde kullanıldı. --İşlev değişkenleri T a ve T b olarak yazıldı. Bunun genel anlamı Ttipinde a ve b nesne veya değerleri demektir. Eğer burada T const& a veT const& b denilmiş olsaydı o zaman bu değişkenler (nesne veya değer)işlev tarafından değiştirilemez demekti.--Kalıp işlev gövdesi normal işlev gövdesine benzer. Şekli tip adlarıdeğişkenleri bildirmek veya tanımlamak için burada kullanılır. Bazenbaşka yörel değişkenlerde eklenebilir. Buna rağmen bazı kısıtlamalar yinede bulunur. İşlevin bir değer döndürebilmesi için, kopya yapıcı işlev ileişleç işlevlerini desteklemesi gerekir. Yani şekli tip T için aşağıdakikısıtlamalar yazılabilir;

** T, işleç işlevlerini desteklemesi gerekir.** T, kopya yapıcı işlevi desteklemelidir.

T, std::string olsaydı ostream bulunmadığı için, ne işleç işlevleri ne dekopya yapıcı işlev hiçbir zaman olmazdı.

Page 103: C++ 2 (Kitap - Selami KOÇLU)

Kalıp tanımları normal görüntü ve belirteç görünürlük kurallarına uyar.Şekli tip adları, kalıp tanımında aynı adı taşıyan daha geniş görüntülübelirteçleri etkisi altına alırlar.Şimdi tekrar değişken listesinde tanımlanan işlev değişkenlerini ele alalım.Eğer T yi T const& a olarak yazarsak fazladan kopyalama yapılmasıengellenmiş olur. Şimdi başka bir örnekle bazı konuların ayrıntısınagirelim;

template <typename Type>Type topla(Type const& solyan, Type const& sagyan){ return solyan+sagyan ;}

int main( ){ unsigned const& is=unsigned(4) ; cout<<topla(is, is)<<endl ;}

Yukarıdaki örnekte is, işaretsiz sabit (unsigned const)bir değere dayançolmaktadır. Bu, topla( ) işlevine değişken verisi olarak aktarılmaktadır.Burada solyan ve sagyan ın Type const& olan değerleri unsignedconst& ve Type ı unsigned yorumlanır. Başka bir seçenekte Type const&yerine Type& yazmaktır. Sabit (const) kullanmadan değişken belirtilince,geçici değerler işleve aktarılamaz. Tabii bu önemli bir dezavantajdır. Buyüzden aşağıdaki kod parçası derlenemez.

int main( ){ cout<<topla(string(“a”), string(“b”))<<endl ;}//HATA derlenemez. Zira string const&, string& i başlatamaz.

Ama aşağıdaki kod derlenebilir;

int main( ){ string const& s=string(“a”) ; cout<<topla(s, s)<<endl ;}//Derleyici Type ı string const olarak alır.

Şimdi bu örneklerden elde ettiğimiz sonuçları yorumlayalım;

1- Kalıp değişkenleri gereksiz kopyalamalardan korunmak için

Page 104: C++ 2 (Kitap - Selami KOÇLU)

Type const& değişkenler olarak belirtilmelidirler.2- Kalıp mekanizması oldukça esnek bir uygulamadır. Bu mekanizmadaasıl tiplere bağlı olarak şekli tipler basit (int, float, char vs), const, vegöstergeç vs. tipler olarak yorumlanır. Ana kural; şekli tipin asıl tip içinmaske olarak kullanılmasıdır. Aşağıda değişkenin Type const& olaraktanımlandığı bazı örnekler verildi;

Değişken tipi Type==---------------- ----------------unsigned const ....... unsignedunsigned ...... unsignedunsigned * ...... unsigned *unsigned const * ..... unsigned const *

Kalıp işlevine ikinci örnek olarak aşağıdaki kodları inceleyelim;

template <typename Type, unsigned Size>Type sum(Type const (&array)[Size]){ Type t=Type( ) ; for(unsigned idx=0; idx<Size; idx++) t+=array[idx] ; return t ;}

Evet şimdi daha önceki işlev kalıbından farklı bir durumla karşı karşıyayız.1- Kalıp değişken listesinde iki öğe var. İlk öğe kalıp işlevleri için çok iyibilinen typename ama ikincisi çok özel bir öğe; unsigned. Kalıp değişkenlistesinde kullanılan özel tip değişkenler (yani şekli olmayan=non-formal),tipsiz kalıp değişkeni (template non-type parameter) olarak adlandırılır.Kalıp tipsiz değişkeni sabit açıklamadır, ve kalıbın anlık durumuncasaptanmak zorundadır, ve unsigned benzeri varolan tiplerce belirtilir.2- İşlev başlığına bakıldığında bir değişken görülür;

Type const (&array)[Size]

Bu değişken, Type tipinde Size öğelerine sahip diziyi dayanç alarak birdizi tanımlar. Ve bu değiştirilemez.3- Değişken tanımında hem Type hem de Size kullanılır. Gerek Typegerekse Size kalıp değişkenleridir. Derleyici derleme esnasında sum( )kalıp işlevi çağrıldığında unsigned değeri mutlaka bulunmalıdır. Bu

Page 105: C++ 2 (Kitap - Selami KOÇLU)

yüzden Size mutlaka sabit bir değer olmalıdır. Sabit (const) açıklama,tipsiz kalıp değişkenidir. Ve adı kalıp değişken listesinde yer alır.4- Kalıp işlevi çağrıldığı zaman derleyici hem Type ın açık değerini hemde Size ın değerini elde edebilmelidir. sum( ) işlevi tek değişkene sahipolduğu için derleyici sadece, Size değerini işlevin asıl değişkeninden eldeeder. Aslında elde edilen değişken Type öğelerine göstergeçten ziyade birdizi ise (bilinen ve sabit büyüklükte) böyle yapılabilir. Bu yüzdenaşağıdaki örnekte birinci satır derlenebilir ama ikincisi derlenemez.

int main( ){ int values[5] ; int* ip=values ; cout<<sum(values)<<endl ; //derlenir cout<<sum(ip)<<endl ; //HATA derlenemez}5- İşlev içinde t yi varsayılan değerle başlatmak için Type t=Type( )deyimi kullanılır. Burada belli bir değer (sıfır=0 benzeri) kullanılamaz.Herhangi bir tipin varsayılan değeri belli bir sayı kullanmak yerinevarsayılan yapıcı işlevce belirlenir. Tabii, her sınıf yapıcı işlevine birdeğişken olarak sayı kabul etmez. Ama bütün tipler, hatta temel yerleşiktipler de, varsayılan yapıcı işlevleri destekler, hatırlatalım. (aslında bazısınıflar varsayılan yapıcı işlevi hiç kullanmaz, ama çoğu bunun dışındadır).Yerleşik tiplerin varsayılan yapıcı işlevleri değişkenleri sıfır (=0) veyafalse (=yanlış) ile başlatır. Bundan dolayı Type t=Type( ) gerçek birbaşlatmadır. t, Type varsayılan yapıcı işlevince başlatılır. Yani Typekopya yapıcı işlevi kullanılarak Type( ) kopyası t ye atanarak değil. Ayrıcaburada seçenek olarak Type t(Type( )) yazım biçimi de kullanılabilir.6- İlk kalıp işlevi ile karşılaştırıldığında sum( ), Type sınıfında belli bazıpublic üyelerin var olduğunu kabul etmektedir; operator+=( ) ve kopyayapıcı işlev gibi.

Sınıf tanımlarında olduğu gibi kalıp tanımlarında da using yönerge vebildirimleri bulunmaz.

Değişkenden Gelenler

Bu bölümde kalıp işlevi çağrıldığında, kalıp tip değişkenlerinin asıltiplerinin, derleyici tarafından belirlenmesi sürecine odaklanacağız. Busürece kalıp değişken bulunması (template argument deductions) denilir.

Page 106: C++ 2 (Kitap - Selami KOÇLU)

Daha önce anlatıldığı üzere derleyici, tek bir şekli kalıp tip değişkeni içinçok sayıda asıl değişken yerleştirebilir. Ama, böyle olsa bile bütünyerleştirmeler geçerli değildir. Özellikle işlev, aynı kalıp tip değişkenlistesinde birden fazla değişken olduğu zaman, derleyicinin kabul edeceğiasıl değişken tipleri oldukça sınırlıdır.Derleyici kalıp tip değişkenleri için asıl tipleri saptarken sadecedeğişkenlerin tiplerini gözönüne alır. Bu süreçte ne yörel değişkenler ne deişlevin dönüş değeri dikkate alınır. İşlev çağrıldığı zaman derleyici, sadecekalıp işlevinin değişkenlerini tam bir kesinlikle görebilir. Çağrı noktasındaişlevin yörel değişkenlerinin tipleri açık olarak görülemez, ve işlevindönüş değeri aslında kullanılmaz, veya eldeki kalıp tip değişkeninin alttipinin (veya üst tipinin) değişkenine atanır. Bu yüzden aşağıdaki örnektederleyici hiç bir zaman komik( ) işlevini çağıramaz, zira Type kalıp tipdeğişkeni için asıl tipi belirlemek mümkün değildir.

template <typename Type>Type komik( ) { //hiçbir zaman çağrılamaz return Type( ) ;}//HATALI

İşlevde eşdeğer kalıp tip değişkenlerinde genelde çok sayıda değişkenolunca, asıl tipler mutlaka aynı olmak zorundadır. İşte aşağıda;

void ikici_de(double a, double b) ;

int ve double kullanılarak çağrılabilir, int değişken ile olduğunda içerdederleyici tarafından double a çevrilir. Denk gelen kalıp işlevi, int veyadouble değişken ile çağrılmaz. Derleyici kendisini int i double a çevirmekonusunda zorlamaz, ve sonra Type ın double olduğuna karar verir.

template <typename Type>void ikici_de(Type const& p1, Type const& p2){}

int main( ){ ikici_de(4, 4.5) ; //derlenemez ; zira farklı asıl tipler}///:~

Derleyici kalıp tip değişkenlerinin aslını saptarken hangi çevrimleriyapmaktadır?. Değişken tip çevrimlerinden sadece üç tane tip gerçeklenir.

Page 107: C++ 2 (Kitap - Selami KOÇLU)

(ve dördüncüsü de belli (fixed) bir tipin işlev değişkenine yapılanıdır (yanikalıpsız işlev değişken tipinin)). Çevrimler sonunda asıl tiplereerişilemezse kalıp işlevi gözardı edilir. Bu çevrimleri şimdi verelim;-- solyan dönüşümleri; solyan değerinden sağyan değeri üretilir-- nitelik çevrimleri;sabit olmayan değişken tipine const özelleştiricisieklenir-- Bir sınıf kalıbının anlık durumundan ana sınıfa çevrim; çağrıda bir kalıptüretilmiş sınıfı sağlanmış olduğunda, kalıp ana sınıfını kullanma.-- Kalıpsız tip işlev değişkenleri için standart çevrimler. Bu kalıp değişkentip çevrimi değildir. Ama kalıp işlevlerinin arta kalan kalıpsız tipdeğişkenlerine karşılık gelir. Derleyici bu işlev değişkenleri için uygunolan herhangi bir çevrimi gerçekler. (örnek; int ten unsigned a, int tendouble a gibi).İlk üç çevrim birazdan ayrıntıları ile incelenecek.

Solyan dönüşümleri

Üç çeşit solyan (lvalue) dönüşümü vardır;

1- Solyandan sağyana çevrimler (lvalue-to-rvalue). Bu çevrim sağyan gerektiği zaman uygulanır. Solyan değişken olarakkullanılır. Bu da bir değişken bir işleve değer değişkeni olduğunda ortayaçıkar. Örnek;

template <typename Type>Type tersle(Type value){ return -value ;}int main( ){ int a=8 ; a=tersle(a) ; //solyan(a) sağyana (a kopyalanır)}

2- Diziden göstergeçe çevrimler (array to pointer) Bu çevrim göstergeç değişkenine bir dizi adı atandığı zaman uygulanır.Çoğunlukla göstergeç değişkenleri ile tanımlanan işlevlerde rastlanır. Butip işlevler çağrıldığında, sıklıkla diziler işleve atananlar olur. Dizi adresigöstergeç değişkenine atanır, ve tipi denk gelen kalıp değişken tipininbelirlenmesinde kullanılır. Örnek;

Page 108: C++ 2 (Kitap - Selami KOÇLU)

template <typename Type>Type topla(Type* tp, unsigned n){ return biriktir(tp, tp+n, Type( ));}int main( ){ int a[100] ; topla(a, 100) ;}

Bu örnekte a dizisinin adresi topla( ) işlevine aktarılır, tiplere göstergeçbeklenir. Anılan çevrim kullanıldığında, a nın adresi tp ye atanangöstergeç ve sonra Type ta int olur.

3- İşlevden göstergeçe çevrimler (function to pointer) Bu çevrim değişkeni, işleve göstergeç olan kalıp işlevlerinde kullanılır.Böyle bir işlev çağrıldığında, işlev adı değişken olarak tanımlanır. İşlevadresi değişken göstergeçine atanır. Ve kalıp tip değişkeni saptanır.Bunaişlevden göstergeçe çevrim denir. Örnek;

#include <cmath>template <typename Type>void call(Type (*fp) (Type), Type const& value){ (*fp) (value) ;}int main( ){ call(&sqrt, 2.0) ;}

Bu örnekte sqrt( ) işlevinin adresi call( ) işlevine aktarılır, Type döndürenişleve göstergeç beklenir, ve onun değişkeni de bir Type tır. İşlevdengöstergeçe çevrimi ile, sqrt adresi fp ye atanan bir göstergeç değeri veType ta double olarak belirlenir. Hemen açıklayalım burada 2.0 değeri 2yapılamaz, zira int sqrt(int) öntiplemesi bulunmaz. Ayrıca daha öncekianlattıklarımızdan, işlev değişkeni olarak Type (*fp)(Type) yerine Type (*fp)(Type const&) beklemektesiniz. Zira kalıp işlevdeğişkenlerinin tip saptamasında en uygun gösterim, değerler yerinedayançlardır demiştik. Bununla birlikte fp değişkeni Type, bir kalıp işlevdeğişkeni değildir, ama fp işlevinin değişkenini işaret eder. sqrt( ) işlevi

Page 109: C++ 2 (Kitap - Selami KOÇLU)

de double sqrt(double const&) değil, double sqrt(double) öntipinesahiptir. call( ) değişkeni fp mutlaka Type (*fp)(Type) olarakbelirtilmelidir. Bu çok açıktır.

Nitelik Çevrimleri

Nitelik çevrimleri göstergeçlere const (veya volatile) ekler. Bu çevrimkalıp işlev değişkeni açıkça const (veya volatile) kullanarak tanımlandığızaman uygulanır. İşlevin değişkeni const veya volatile varlığı değildir. Budurumda çevrim const veya volatile ekler, ve kalıbın tip değişkenini saptar.Örnek;

template <typename Type>Type tersle(Type const& value){ return -value ;}int main( ){ int a=10 ; a=tersle(a) ;}

Burada kalıp işlevinin Type const& value değişkenini görüyoruz, constType a bir dayanç. Bununla birlikte değişken bir const int değil, ama intdeğiştirilebilir. Nitelik çevrimi uygulanarak derleyici a nın tipine constekler, böylece Type const& value ile birlikte int const a olur ve Type taint olmak zorunda kalır.

Ana Sınıfa Çevrim

Kalıp sınıflarının inşasını daha sonra işleyecek olmamıza rağmen, kalıpsınıfları şimdiden kullanacağız. Örnek olarak; soyut kaplar (abstractcontainers) aslında kalıp sınıfları olarak tanımlanırlar. Bilinen sınıflar(kalıp sınıf olamyanlar) gibi kalıp sınıflarda da sınıf hiyerarşileri uygulanır.Bu konuların ayrıntıları kalıp sınıflar konusunda incelenecek. Bir kalıpsınıf diğerinden nasıl türetilir gibi.Kalıp sınıf türetimi daha sonra ayrıntıları ile inceleneceği için aşağıda elealınacak konu biraz soyut kalacaktır. Okur dilerse ilerdeki kalıp sınıftüretimini inceledikten sonra bunları okuyabilir.

Page 110: C++ 2 (Kitap - Selami KOÇLU)

Kalıp sınıfımız Vector, değişkenin hatırına, std::vector den türetildi.Kalıp işlevi, işlev nesnesi obj kullanılarak vector sıralaması yapmak içininşa edilmektedir.

template <typename Type, typename Object>void sortVector(std::vector<Type> vect, Object const& obj){ sort(vect.begin( ), vect.end( ), obj) ;}

std::vector<string> nesnelerini büyük harf küçük harf düzeninebakmaksızın sıralamak için CaseLess sınıfı aşağıdaki gibi inşa edilir;

class CaseLess{ public: bool operator( )(std::string const& before, std::string const& after)const } return strcasecmp(before.c_str( ), after.c_str( ))<0 ; }};

Şimdi değişik vector lar sortVector( ) işlevi ile sıraya dizilirler.

int main( ){ Vector<string> vs , Vector<int> vi ; sortVector(vs, CaseLess( )) ; sortVector(vi, less<int>( )) ;}

Örnekte Vector ler sortVector( ) işlevine değişken olarak aktarıldı. Sınıfkalıbının anlık durumundan ana sınıfa çevrim uygularken, derleyici Vectorü std::vector olacak diye düşünür, ve buradan da kalıbın tip değişkeniniçıkarır. Yani bu; Vector vs için std::string ve Vector vi için de intdemektir. Farklı kalıp değişken tip belirleme çevrimlerinin amacını anlambakımından lütfen iyi kavrayın. Amacın işlev verilerinin işlevdeğişkenlerine uyumlu olması değil ama, değişkenlere uyumlu verilerolması bilinmelidir. Çevrimler farklı kalıp tip değişkenlerinin asıl tiplerinisaptamak için kullanılır.

Page 111: C++ 2 (Kitap - Selami KOÇLU)

Kalıp Verilerini Saptama Algoritmaları

Derleyici aşağıdaki algoritmayı kalıp değişkenlerinin asıl tiplerinisaptarken kullanır;

** Sıra ile, kalıp işlevinin değişkenleri çağrılan işlevin değişkenlerikullanılarak tanımlanır.** Kalıp işlev değişken listesinde yer alan kalıp değişkenlerinin her biriiçin, kalıp tip değişkeni ile denk gelen verinin tipi uyumlu olmalıdır. (yani;veri int a ise Type, int ve işlev değişkeni de Type& value olur). ** Veri tipleri kalıp tip değişkenlerine uydurulurken, gereken yerlerdekalıp tip değişkenleri için üç (=3) tane çevrim yapılır.** Çok sayıda işlev değişkeni özdeş kalıp tip değişkenleri ile kullanılırsa,saptanan kalıp tipleri mutlaka aynı olmalıdır. Böylece bir sonraki kalıpişlevi, int ve double veri ile çağrılamaz.

template <typename Type>Type add(Type const& lvalue, Type const& rvalue){ return lvalue+rvalue ;}

Bu kalıp işlevi çağrıldığı zaman, aynı iki tip çağrılmak zorundadır (tabii üçtane çevrime izin verilmiş olsa bile). Kalıp tip çıkarım mekanizması özdeşkalıp tipleri için özdeş asıl tipleri sağlayamazsa, o zaman kalıp işlevininherhangi bir andaki değeri elde edilemez.

Kalıp İşlev Bildirimi

Şimdiye kadar sadece kalıp işlevlerini tanımladık. Hiç biri ciddiolmamasına rağmen, çeşitli nedenlerden dolayı çok sayıda kaynakdosyasında, kalıp işlev tanımları bulunur. Bunlar hakkında bilgi sahibiolmak yararlıdır.

** Sınıf arayüzleri gibi kalıp tanımları da, genellikle başlık dosyalarınıniçinde yer alırlar. Başlık dosyasında yer alan bir kalıp tanımı, derleyicitarafından her okunduğunda, derleyici tanımı mutlaka tam olarakişlemlemelidir, hatta kalıba gereksinim olmasa bile. Bu etkinlikderleyicinin hızını azaltır. Kalıpların işlemlenmesi işi derleyici için

Page 112: C++ 2 (Kitap - Selami KOÇLU)

oldukça yorucudur.** Bir kalıp işlev değerinin saptandığı (yani durumunun belirlendiği) hersefer, kod sonuç nesne modülünde ortaya çıkar. Bununla birlikte birkalıbın birden fazla durum değeri varsa, çok sayıda nesne dosyasındakionların kalıp değişkenleri için aynı asıl tipler kullanılır, daha sonra ilişimcifazlalık durum değerlerini eler. Son programda asıl kalıp tipdeğişkenlerinin belli bir bölümü için sadece bir durum değeri kullanılır.Bundan dolayı ilişimcinin gerçekleştireceği ek bir görev daha olur(gereksiz durum değerlerini eleme), ve bundan ötürü ilişim süreci yavaşlar.** Bazı durumlarda tanımların kendileri gerekmez, ama sadece kalıplaragöstergeçler veya dayançlar istenir. Böyle durumlarda tam kalıptanımlarının işlemlenmesi için derleyicilerin gerekmesi, derlemesüreçlerini lüzumsuz derecede yavaşlatmaktadır.Değişik kaynak dosyalarına tekrar tekrar kalıp tanımlarını koymak yerine,kalıplar ayrıca bildirilebilir. Kalıplar bildirildiği zaman derleyici tarafındankalıp tanımları defalarca işlemlenmez ve özdeş durum değerlerioluşturulmaz. Tabii diğer başka bildirimlerde olduğu gibi, asıl uygulamabir yerlerde bulunmalıdır. Kütüphanlerdeki bilinen işlevlerden farklı olarakbu, şu anda kalıp işlevlerinde mümkün değildir. Sonuç olarak bu ağırgörev, yani gereken anlık durumların oluşturulması, yazılım mühendisininomuzlarına yüklenmiştir. Aşağıda bunu gerçekleştirmenin basit bir yolugösterilmiştir;Basit bir şekilde kalıp işlev bildirimi yapılır; işlev gövdesi noktalı virgülile yerine konur. Bu yol bilinen klasik işlev bildirimleri ile tıpa tıp aynıdır.Böylece add( ) kalıp işlevi aşağıdaki biçimde bildirilir;

template <typename Type>Type add(Type const& lvalue, Type const& rvalue) ;

Aslında daha öncede bu tür bildirimlere rastlamıştık. ios sınıfı ve ondantüretilmiş sınıfların öğelerinin anlık durumları gerekmeden iosfwd başlıkdosyası eklenir. Örnek olarak, bildirimi derlemek için;

std::string getCsvline(std::istream& in, char const* delim) ;

Artık string ve istream başlık dosyalarını eklemek gerekmez, sadece tek

#include <iosfwd>

yeterlidir. Bunun kullanılması, istream ve string eklenmesi ile bildirimin

Page 113: C++ 2 (Kitap - Selami KOÇLU)

derlenmesine göre zaman olarak dokuzda bir süre alır.

Anlık Durum Bildirimleri

Kalıp işlev bildirimleri programların derleme ve ilişim evrelerinihızlandırırsa, neticede programın ilişiminde kalıp işlevlerinin gerekli anlıkdurumları güvenli bir biçimde gerçeklenirmi acaba?.Tabii bunun için de bir çözüm bulunur; bildirimin başka bir biçimi. Bu,anlık durumun açıkça belirtilmesidir. Açık anlık durum bildirim öğelerişunlardır;

1- template anahtar kelimesi ile başlar, ama kalıp değişken listesibulunmaz.2- Daha sonra işlev dönüş tipi ve adı belirtilir.3- İşlev adını tip özellik listesi takip eder, bunlar büyük küçük işaretleriarasında yer alan tipler listesidir. Bunların her biri kalıp değişken listesindeyer alan kalıp tip değişkenlerinin asıl tiplerine denk gelirler.4- Açıklanan işlev değişken listesi noktalı virgülle sonlanır.

Bu, aslında bildirim olmasına rağmen, derleyici tarafından anlık durumungüvenli bir şekilde gerçeklenmesi için sağlanan işlevin özel bir çeşididir.Bir program için gereken kalıp işlevlerinin bütün anlık durumları, anlıkdurum bildirimlerini açıkça kullanarak tek dosyada toplanabilir. Anılandosya normal bir kaynak dosyası olmalıdır, kalıp tanım başlık dosyasınıiçermelidir, ve daha sonra gereken anlık durum bildirimlerini taşımalıdır.Şimdi anlattıklarımızla ilgili bir örnek verelim; daha önce tanımladığımız topla( ) işlevi için int, double, ve std:.string tipleri için anlık durumbildirimlerini gösteren dosyayı yazalım.

#inlcude “topla.h”#include <string>using namespace std ;template int topla<int>(int const& lvalue, int const& rvalue) ;template double topla<double>(double const& lvalue, double const&rvalue) ;template string topla<string>(string const& lvalue, string const&rvalue) ;

Program için gereken anlık durumlardan biri unutulursa tamirat oldukça

Page 114: C++ 2 (Kitap - Selami KOÇLU)

kolay yapılır; sadece listeye eksik olan anlık durumlar yazılır, programyeniden derlenir ve yeniden ilişimlenir.

Kalıp İşlevlerin Tip Durumları

Derleyici kalıp işlev tanımını okuduğu zaman kalıbın anlık durumu tespitedilmez. Burada anlık durumdan kastedilen; kalıbın o anki taşıdığı tipdeğeri demektir. Kalıp aslında derleyiciye kullanılmak üzere verilen birreçetedir. Aynı yemek tarifleri gibi. Siz elinizde bulundurduğunuz yemektarifini okurken yemeği yapmış olmazsınız.Peki öyleyse kalıp işlevi tam olarak ne zaman anlık tipine, yani anlıkdurumuna sahip olur?. Derleyicinin iki tane verisi kalıpların anlıkdurumunu saptar;

1- Gerçekten kullanıldıkları zaman (yani topla( ) işlevi iki tane unsigneddeğerle çağrıldığı zaman)2- Anlık durumlar kalıp işlev adresleri alındığı sırada saptanır. Örnek;

#include “topla.h”char (*toplaptr)(char const&, char const&)=topla ;

Kalıbın anlık durumunun derleyici tarafından saptanmasını sağlayanaçıklama, kalıbın anlık durum noktası olarak adlandırılır. Bu nokta, kalıpişlev kodu için önemli anlamlar taşır. Konunun önemi, ilerleyenbölümlerde ayrıntıları ile incelenecek. Derleyici her zaman kalıbın tip değişkenlerini net şekilde saptayamaz, yanimüphemlik oluşabilir. Böyle durumlarda derleyici, çözülmesini istediğibelirsizliği yazılım mühendisine bildirir. Şimdi aşağıdaki kodlara bakalım;

#include <iostream>#include “sumvector.h”unsigned komik(unsigned (*f)(unsigned* p, unsigned n)) ;double komik(double (*f)(double* p, unsigned n)) ;

int main( ){ std::cout<<komik(sumVector)<<std::endl ;}///:~

Page 115: C++ 2 (Kitap - Selami KOÇLU)

Yukarıdaki basit program derlendiği zaman derleyici çözümleyemediği birbelirsizliği bildirir. Böyle durumlardan sakınmak elbette mümkündür. Kalıp işlevlerinin anlıkdurumları, sadece belirsizliğin olmadığı durumlarda saptanmalıdır.Belirsizlikler, derleyici işlev seçim mekanizmasında çok sayıda işlevbulunduğunda olur. Birazdan bunlara da değineceğiz. Tabii bumüphemlikleri ortadan kaldırmak tamamen bize bağlıdır. Yukarıdabulunan belirsizlikler kabaca static_cast kullanarak çözülebilir. (çoksayıdaki seçenekten biri olarak, ama hepsi de kullanılabilir)

#include <iostream>#include “topla.h”int call(int (*f)(int const& lvalue, int const& rvalue)) ;double call(double (*f)(double const& lvalue, double const& rvalue)) ;int value( ){ std::cout<<call(static_cast<int (*)(int const&, int const&)>(topla)) <<std::endl; return 0 ;}///:~

Hemen belirtelim; mümkünse tip dökümlerinden kaçınılması lazımdır.Birazdan tip dökümlerinden nasıl kaçınılacağını anlatacağız.Bir önceki bölümde işlendiği üzere, ilişimci sonuç programda bir kalıbınözdeş anlık durumlarının hepsini bırakmaz. Asıl kalıp değişkenlerinden herbir küme için sadece bir anlık durumu bırakır. Şimdi bir örnekle ilişimcinindavranışını inceleyelim. İlişimcinin davranışını gözlemek için aşağıdakilerisıra ile yapacağız;

--Önce birden fazla kaynak dosya inşa ederiz. ** source1.cc call( ) işlevini tanımlar, int tip değişkenler için topla( )nın anlık durumunu tespit eder, topla( ) nın kalıp tanımını ekler, ve topla( ) nın adresini belirtir. İşte source1.cc ;

//:Source1.cpp#include <iostream>#include “topla.h”#include “pointerunion.h”void call( ){ PointerUnion pu={topla} ; std::cout<<pu.pv<<std::endl ;

Page 116: C++ 2 (Kitap - Selami KOÇLU)

} ** source2.cc aynı işlevi tanımlar, ama bir kalıp bildirimini kullanarak(anlık durum bildirimi değil) sadece uygun topla( ) kalıbını bildirir. İştesource2.cc

//source2.cpp#include <iostream>#include “pointerunion.h”

template <typename Type>Type topla(Type const&, Type const&) ;

void call( ){ PointerUnion pu={topla} ; std::cout<<pu.pv<<std::endl ;}

** main.cc topla( ) nın kalıbını içerir, call( ) işlevini bildirir, ve main( ) i tanımlar, topla( ) işlev adresi ile int tipi için topla( ) tanımlar.Ayrıca call( ) çağrılır. İşte main.cc ;

#include <iostream>#include “topla.h”#include “pointerunion.h”

void call( ) ;

int main( ){ PointerUnion pu={topla} ; call( ) ; std::cout<<pu.pv<<std::endl ;}

Kaynak kodların hepsi hedef kodları (.o uzantılı)elde etmek içinderlenir.Derlenen kodların boyutu derleyiciden derleyiciye değişir. Amasource1.o her zaman source2.o den daha büyük boyutttadır. Şimdi küçükdenememizi yapalım;main.o ile source1.o ile ilişimlendirince, sonuç programın boyutu bizimsistemimizde; 14514 bayttır. main.o ile source2.o ile ilişimlendirince, kalıp işlevinin gereken anlık

Page 117: C++ 2 (Kitap - Selami KOÇLU)

durumu birden fazla olmasına rağmen bu programda bir önceki ile aynıboyutta ve aynı sonucu vermektedir. Bu basit örnekten şu sonuç çıkmaktadır; ilişimci kalıp anlık durumlarınınaynı olanlarını, yani özdeş olanlarını kaldırır. Tekrar tekrar kullanılmasınıengeller. Kalıp bildirimleri kalıp anlık durumlarının elde edilmesinisağlamaz.

Açık Kalıp Tiplerin Kullanımı

Bir önceki kısımda, derleyici kalıbın anlık durumunu tespit ederken bazıbelirsizliklerle karşılaşabilir, gördük. Örnekte bulunan call( ) işlevininbindirimli sürümleri, değişkenlerin farklı tipleri beklenerek, her ikisi kalıpişlevinin anlık durumu tespit edilerek sağlandı. Bu tür ortaya çıkanbelirsizlikleri kaldırmanın en akıllıca yolu, static_cast tip dökümünükullanmaktır. Ama aklınızda bulunması gereken en önemli nokta; herzaman tip dökümlerinden sakınmaktır. static_cast tarzı tip dökümlerinden sakınmanın mecbur olduğu kalıpişlevlerinde açık kalıp tip verileri (explicit template type arguments)kullanılır. Açık kalıp tip verileri kullanıldığı zaman derleyici açıkça asılkalıp tip değişkenleri hakkında bilgilendirilir. Derleyici bu verileri kalıbınanlık durum tespitlerinde kullanır. Burada işlev adını, asıl kalıp değişkentip listesi takip eder, yine gerektiğinde işlevin veri listesince de takipedilebilir. Derleyici tarafından kullanılan asıl kalıp değişken listesindebulunan tiplerin asılları, işlevin kalıp değişken tip listesinin kalıp tiplerinedenk gelen, asıl tipleri saptamak içindir. Önceki örneği açık kalıp tipverileri kullanarak gerçekleştirelim;

#include <iostream>#include “topla.h”int call(int (*f)(int const& lvalue, int const& rvalue)) ;double call(double (*f)(double const& lvalue, double const& rvalue)) ;

int main( ){ std::cout<<call(topla<int>)<<std::endl ; return 0 ;}///:~

Kalıp İşlev Bindirimleri

Page 118: C++ 2 (Kitap - Selami KOÇLU)

Şimdi topla( ) kalıp işlevimize bakalım; bu işlev iki değerin biribirineeklenmesi için tasarlanmıştır. Eğer üç tane değeri ekelmek istersekyazacağımız kod aşağıdaki gibi olur;

int main( ){ topla(2, topla(3, 4)) ;}///~

Yerine göre böyle uygulamalar mükemmel sonuçlar verir. Bununlabirlikte, üç değerin düzenli olarak toplandığı durumlarda, bindirimli topla( ) işlevi daha yararlıdır. Bunun çözümü oldukça basittir; bindirimlikalıp işlevleri.Bindirimli sürümü tanımlamak için, çok sayıda kalıp tanımları tanım başlıkdosyasına yerleştirilir. Böylece topla( ) işlevi aşağıdaki gibi olur;

template <typename Type>Type topla(Type const& lvalue, Type const& rvalue){ return lvalue+rvalue ;}template <typename Type>Type topla(Type const& lvalue, Type const& mvalue, Type const&rvalue){ return lvalue+mvalue+rvalue ;}///:~

Bindirilmiş işlev basit değerlerle tanımlanmak zorunda değildir. Bütünbindirime uğratılmış işlevler gibi işlev değişkenlerinin belli bir kümesitanım için yeterlidir. Şimdi de bir vektörün öğelerini toplayan örneğiverelim;

template <typename Type>Type topla(std::vector<Type> const& vector){ return accumulate(vect.begin( ), vect.end( ), Type( )) ;}///:~

Kalıp bindirimleri, kendilerini işlevin değişken listesi ile sınırlandırmazlar.Kalıbın tip değişken listesinin bizzat kendisi de bindirime uğrayabilir.topla( ) kalıbının son tanımında ilk değişken olarak std::vectorbelirtilmişti, ama deque ve map yoktu. Doğal olarak bunların dabulunduğu bindirilmiş kalıp işlevleri inşa edilebilir. Peki bu işin sonu

Page 119: C++ 2 (Kitap - Selami KOÇLU)

nerede?. Şimdi gelin bu kap (container) sınıflarının ortak yanlarınabakalım. Eğer hepsinde ortak yan bulabilirsek, bu ortak özellikleredayanan bindirilmiş kalıp işlevi tanımlarız. Anılan kap sınıflarınınhepsinde ortak begin( ) ve end( ) üye işlevleri bulunur, ve onlar yineleyicidöndürürler. Bu kullanılarak, anılan üyeleri desteklemek zorunluluğundaolan kapları temsil eden, kalıp tip değişkeni tanımlayabiliriz. Amaunutulmaması gereken; basit kap tipi, hangi veri tipin anlık durumununkullanılacağını bize söylemez. Bu yüzden kap veri tipini gösteren ikincikalıp tip değişkeni gerekir, böylece kalıp tip değişken listesi bindirimeuğramış olur. Şimdi topla( ) kalıbının bindirimli durumunu yazalım;

template <typename Container, typename Type>Type topla(Container const& cont, Type const& init){ return std::accumulate(cont.begin( ), cont.end( ), init) ;}///:~

Bütün bindirilmiş sürümlerin bulunduğu durumda aşağıdaki işlevinderleyici tarafından derlenmesine başlayabiliriz;

using namespace std ;int main( ){ vector<int> v ; topla(3, 4) ; //1. deyim topla(v) ; //2.deyim topla(v, 0) ; //3.deyim}///:~

1. 1.deyimde derleyici iki aynı tipi, yani int kullanır. Bundan dolayı anlıkdurum olarak topla<int>( ) alınır. Bu bizim ilk topla( ) kalıbımızıntanımıydı.

2. 2. deyimde tek veri kullanıldı yani v. Gerektiğinde derleyici topla( ) tekdeğişkenli olsa bile bindirilmişini arar. std::vector sürümünü bulursakalıp tip değişkeninin int olduğunu saptar. O zaman anlık durumu; topla<int>(std::vector<int>const&)

3. 3.deyimde derleyici iki tane verinin bulunduğu değişken listesi ilekarşılaşır. Ama verilerin tipleri farklıdır. Bu yüzden topla( ) nın ilk kalıptanımı kullanılmaz. Onun yerine son kalıp tanımı farklı tiplerle

Page 120: C++ 2 (Kitap - Selami KOÇLU)

kullanılır. std::vector begin( ) ve end( ) üye işlevlerini destekler,derleyici de kalıp işlevinin anlık durumunu aşağıdaki gibi tespit eder;

topla<std::vector<int>, int>(std::vector<int> const&)

İki farklı kalıp tip değişkeni kullanarak topla( ) tanımı yapmak, ve dahasonra öbür uygulamaları hazırlamak, topla( ) işlevi için epey yorucu oldu.Şimdi de topla( ) kalıp işlevini iki farklı tipte değişkenle tanımlayalım;

template <typename T1, typename T2>T1 topla(T1 const& lvalue, T2 const& rvalue){ return lvalue+rvalue ;}

Bu kalıbı iki farklı tip değişkenle kullamak mümkün olamaz. Yani topla( )işlevinin anlık durumu iki farklı tipte veri ile elde edilemez. Derleyicitiplerle ilgili belirsizliği çözümleyemez. Yani dönüş tipi ne olacak bilemez.Aşağıdaki kodu derlemeye çalışırsak;

int main( ){ topla(3, 5.5) ;}///:~//HATA derlenemez

Derleyici sonucun int mi yoksa double mı olduğuna karar veremez.Buyüzden hata üretir.

Tekrar aşağıdaki üç değişkenli bindirilmiş işlevi ele alalım;

template <typename Type>Type topla(Type const& lvalue, Type const& mvalue, Type const&rvalue){ return lvalue+mvalue+rvalue ;}///:~

Burada işlev verilerinin özdeş olma zorunluluğu sanki dezavantaj gibigörünebilir. Yani üç tane int, üç tane double, veya üç tane string gibi.Bunu ortadan kaldırmak için işlevin bindirilmiş sürümünü yenidenyazabiliriz. Bu sefer herhangi tipte veri kabul edebilir. Tabii işlevprogramda çağrıldığı zaman, farklı tipler arasındaki operator+( ) mutlakatanımlanmış olmalıdır. Ve bunun başka yerlerde görünmesinde sakınca

Page 121: C++ 2 (Kitap - Selami KOÇLU)

olmamalıdır. İşte herhangi tipi kabul edebilen bindirilmiş sürüm;

template <typename Type1, typename Type2, typename Type3>Type1 topla(Type1 const& lvalue, Type2 const& mvalue, Type3const& rvalue){ return lvalue+mvalue+rvalue ;}///:~

Şimdi topla( ) çağıralım ;

topla(2, 3, 4) ;

Burada derleyici herhangi bir şüpheye mahal vermeden doğru tipişlemlerini gerçekler.Burada ana kural; bir kalıp işlevinin bindirilmiş sürümleri tanımlanırken,her bindirilmiş sürüm, kalıpların anlık durumları elde edilirkenbelirsizlikten korunmak için, kalıp tip değişkenlerinin tek bileşiminikullanmak zorundadır. İşlev değişken listesindeki kalıp tip değişkenlerininsırası önemli değildir. Aşağıdaki binarg( ) kalıbının anlık durumu eldeedilirken belirsizlikler ortaya çıkar. Hatırlatalım; belirsizlikten kastedilen,derleyicinin tip hakkında kesin karar verememesidir, yani derlemeyapamamasıdır.

template <typename T1, typename T2>void binarg(T1 const& first, T2 const& second){ }//vetemplate <typename T1, typename T2>void binarg(T2 const& first, T1 const& second){}//sadece T1 ile T2 yer değiştirdi

Burada belirsizliğin ortaya çıkması şaşırtıcı değildir. Kalıp tip değişkenlerişekli adlardır. Onların isimleri ne olursa olsun o kadar anlam taşımazlar.

Son olarak, bindirilmiş işlevler ya basit biçimde veya anlık durum olarakbildirilebilirler. Açık kalıp değişken tipleri de kullanılabilir. Örnek;

** Belli bir tipin kaplarını kabul eden topla( ) kalıp işlevinin bildirimi;

template <typename Container, typename Type>

Page 122: C++ 2 (Kitap - Selami KOÇLU)

Type topla(Container const& container, Type const& init) ;** Aynı işlev ama şimdi anlık durum bildirimi var (not: derleyici kalıbıntanımını önceden bilir).

template int topla<std::vector<int>, int>(std::vector<int> const& vect,int const& init)

** Derleyicinin çok sayıda olasılık arasında belirsizlikleri kaldırması için,açık kesin veriler kullanılır. Örnek;

std::vector<int> vi ;int sum=topla<std::vector<int>, int>(vi, 0) ;

Sapmış Tipler İçin Kalıp Uydurmak

İlk topla( ) işlev kalıbımız, iki aynı tip değişkenle tanımlanmış olup,operator+( ) ile kopya yapıcı işlevi destekleyen bütün tipler için, gayet iyiçalışır. Bununla birlikte bazı durumlarda bu gerekler karşılanmaz. Örnekolarak char* s ele alalım, ne operator+( ) ne de kopya yapıcı işlevbulunur. Derleyici bunu bilmez, ve basit kalıp işlevinin anlık durumunutespite çalışır;

template <typename Type>Type topla(Type const& T1, Type const& T2) ;

Burada derleme olmaz, zira operator+( ) göstergeçler içintanımlanmamıştır. Benzer durumlarda, kalıp tip değişkenleri ile asılkullanılan tipler arasında uyum olabilir. Fakat standart uygulamaduyarsızdır ve hatalar üretir.Sorunu çözmek için kalıbın açık özelliği tanımlanır. Bu, varolan tanımıiçin, özel asıl kalıp tip değişkenleri kullanarak kalıp işlevini tanımlar.Yukarıda anlatılan örnekte char const* için açık özelleştirme gerekir.Ayrıca tabii char* tipi içinde. Derleyici hala önceden anlatılan standart tipsaptama sürecini aynen kullanır. Böylece, bizim topla( ) kalıp işlevimizchar* verileri için uyumlu yapıldığında, dönüş tipi mutlaka char* olmakzorundadır, ve eğer veriler char const* ise o zaman da, dönüş tipi char const* olmalıdır. Bu durumlar için Type değişkeni uygun tipedönüşür. Type==char* için anlık durum işlevinin başlığı şöyle olur;

Page 123: C++ 2 (Kitap - Selami KOÇLU)

char* topla(char* const& T1, char* const& T2)

Bu durum istenmezse göstergeçleri veren bindirimli sürüm tasarlanır.Aşağıdaki kalıp işlev tanımında iki (const) göstergeç bulunur, ve dönüş iseconst göstergeç değildir.

template <typename T>T* topla(T const* T1, T const* T2){ std::cout<<”Pointers\n” ; return new T ;}///:~

Ama hala olmak istediğimiz yerde değiliz, this in bindirimli sürümü sabitT öğe göstergeçlerini kabul eder. Sabit olmayan T öğe göstergeçleri kabuledilmezler. İlk bakışta, derleyicinin nitelik çevrimi yapmaması sürpriz gibigelebilir. Ama derleyicinin buna ihtiyacı yoktur. Sabit olmayangöstergeçler kullanıldığı zaman, derleyici, iki özdeş veri tipi bekleyen topla( ) kalıp işlevinin, başlangıçtaki tanımını kullanır. Sabit olmayan göstergeçleri kullanan başka bindirilmiş sürüm tanımlamakzorundamıyız?. Mümkün ama bazı noktalarda açıktır ki amacı aşmışoluruz. Zira, aynı normal işlev ve sınıflarda olduğu gibi, kalıplarında iyitanımlanmış amaçları olmalıdır. Bindirilmiş kalıp tanımlarına bindirilmişkalıp tanımlarını eklemeye çalışmak kalıbı hızla bir kludge ye çevirir. Buyaklaşımı sakın ola takip etmeyin. En iyi yaklaşım; kalıbı esas amacınauygun inşa etmektir. Bu yerine göre özel durum uygulamalarına izinvermeli, ve kalıbın amacı hakkında ayrıntılı bilgilendirme yapmalıdır.Bütün bunların yanında bazı durumlarda, kalıp açık özelleştirmesi çokdikkate değer olur. topla( ) kalıp işlevimiz için karakterlere const ve non-const göstergeçlerin iki özelleştirmesi gerçeklenir. Kalıp açıközelleştirmesi aşağıdaki sıralama ile inşa edilir;

** template anahtar kelimesi ile başlar.** Sonra açılı ayraçlardan boş bir küme yazılır. Bu, öntipinitanımlayacağımız bir kalıbın mutlaka var olduğunu gösterir. err yaparsakveya kalıp olmazsa derleyici hata üretir.** Daha sonra işlev başlığı tanımlanır. Kalıp açık anlık durum bildirimigibi aynı yazım biçimi kullanılmak zorundadır; dönüş tipi, işlev adı, işlevdeğişken listesi kadar asıl kalıp tip değişkeni mutlaka doğru olmalıdır.** İşlev gövdesi, özel asıl kalıp değişken tipleri için gereken özeluygulamayı tanımlar.

Page 124: C++ 2 (Kitap - Selami KOÇLU)

Şimdi topla( ) kalıp işlevinin iki özel halini yazalım; biri char* diğeri char const* verilerini bekler.

template < > char* topla<char*>(char* const& p1, char* const& p2){ std::string str(p1) ; str+=p2 ; return strcpy(new char[str.length( )+1], str.c_str( )) ;}template < > char const* topla<char const*>(char const* const& p1, char const* const& p2){ static std::string str ; str=p1 ; str+=p2 ; return str.c_str( ) ;}///:~

Kalıbın bu tür özel durumları, diğer kalıp işlev uygulamalarının bulunduğudosyada yer alır.Özel kalıp durum bildirimleri de bilinen usulle yapılır. Yani işlevgövdesinin yerine, noktalı virgül konur.Burada çok önemli bir nokta var; template anahtar kelimesinden sonragelen içi boş açılı (< >) ayraçlar. Bunların burada bulunması çokönemlidir. Eğer bunlar olmasaydı, yazdığımız kalıp anlık durum bildirimiolurdu. Derleyici derleme işlemini sessizce sürdürürdü, ama çok daha uzunderleme süresi ile. Kalıbın açık özelleştirilmesi bildirilirken (veya anlık durum bildirilirken),eğer derleyici işlevin verilerinden tipleri belirleyebilirse, kalıp tipdeğişkenlerinin açık özelleştirmesinden vazgeçilebilir. char (const)*özelleştirme durumunda ayrıca aşağıdaki gibi bildirilebilirdi;

template < > char const* topla(char const* const& p1, char const* const& p2) ;

Burada template < > atlanabilirdi. Bununla birlikte bu bildirimden kalıpkarakteri çıkarabilirdi. Şimdi sonuç bildirim basit bir şey olur, yani sadecebasit işlev bildirimi gibi. Bu hata değildir; kalıp işlevleri ile kalıp olmayanişlevler birbirine bindirim yaparlar. Normal işlevler izin verilen tipçevrimleri açısından kalıp işlevleri kadar kısıtlı değildirler. Bu, kalıpişlevinin her seferinde normal işlevle bindirim yapabilme sebebidir.

Page 125: C++ 2 (Kitap - Selami KOÇLU)

Kalıp İşlevinin Seçim Mekanizması

Derleyici, bir işlev çağrısı ile karşılaştığı zaman, bindirilmiş hangi işlevinçağrılacağını, mutlaka tespit etmek zorundadır. Şimdi bu işlev seçimmekanizması anlatılacak.İncelememizde derleyiciden aşağıdaki main( ) işlevini derlemesiniisteyeceğiz;

int main( ){ double a=11.5 ; topla(a, 11.5) ;}///:~

Aşağıdaki altı (6) işlev bildirimini derleyici main( ) işlevini derlerkengörür.

template <typename Type> //1. işlevType topla(Type const& lvalue, Type const& rvalue) ;

template <typename Type1, typename Type2> //2. işlevType1 topla(Type1 const& lvalue, Type2 const& rvalue) ;

template <typename Type1, typename Type2, typename Type3> //3.işlev Type1 topla(Type1 const& lvalue, Type1 const& mvalue, Type2const& rvalue) ;

double topla(double lvalue, double rvalue) ; //4.işlevdouble topla(std::vector<double> const& vd); //5 işlevdouble divide(double lvalue, double rvalue) ; //6.işlev

Derleyici main( ) işlevini okurken hangi işlevin çağrıldığını aşağıdakiişlemlerden sonra saptar;

** Önce aday işlevler kümesi inşa edilir. Bu kümenin işlevleri;--- çağrı noktasından görülür.--- çağrılan işlevle aynı adı taşırlar.6.işlevin adı bu yüzden kümeden çıkarılır. Böylece derleyici için 1 den 5 ekadar olan işlevler kalır.

Page 126: C++ 2 (Kitap - Selami KOÇLU)

** İkinci olarak uygulanabilir işlevler kümesi inşa edilir. Uygulanabilirişlevler, asıl veri tipleri ve işlev değişkenlerinin tiplerini uyumlu kılmakiçin, tip çevrimleri uygulanabilir işlevlerdir. Bunun anlamı; veri sayısımutlaka uygulanabilir işlevin değişken sayısına uygun olmalıdır. 3. ve 5.işlevlerin değişken sayısı farklı olduğu için, bunlarda kümeden çıkarılır.Daha sonra 1. işlev çıkarılır, zira bunun da değişken sayısı uygun değildir.2. işlev uygundur zira Type1 double ile a verisi lvalue ile uyuşur ayrıcatip çevriminde const double a eklenir. Benzer şekilde 4 .işlev de uygundurzira, double dan float a standart çevrim yapılır. Her iki işlevin (2. ve 4.)ikinci verileri de doğrudan uyuşur. Bu yüzden, uygulanabilir işlevler 2. ve4. işlevlerdir.

** Üçüncü olarak, geri kalan işlevler tercihe göre sıralanır ve ilkikullanılır. Sıralama her bir işlev değişkenine puan vererek yapılır, ve bupuanlar bütün işlev değişkenleri boyunca eklenir. Tam uygunluk olursaişlev bir (1) puan alır, ve eğer ek çevrim gerekirse puan verilmez. Şimdikonuyu özetleyelim;

--- Kalıp işlevi için, topla<double, double> işlevinin anlık durumu alınır.Anılan işlevin değişkenleri işlev verileri ile tam uygunluk gösterir, buyüzden çevrim gerekmez. Her değişken bir (1) puan alır, böylece kalıpişlevi iki (2) puan almış olur.--- topla(float, double) işlevinde ikinci değişkenin tipi ikinci verinin tipiile tam uyumludur. Ama birinci verinin tipi için çevrim (double dan float a ) gerekir. Böylece toplam puan bir (1) olur.Sıralama süreci sonunda, kalıp işlevi kazanan olarak ortaya çıkar. Bundandolayı biri kullanılmak üzere seçilir. Anlık durumu tespit edilir ve main( )işlevince çağrılır.Siz çalışma olması için örnek programı yukarıdaki her bir işlev içinyazınız, ve sonra derleme ve ilişim işlemlerini gerçekleyiniz, bakalım nesonuçlar elde edeceksiniz. En üstte çok sayıda işlev olması, derleyiciyi otomatik olarak belirsizliğesürüklemez. Daha önce anlatıldığı gibi, en özelleşmiş işlev daha genelolanlara göre öncelik sahibidir. Böylece kalıp açık özelleşmesi daha genelolana göre öncelik alır, ve temel işlevler kalıp işlevlerine göre sıralamadadaha öndedirler. (ama ancak her ikisi de sıralama sürecinin en tepesindeise).Temel kural olarak şudur; uygulanabilir işlevler kümesinin tepesinde çoksayıda uygulanabilir işlev olduğu zaman, basit işlev kalıp anlık durumuortadan kaldırılır. Çok sayıda işlev kalırsa o zaman kalıp açık

Page 127: C++ 2 (Kitap - Selami KOÇLU)

özelleştirmesi kaldırılır. Eğer sadece tek işlev kalırsa, o seçilir. Aksidurumda belirsizlik ortya çıkar.

Kalıp İşlev Ve Anlık Durumlarının Derlenmesi

topla( ) kalıp işlevinin aşağıdaki tanımını inceleyelim;

template <typename Container, typename Type>Type topla(Container const& container, Type init){ return std::accumulate(container.begin( ), container.end( ), init) ;}

Bu kalıp tanımında, kabın begin( ) ve end( ) üye işlevleri kullanılarakstd::accumulate( ) işlevi çağrılır. container.begin( ) ve container.end( ) işlev çağrılarına kalıp tipdeğişkenlerine bağlı olacağı söylenir. Container'in arayüzünü görmeksizinderleyici, container'in girdi yineleyicileri döndüren üyeler begin( ) ve end( ) işlevlerinin olup olmadığını denetleyemez. std::accumulate dönüşdeğerlerini kullanır. Öte yandan std::accumulate in kendisi de bir işlev çağrısıdır, ve herhangibir kalıp tip değişkeninden bağımsızdır. Bunlar ise veriler olup işlevçağrısının kendisi değildir. Kalıp tip değişkeninden bağımsız olan kalıpgövdesindeki deyimlere, kalıp tip değişkenlerine bağlı değildirler denir.Derleyici bir kalıp tanımını okuduğu zaman, kalıp tip değişkenlerine bağlıolmayan bütün deyimlerin, yazımlarının doğruluğunu saptar. Yani bütünsınıf tanımlarını, bütün tip tanımlarını ve bütün işlev bildirimlerini vs.mutlaka izlemek zorundadır. Ki bunlar, kalıp tip değişkenlerine bağlıolmayan deyimlerde kullanılır. Bu koşul karşılanmazsa, derleyici kalıptanımını kabul etmez. Sonuçta yukarıdaki kalıp tanımlandığı zaman , öncenumeric başlık dosyası eklenmelidir, zira bu başlık dosyası std::accumulate( ) i bildirir. Diğer yandan, derleyici kalıp tip değişkenlerine bağlı deyimlerle ayrıntılıdenetimleri yapamaz, örnek olarak; begin( ) işlevinin var olup olmadığını,henüz belirtilmemiş container tipi için saptamak mümkün değildir. Budurumlarda derleyici üstünkörü denetimler gerçekler, gereken üyeler,işleçler ve tipler kabul edilerek sağlanır.Kalıbın anlık durumunun elde edildiği program kaynağındaki yerine, anlıkdurum noktası denir. Bu anlık durum noktasında, derleyici kalıp tipdeğişkenlerinin asıl tiplerini saptar. Bu noktada kalıp tip değişkenlerine

Page 128: C++ 2 (Kitap - Selami KOÇLU)

bağlı kalıp deyimlerinin yazım doğruluklarını belirler. Buradan şu anlamçıkarılır; sadece anlık durum elde etme noktasında, derleyici mutlakagerekli bildirimleri okumalıdır. Ana kural olarakta; derleyici kalıbın heranlık durum noktasında, bütün gereken bildirimleri (genellikle başlıkdosyaları) okumalıdır. Kalıp tanımının kendisi için ise, bunlar çok daharahat düzenlenebilir. Tanım okunduğu zaman, sadece kalıp değişkenlerinebağlı olmayan deyimler için gereken bildirimler mutlaka bilinmelidir.

Kalıp Bildirimlerinin YazımıKalıplar bildirilirken uyulması gereken yazım biçimleri özet olarak aşağıdaverilmiştir. Noktalı virgül yerine işlev gövdesi yerleştirilerek tanım halinegetirilebilir. Tanım haline getirilebilecekler, ayrıca ayraçlar arasındabelirtildi.

** Temel kalıp bildirimi (tanım mümkün); template <typename Type1, typename Type2>void fonksiyon(Type1 const& t1, Type2 const& t2) ;

** Kalıp anlık durum bildirimi (tanım yapılamaz) ;template void fonksiyon<int, double>(Type1 const& t1, Type2 const& t2);

** Açık tipler kullanılan kalıp (tanım yapılamaz) ;void (*fp)(double, double) = fonksiyon<double, double>;void (*fp)(int, int) = fonksiyon<int, int> ;

** Kalıp özelleştirmesi (tanım yapılabilir) ;template < >void fonksiyon<char*, char*>(Type1 const& t1, Type2 const& t2) ;

** Kalıp sınıflarında friend kalıp işlevleri bildirerek yapılan kalıpbildirimi (ayrıntılar bir sonraki bölümde incelenecek) ;friend void fonksiyon<Type1, Type2>(değişkenler) ;

Page 129: C++ 2 (Kitap - Selami KOÇLU)

Örnek Çözümler

1- İki sayıdan büyüğünü döndüren kalıp işlevi:

//temel/max.hpptemplate <typename T>inline T const& max<T const& a, T const& b>{ return a<b?b:a;}///:~

max kalıp işlevinin bir programda kullanımı:

#include <iostream>#include <string>#include “max.hpp”int main( ){ int i=40 ; std::cout<<”max(8, i): “<<::max(8, i)<<std::endl ; double a1=3.5 ; double a2=-7.2 ; std::cout<<”max(a1, a2): “<<::max(a1, a2)<<std::endl ; std::string s1=”aritmetik” ; std::string s2=”arit” ; std::cout<<”max(s1, s2): “<<::max(s1, s2)<<std::endl ;}///:~//çıktısı:max(8, i): 40max(a1, a2): 3.5max(s1, s2): aritmetik//

2- Yukarıdaki örnekte dikkat edilirse her iki değişkende aynı tip (örneğinmax(int, int) , max(double, double) vs.. Eğer değişkenler farklı tipteolursa yapılması gerekenler:

max(4, 4.5) ; //!!İlk T int ikinci T double. HATA çalışmaz.!!

Yazılması gereken:

Page 130: C++ 2 (Kitap - Selami KOÇLU)

max(static_cast<double>(4), 4.5) ; //şimdi oldu DOĞRUveyamax<double>(4, 4.5) ; //Bu da DOĞRU

Birazda kavramları örnekle açıklayalım:Kalıp değişkenleri:template <typename T> //T kalıp değişkenleridirÇağrı değişkenleri:.. max(T const& a, T const& b) // a ve b çağrı değişkenleridir.Anlık durum saptaması:max<double>(4, 4.5) //T nin anlık durumu double olarak saptanmıştır.

3- Şimdi de çok farklı tipleri içeren karşılaştırma çözümleri, yani işlevkalıp bindirimleri:

//temel/max2.cpp//iki int değerin büyüğüinline int const& max(int const& a, int const& b){ return a<b? b:a ;}//herhangi tipte iki değerin büyüğünü bulmatemplate <typename T>inline T const& max(T const& a, T const& b){ return a<b?b:a;}//herhangi tipte üç değerin büyüğünü bulmakinline T const& max(T const& a, T const& b, T const& c){ return max(max(a, b), c);}int main( ){ ::max(6, 40, 69) ; //üç verili kalıp çağrılır ::max(6.0, 40.0) ; //max<double> çağrılır (veriden çıkarım yapılır) ::max('a', 'b') ; //max<char> çağrılır (veriden çıkarım yapılır) ::max(8, 40) ; //iki int için kalıpsız yapı çağrılır ::max< >(8, 40) ; //max<int> çağrılır (veriden çıkarım yapılır) ::max<double>(8, 40) ; //max<double> çağrılır (veriden çıkarım yok) ::max('a', 40.5) ; //iki int için kalıpsız yapı çağrılır}///:~

Page 131: C++ 2 (Kitap - Selami KOÇLU)

4- Başka yararlı bir örnekte de C stringlerini ve max işlev bindirimlerinikullanalım.

//temel/max3.cpp#include <iostream>#include <string>#include <cstring>//herhangi tipte iki değerin büyüğünü bulma (dayançla çağrı)template <typename T>inline T const& max(T const& a, T const& b){ return a<b?b:a;}//İki C-string in büyüğünü bulmak. (değerle çağrı)inline char const* max(char const* a, char conat* b){ return std::strcmp(a, b)<0?b:a;}//Herhangi tipte üç değerden büyüğünü bulma (dayançla çağrı)template <typename T>inline T const& max(T const& a, T const& b, T const& c){ return max(max(a, b), c) ; //max(a, b), değerle çağrı kullanırsaHATA!}int main( ){ ::max(8, 27, 45) ; //Tamam doğru. const char* s1=”selami” ; const char* s2=”deniz” ; const char* s3=”sema”; ::max(s1, s2, s3) ; //HATA Çünkü Bunlar C -string.}

Page 132: C++ 2 (Kitap - Selami KOÇLU)
Page 133: C++ 2 (Kitap - Selami KOÇLU)

4. BÖLÜM

KALIP SINIFLAR (template class)

Kalıplar, işlevler de olduğu gibi sınıflar için de inşa edilebilir. Kalıp sınıfı,şöyle düşünülebilir; farklı tip verileri yönetmesi gereken sınıf. C++programlarında kalıp sınıflar yaygın şekilde kullanılır; 1. kitabımızda yeralan stack, vector, adı ayrıntılı şekilde ele alınan deque veri yapıları, kalıpsınıflar olarak tanımlanmışlardır. Kalıp sınıflar sayesinde, algoritmalar vealgoritmalarda kullanılan veriler, tamamen birbirlerinden ayrı çalışabilir.Belli bir veri tipi üzerinde çalışan belli bir veri yapısını kullanmak için,kalıp sınıfı tanımlandığında veya bildirildiğinde, sadece veri tipininbelirtilmesi yeterlidir. (örnek; stack<int> iStack )Birazdan kalıp sınıflarının inşa edilmesini inceleyeceğiz. Bir bakıma kalıpsınıfları, nesne yönelimli programlama ile rekabet eder. Burası genelliklebir şekilde kalıplara benzer mekanizmaların görüldüğü yerlerdir.Çokbiçimlilik programcının algoritma tanımlarını ileriye ötelemesinisağlar. Kısmi olarak uygulanan algoritmalarda ana sınıflardan yeni sınıflartüreterek, algoritmalarda işlenen veriler önce türetilmiş sınıflardatanımlanarak, ana sınıfta saf sanal işlevler olarak tanımlanmış olan üyelerlebirlikte, veriler yönetilir. Öte yandan kalıplar ise, veri özelliklerininbelirlenmesini ertelemeye izin verir, algoritmalar o arada çalışır. Bu enaçık şekilde soyut kaplarda (abstract container) görülür, algoritmalar tam

Page 134: C++ 2 (Kitap - Selami KOÇLU)

olarak belirlenir ama aynı zamanda, algoritmalarda kullanılan veri tipi tamolarak belirsiz kalır.Genelde kalıp sınıfları kullanmak kolaydır. Herhalde int lerin yığıtınıoluşturan

stack<int> iStack

yazmak yerine nesne yönelimli yaklaşım kullanarak ;

class iStack : public stack{//üyeler

yazmak daha zordur.Öte yandan, kalıp sınıfı ile kullanılan her farklı tip için bütün sınıfın butipe uygun anlık durumu tespit edilmelidir. Nesne yönelimli yaklaşımlabağlantı kurarsak bu, ana sınıfta varolan işlevlerin türetilmiş sınıftakullanılmasını (kopyalanmasından ziyade) andırır.

Kalıp Sınıfların Tanımlanması

Bu kısımda kalıp sınıflarının inşasını anlatacağız. Aslında çok sayıdayararlı kalıp sınıfı sistemlerde mevcuttur. Biz burada varolan bir kalıpsınıfının nasıl inşa edilmiş olduğundan çok, yeni yararlı bir kalıp sınıfınınnasıl inşa edileceği üzerinde duracağız.Şimdi, göstergeç gibi davranan nesne tanımlamamızı sağlayan auto_ptrsınıfını ele alalım. Aslında buna cingöz göstergeç de (smart pointers) deriz.Bu auto_ptr sınıfının kullanılması sadece uygun bellek yönetimine izinvermez, aynı zamanda göstergeç veri üyeler kullanan sınıf nesneleri inşasıtamamlanamadığı zaman, bellek kaçaklarını engeller. auto_ptr lerin bir dezavantajı vardır; sadece tek nesneler için kullanılabiliryani nesneler dizisine göstergeç için değil. Şimdi biz auto_ptr gibidavranan, ama nesne dizisi göstergeçlerini yöneten SK::auto_ptr kalıpsınıfını inşa edeceğiz. Varolan bir sınıfın kullanımını başlangıç noktasıyapmak, önemli bir tasarım prensibini gösterir; varolan bir kalıptan yenibir kalıp (sınıf veya işlev) inşa etmek, sıfırdan kalıp inşa etmeye göre çokdaha kolaydır. Bizim durumumuzda varolan modelimiz std::auto_ptr dir.Aşağıdaki üyelerin de sınıfımızda olmasını istiyoruz;

** SK::auto_ptr sınıfının nesnelerini oluşturacak yapıcı işlevler.** Yıkıcı işlevler.

Page 135: C++ 2 (Kitap - Selami KOÇLU)

** Bindirilmiş operator=( ) işleci.** Verilen dizi öğelerinin tespit ve ataması için operator[] ( ) .** Dayançtan kurtarma işleci (operator* ( )) haricindeki diğer bütünstd::auto_ptr üyeleri. Zira bizim SK::auto_ptr çok sayıda nesneyitutabilmektedir, bu yüzden dayançtan kurtarma işleci şüpheye mahalvermeyecek şekilde tanımlanamaz.

Gereken üyeleri saptadık, artık sınıf arayüzü inşa edilebilir. Kalıpsınıflarının tanımı, aynı kalıp işlevlerin de olduğu gibi, template anahtarkelimesi ile başlar. Daha sonra açılı ayraçlar içinde bulunan kalıp tip (veyatipi olmayan) değişkenlerinin listesi takip eder. Bu liste boş değildir.template anahtar kelimesini takip eden açılı ayraçlar arasında işaretlenmişliste C++ dilinde template ilanıdır. Bazı durumlarda değişken listesi boşolabilir, ve sadece işaretli ayraçlar bulunur. Sınıf arayüzü kalıp ilanını takip eder, ve burada şekli kalıp tip değişkenadları sabit ve tipleri gösterirler. Sınıf arayüzü her zamanki gibi inşa edilir.class anahtar kelimesi ile başlar ve noktalı virgül ile biter. Kalıp sınıf işlevleri ve kalıp sınıf yapıcı işlevleri inşa edilirken, normaltasarım gerekleri yerine getirilir; kalıp sınıf tip değişkenlerini Type yerineType const& olarak tanımlamak tercih edilir. Böylece büyük veriyapılarının gereksiz kopyalanması engellenmiş olur. Kalıp sınıf yapıcıişlevleri, yapıcı işlev gövdesinde üye atamak yerine, üye başlatıcılarıkullanmak gerekir, bu bileşik nesnelere ikili atamaları engeller. Böylecenesne varsayılan yapıcıyı bir kez kullanır, ve atamanın kendisi bir kezyapılır.İşte bütün üyeleri birlikte SK::auto_ptr sınıfımızın ilk sürümü;

namespace SK{ template <typename Data> class auto_ptr{ Data* d_data ; public: auto_ptr( ) : d_data(0){ } auto_ptr(auto_ptr<Data> &other){ copy(other) ; } auto_ptr(Data* data){

Page 136: C++ 2 (Kitap - Selami KOÇLU)

: d_data(data){ } ~auto_ptr( ){ destroy( ) ; } auto_ptr<Data> &operator=(auto_ptr<Data> &rvalue) ; Data &operator [] (unsigned index){ return d_data[index] ; } Data const& operator [] (unsigned index) const{ return d_data[index] ; } Data* get( ){ return d_data ; } Data* const get( ) const{ return d_data ; } Data* release( ); void reset(Data*) ; private: void destroy( ) { delete [] d_data ; } void copy(auto_ptr<Data> &other){ d_data( )=other.release( ) ; } Data& element(unsigned idx) const ; };}

Sınıf arayüzünün özellikleri şunlardır;

** Kalıp tipi Data normal tip olarak kabul edilirse, sınıf arayüzününherhangi özel bir karakteristiği hiç olmaz. Sadece eski tip sınıf arayüzügibi görünür. Bu da genellikle doğrudur. Kalıp sınıflar, bir veya iki basittip için inşa edilmiş sınıftan sonra çok daha kolay inşa edilebilir.Soyutlama evresini takiben gerekli bütün veri tiplerinin karşılıkları kalıplıveri tiplerine çevrilirler, ve bunlar kalıbın tip değişkenleri olur.

Page 137: C++ 2 (Kitap - Selami KOÇLU)

** Daha ayrıntıya girersek, bazı özel karakteristiklerde ayırdedilir. Sınıfkopya yapıcı değişkenleri ve bindirilmiş atama işleçleri basit auto_ptrnesnelerine değil auto_ptr<Data> nesnelerine dayanç olur. Kalıp sınıfnesneleri (veya dayançları veya göstergeçleri) her zaman belirlenmiş kalıptip değişkenlerine gerek duyar.

** Kopya yapıcıların ve bindirilmiş atama işleçlerin standart tasarımındanfarklı olarak; değişkenleri const olmayan dayançlardır. Bu, kalıp sınıfıolarak sınıfla yapılacak bir şeye sahip değildir. Ama auto_ptr tasarımınınkendisinin sonucudur. Hem kopya yapıcı işlev hem de bindirilmiş atamaişleci, ötekinin nesne göstergecini alır. Yani öteki nesneyi 0 (sıfır)göstergece çevirir.

** Normal sınıflarda olduğu gibi üyeler satıriçi (inline) olaraktanımlanabilir. Aslında bütün kalıp sınıf üyeleri satıriçi (inline) olaraktanımlanmak zorundadır. Önderlemeli kalıplar halen desteklenmemektedir.Öyle de olsa tanım, sınıf arayüzün içine veya sınıf arayüzün dışına (yanitakiben) konur. Ana kural olarak; basit sınıflarda olduğu gibi aynı tasarımprensipleri uygulanması gerekir. Ama bu sefer, arayüz okunabilirliği temelamaçtır. Arayüzdeki uzun uygulamalar arayüzü belirsiz hale getirir.

** Kalıp sınıf nesnelerinin anlık durumu tespit edildiği zaman, kullanılanbütün kalıp üye işlev tanımları, derleyici tarafından görülmek zorundadır.Her tanımın ayrı kalıp işlev tanım dosyasında saklandığı yere kalıpkarakteristikleri yerleştirilebilmelerine rağmen, o yol hep çok zordur,(derlemeyi hızlandırsa bile). Bunun yerine, kalıp sınıfları tanımlamanınklasik yolu; arayüz tanımı, bazı işlevleri satıriçi (inline) tanımlama, ve gerikalan kalıp işlevleri arayüzün hemen altında tanımlamaktır.

** Dayançtan kurtarma işleci (operator*( )) yanında herkesin bildiği operator [] ( ) üye çifti tanımlanır. Sınıf nesne dizilerinin boyutuhakkında bilgi sahibi olmadığında, bu üyeler dizi sınırları hakkındadenetleme yapamaz.

Şimdi sınıf arayüzünün ötesinde tanımlanan bazı üye işlevleri inceleyelim.

** copy( ) ve destroy( ) işlevleri göstergeç üye sahibi sınıflarınstandartlaşmasını teşvik amacı ile uygulamaya eklenmişti. Bu üyeler çokbasittirler.

Page 138: C++ 2 (Kitap - Selami KOÇLU)

** Bindirilmiş atama yapıcı işlevi, hala kendiliğinden atama için denetlemeyapmak zorundadır. Arayüz dışında tanımlandığında uygulama aşağıdakigibi olur;

namespace SK{ template <typename Data> auto_ptr<Data> &auto_ptr<Data>::operator=(auto_ptr<Data>&rvalue){ if(this!=&rvalue){ destroy( ) ; copy(rvalue) ; } return *this ; }}

-- Bu, asıl kalıp tanımıdır. Tanım olduğu için mutlaka template anahtarkelimesi ile başlamalıdır. İşlev bildirimi de mutlaka template ilebaşlamalıdır. Ama o arayüzün kendisi tarafından ima edilmiştir, yani taa enbaşında gereken kelime yer alır.-- Bütün oluşturulan auto_ptr tip kalıbın tip değişkenini içerir. Bu birmecburiyettir.

** release( ) ve reset( ) üyelerinin uygulamaları oldukça basittir. İşte onlar;

namespace SK{ template <typename Data> Data* auto_ptr<Data>::release( ){ Data* ret=d_data ; d_data=0; return ret ; } template <typename Data> void auto_ptr<Data>::reset(Data* ptr){ destroy( ) ; d_data=ptr ; }}

Şimdi sınıf tanımlandı, artık kullanılabilir. Sınıfı kullanmak için, mutlaka

Page 139: C++ 2 (Kitap - Selami KOÇLU)

onun nesnesinin belli bir veri tipi için anlık durumu saptanmalıdır.Örneğimiz; yeni bir std::string dizisi tanımlamaktadır. Bütün komut satırverilerini saklar. Daha sonra ilk komut satır verisi yazdırılır. Bir sonrakiadımda auto_ptr nesnesi, aynı tipteki başka bir auto_ptr i başlatmak içinkullanılır. İlk auto_ptr şimdi 0- göstergeçi, ikinci auto_ptr ise komut satırverilerini tuttuğu görülür.

#include <iostream>#include <string>#include <algorithm>#include “autoptr.h”using namespace std ;int main(int argc, char** argv){ SK::auto_ptr<string> sp(new string[argc]) ; copy(argv, argv+argc, sp.get( )) ; cout<<”ilk auto_ptr, program ismi: “<<sp[0]<<endl ; SK::auto_ptr<string> second(sp) ; cout<<”ilk auto_ptr, pointer degeri: “<<sp.get( )<<endl ; cout<<”ikinci auto_ptr, program ismi: “<<second[0]<<endl ; SK::auto_ptr< > iap(new int[10]) ; cout<<iap[0]<<endl ; return 0 ;}///:~

//Çıktı:ilk auto_ptr, program ismi:a.out ilk auto_ptr, pointer degeri: 0ikinci auto_ptr, program ismi: a.out//

Varsayılan Kalıp Sınıf Değişkenleri

Kalıp işlevlerinden farklı olarak kalıp sınıflarında kalıp değişkenlerinevarsayılan değerler verilebilir. Bu, hem kalıp tipi olan hem de kalıp tipiolmayan değişkenler için doğrudur. Eğer bir kalıp sınıfının değişkenlerininkalıp verileri belirlenmeden anlık durumu saptanırsa, ve eğer varsayılandeğişkenler tanımlanmışsa, o zaman varsayılanlar kullanılabilir. Bu türvarsayılanları belirlerken aklınızda bulunması gereken; sınıf anlıkdurumlarının çoğuna varsayılanların uygun olmasıdır. Örnek olarak

Page 140: C++ 2 (Kitap - Selami KOÇLU)

SK::auto_ptr kalıp sınıfına, kalıp tip değişken listesinde int varsayılanolarak atanır.

template <typename Data=int>

Varsayılanlar belirlenmiş olsa bile, derleyici kalıbın nesne tanımları ileilgili mutlaka bilgilendirilmiş olmalıdır. Böylece tanımlanmış varsayılandeğişken değerleri için kalıp sınıf nesnelerinin anlık durumları tespitedildiği zaman tip özellikleri atlanabilir, ama işaretli ayraçlar kalır.Böylece SK::auto_ptr sınıfı için bir varsayılan tip kabul edilerek, sınıfnesnesi aşağıdaki gibi tanımlanır;

SK::auto_ptr< > intAutoPtr ;

Sınıf arayüzünün dışında tanımlanmış kalıp üyeleri için hiç bir varsayılanbelirlenmemelidir. Kalıp işlevleri, hatta kalıp üye işlevleri, varsayılandeğişken değerlerini belirleyemez. Böylece örneğin; release( ) üyesi herzaman aynı kalıp özelliği ile başlar;

template <typename Data>

Bir kalıp sınıfı çok sayıda kalıp değişkeni kullanırsa, hepsine varsayılandeğerler verilir. Bununla birlikte, varsayılan işlev verileri gibi, bir kezvarsayılan değer kullanılır, bütün geri kalan değişkenler mutlaka kendivarsayılan değerlerini kullanırlar. Kalıp tip özellik listesi virgüllebaşlamamalıdır, ne de çok sayıda ardışık virgülle.

Kalıp Sınıflarının BildirimiKalıp sınıflarını bildirmekte mümkündür. Bu durumlar ilerde sınıfbildirimleri gerektiği zaman yararlı olur. Bir kalıp sınıfını bildirmek için,arayüz (zincirli ayraçlar arasındaki kısım) noktalı virgülle yer değiştirir.

namespace SK{ template <typename Type> class auto_ptr ;}

Burada varsayılan tipler ayrıca belirlenir. Bununla birlikte, varsayılan tip

Page 141: C++ 2 (Kitap - Selami KOÇLU)

değerleri, hem kalıp sınıf tanımında hem de kalıp sınıf bildiriminde aynıanda belirtilemez. Burada temel kural; varsayılan değerlerin bildirimlerdençıkarılması lazım geldiğidir, nesnelerin anlık durumları saptanırken kalıpsınıf bildirimleri hiç bir zaman kullanılmaz, ama sadece ileridekullanılacak olan uygun dayançlar hariç. Burada hemen şunu belirtelim;bu, basit sınıflardaki üye işlevlerin varsayılan değişken özelliklerindenfarklıdır. Bu tür varsayımlar üye işlevlerin bildirimlerinde belirtilmelidir,tanımlarında değil.

Şekli sınıf-tiplerin Üye Ve Tiplerini Ayırdetme

Kalıp tip adı bir tipe denk geldiği için, kalıbın tip adı ayrıca bizzat kalıbınkendisine veya sınıfa karşılık gelir. Şimdi kalıp tip değişkeni olaraktypename Container ı alan Handler kalıp sınıfını ele alalım, ve veriüyelerini saklayan da kabın begin( ) yineleyicisi (=iterator) olsun. Kalıpsınıfı Handler ın yapıcı işlevi, herhangi bir kabın (=container) desteklediğibegin( ) üyesini kabul ediyor. O zaman Handler kalıp sınıfımızın iskeleti ;

template <typename Container>class Handler{ Container::const_iterator d_it ; public: Handler(Container const& container) : d_it(container.begin( )) {}};

Bu sınıfı tasarlarken neleri dikkate aldık?.

** typename Container yineleyicileri destekleyen herhangi bir kabı(=container) gösterir.** Kap büyük ihtimalle begin( ) üye işlevini destekler. d_it(container.begin( )) ilk değer ataması (=başlatması) açık biçimdekalıbın tip değişkenlerine bağlıdır. Bu yüzden sadece basit şekilde, yazımbiçiminin doğruluğu bakımından sınanır.** Benzer şekilde, büyük ihtimalle kap, Container sınıfında tanımlananconst_iterator tipini destekler. Container const dayanç olduğu için, begin( ) tarafından döndürülen yineleyici basit yineleyici olmayıp

Page 142: C++ 2 (Kitap - Selami KOÇLU)

const_iterator dur.Aşağıdaki main( ) işlevini kullanarak Handler kalıp sınıfının anlıkdurumunu saptamaya kalktığımız da, hatalarla karşılaşırız;

#include <vector>#include “handler.h”using namespace std ;int main( ){ vector<int> vi ; PairHandler<vector<int> > ph(vi) ;}///:~

//Reported Error:handler.h:4:error :syntax error before ';' tolen//

Bunun anlamı, aşağıdaki satır sorun yaratmıştır;

Container::const_iterator d_it ;

Sorun şudur; kalıp tip değişkenlerini kullanırken, basit yazım biçimdenetimi, derleyicinin bir container ın bir Container e karşılık geldiğinekarar vermesini sağlayacaktır. Böyle bir Container, begin( ) üye işlevinigayet iyi destekler, bu yüzden container.begin( ) de yazım biçimibakımından herhangi bir sorun olmaz. Bununla birlikte, begin( ) üyesi özelbir Container tipi için aslında yoktur. Elbette begin( ) üyesinin varlığısadece Container in asıl tipi belirlendiği anda bilinir. Öte yandan,derleyici Container::const_iterator un ne olduğunu belirleyemez. Acayipbir Container üyesi olduğu düşünülür, yazımla ilgili basit denetim olumlusonuç vermez. Yani const_iterator Container üyesi olduğu süreceaşağıdaki yazım biçimi her zaman hatalıdır;

Container::const_iterator d_it;

Bilindiği gibi derleyici X::a yı a X nın üyesidir diye alır. AşağıdakiHandler yapıcı işlev uygulamasını kullanarak main( ) işlevini derlersekgörülen hata iletisi devamında bulunmaktadır.

PairHandler(Container const& container):

Page 143: C++ 2 (Kitap - Selami KOÇLU)

d_it(container.begin( )) { unsigned x=Container::ios_end ; } //Reported error: error: 'ios_end' is not a member of type 'std::vector<int, std::allocator<int> >'//

Buna benzer durumlarda, Container gibi sınıflarda, tip açıkça derleyiciyebildirilmelidir, bu da typename anahtar kelimesi ile gerçeklenir. Şimditypename kelimesi ile Handler sınıfını yeniden yazalım;

template <typename Container>class Handler{ typename Container::const_iterator d_it ; public: Handler(Container const& container) : d_it(container.begin( )) { }};

Ehh artık derleme de herhangi bir sorun çıkmaz.

Tipi Olmayan DeğişkenlerKalıp işlevlerinde gördüğümüz gibi, kalıp değişkenleri ya kalıp tipdeğişkenleridir, ya da kalıp tipi olmayan değişkenlerdir. Kalıp sınıfları tipiolmayan değişkenleri de tanımlar. Kalıp işlevlerinde kullanılan sabitolmayan değişkenlere benzer şekilde, bu tipi olmayan değişkenlerindeğerleri, nesnenin anlık değeri saptandığı esnada belirlenen sabitlerolmalıdır.Bundan başka, derleyici, değerleri, verileri yapıcı işleve aktararak eldeedemez. Örnek olarak şimdi SK::auto_ptr kalıp sınıfının ilaveten, tipiolmayan unsigned Size adında değişkeni olduğunu kabul edelim. Dahasonra Size değişkenini, Data tipinin Size öğeli dizisini tanımlamak içinyapıcı işlevde, onun değişkeni olarak kullanalım. Yeni SK::auto_ptr kalıpsınıfı aşağıdaki gibi olur;

Page 144: C++ 2 (Kitap - Selami KOÇLU)

namespace SK{ template <typename Data, unsigned Size> class auto_ptr{ Data* d_data ; unsigned d_n ; public: auto_ptr(auto_ptr<Data, Size> &other){ copy(other) ; } auto_ptr(Data2* data) : d_data(data), d_n(0) {} auto_ptr(Data const (&arr) [Size]) : d_data(new Data2[Size]), d_n(Size) { std::copy(arr, arr+Size, d_data) ; } .... };}///:~

Maalelesef yeni kurgumuz ihtiyaçlarımızı gideremez. Zira derleyici kalıbıntipi olmayan değişkenlerin değerlerini belirleyemez. Derleyicidenaşağıdaki main( ) işlevini derlemesi istendiğinde, gereken kalıp değişkensayısı ile asıl sayı arasındaki uyumsuzluk bilgi notu olarak iletilir. Yanidoğru derleme olmaz.

int main( ){ int arr[30] ; SK::auto_ptr<int> ap(arr) ;}///:~

//Error reported by the compiler: In function 'int main( )': error: kalıp verilerinin hatalı sayısı ( 1 yerine 2 olmalı)

Page 145: C++ 2 (Kitap - Selami KOÇLU)

error: 'template<class Data, unsigned int Size> class SK::auto_ptr' için sağlandı.//(açıklamaları türkçe yazmayı tercih ettik.)

Size varsayılan değere sahip tipi olmayan değişkenler arasına konsa bile,yine çalışmaz. Başka türlüsü açıkça belirtilmezse, derleyici varsayılanıkullanır. Başka şekli bilinmezse Size=0 olur, bu yüzden yukarıda ki main( ) işlevindeki arr dizisinin asıl büyüklüğü ile, varsayılan 0 değeriarasında uyumsuzluk ortaya çıkar. Varsayılan değeri kullanan derleyiciaşağıdaki hata bilgisini iletir;

In instantiation of 'SK::auto_ptr<int, 0>' :...error:creating array with size zero ('0')

Böylece kalıp sınıf, tipi bulunmayan değişkenler kullanmasına rağmen ,onlar mutlaka sınıf nesnelerinin tanımlandığında ki tip değişkenlerindeolduğu gibi, belirlenmek zorundadır. Varsayılan değerler tipi olmayandeğişkenler için belirlenebilir, ama o zaman varsayılan, tipi belli olmayandeğişken özelliği belirtilmeden bırakıldığında kullanılır. Yapıcı işlevi arayüzün dışında tanımlama çalışmaz. Arayüzün dışınayerleştirilmiş olsa da hala aynı kalıp işlevidir. Burada özellikle belirtmemizgereken; kalıp üye işlevleri arayüzün dışında tanımlandığı zaman,varsayılan kalıp tip (tipsiz) değişkenleri kullanılmaz. Kalıp işlevlerine(dolayısı ile kalıp sınıf üye işlevlerine) varsayılan kalıp tip (siz) değişkendeğerleri verilmezKalıp işlevlerinin tipi olmayan değişkenleri gibi, kalıp sınıflarının tipiolmayan değişkenleri de sadece sabitler olarak belirtilebilir.

** Sabit adresleri olan global değişkenler tipi olmayan değişkenelr içinveri olarak kullanılabilir.** Yörel ve dinamik olarak belleğe yerleştirilmiş olan değişkenlerinadresleri, kaynak kod derlendiği zaman derleyici tarafından bilinmez.Bundan dolayı bu adresler tipi olmayan değişkenler için veri olarakkullanılamaz.** Solyan (Lvalue) dönüşümlerine izin verilir; bir göstergeç tipi olmayandeğişken olarak tanımlanırsa, bir dizi adı belirtilir.** Nitelik çevrimlerine izin verilir; sabit olmayan nesneye bir göstergeç,const göstergeç olarak tanımlanmış tipi olmayan değişkenle kullanılabilir.** Terfilere izin verilir; daha kısa veri tipinin sabiti, daha geniş tipin tipi

Page 146: C++ 2 (Kitap - Selami KOÇLU)

olmayan değişken özelliği için kullanılabilir. (örnek int çağrıldığındayerine short, double çağrıldığında long kullanılabilir.)** Bütünleyen çevrimlere izin verilir; unsigned değişken belirtilirse, birint de kullanılır.** Değişkenler (variables), kalıp tipsiz (tipi olmayan) değişkenlerini(parameters) belirlemekte kullanılamaz, eğer değerleri sabit açıklamalardeğilse tabii. Bununla birlikte const kullanarak tanımlanmış değişkenlerise, değerleri hiçbir zaman değişmediği için kullanılabilir.

Veri olarak dizi kabul eden SK::auto_ptr sınıfının yapıcı işlevinitanımlamaya çalışmamıza rağmen, yapıcı işlev kodunda dizi boyutunukullanma izni yoktur, ama henüz seçenekler bitmiş değil. Artık bir sonrakibölümde amacımıza varmak için başka bir yaklaşım kullanacağız.

Üye KalıplarÖnceki bölümlerde tipi olmayan (=tipsiz) değişkenli kalıpları tanımlamagayretlerimiz, derleyici tarafından dizi öğelerinin sayısı ile başlatmaesnasında, başarısızlıkla sonuçlanmıştı. Zira yapıcı işlev çağrıldığı zaman,kalıp değişkenleri gizli olarak bulunup çıkarılamaz, yani kalıp sınıfınınnesnesi tanımlandığı zaman, onlar açık olarak belirtilmelidir. Yapıcı işlevçağrılmadan hemen önce kalıp değişkenleri belirtildiğinde, çıkarımyapılacak herhangi bir şey kalmaz, ve derleyici açık biçimde belirtilmişolan kalıp verilerini kolay bir şekilde kullanır. Öte yandan kalıp işlevleri kullanıldığı zaman, işlev çağrıldığındakullanılan veriler sayesinde asıl kalıp değişkenleri elde edilir. Bu yaklaşımbizim sorunumuzu halletmemiz için bir yol açar. Eğer yapıcı işlevinkendisi kalıp işlevinin kendisine üye yapılırsa, o zaman kalıp sınıfı tipiolmayan değişkenleri açıkça belirtmemize gerek kalmaksızın, derleyicitipsiz (=tipi olmayan) değişkenlerin değerlerini saptayabilir.Kendileri kalıp olan kalıp sınıfının üye işlevlerine (veya sınıfları), üyekalıplar adı verilir. Üye kalıplar aynı öteki kalıplara benzer şekildetanımlanırlar, yani template <typename ....> başlığı ile gibi.Önceki SK::auto_ptr(Data const (&array) [Size]) yapıcı işlevi üyekalıbına çevrilirken, kalıp sınıfının Data tip değişkeni kullanılır, ama üyekalıbı mutlaka kendisinin tipsiz (tipi olmayan) değişkeni ile birlikteolmalıdır. Yapıcı işlevin satıriçi yazımı aşağıdaki gibi olur;

template <unsigned Size>

Page 147: C++ 2 (Kitap - Selami KOÇLU)

auto_ptr(Data const (&arr)[Size]): d_data(new Data[Size]), d_dn(Size){ std::copy(arr, arr+Size, d_data) ;}

Üye kalıpların özellikleri aşağıdaki verilmiştir;

** Normal erişim kuralları uygulanır; genel program tarafındankullanılabilen yapıcı işlev, verilen bir veri tipinin SK::auto_ptr nesnesiniinşa eder. Aynı kalıp sınıflarında olduğu gibi, veri tipi mutlaka nesne inşaedildiği zaman belirtilmelidir. int array[40] dizisinden bir SK::auto_ptrnesnesi inşa etmek için yapacağımız tanım;

SK::auto_ptr<int> object(array) ;

** Sadece yapıcı işlev değil, herhangi bir üye de üye kalıp olaraktanımlanabilir.

** Kalıp üyeler, satıriçi (inline) olarak veya sınıf dışında tanımlanabilir.Sınıfın aşağısında üye tanımlandığı zaman, kalıp sınıfın değişken listesimutlaka kalıp üyenin kalıp işlev değişken listesinden önce gelmelidir.Örnek;

template <typrename Data>template <unsigned Size>auto_ptr<Data>::auto_ptr(Data const (&arr)[Size]): d_data(new Data[Size]), d_n(Size){ std::cout<<”sabit dizi\n” ; std::copy(arr, arr+Size, d_data) ;}

Kalıp üyesi, sınıfının altında tanımlandığı zaman;

-- Tanım, uygun isimalanı ortamında yapılmalıdır. Bundan dolayı,

Page 148: C++ 2 (Kitap - Selami KOÇLU)

isimalanında kalıp sınıflarını tanımlayan dosyaların düzeni;

namespace SomeName{ template <typename Type, .... > //kalıp sınıf tanımı class ClassName{ .... };

template <typename Type, .... > //satıriçi olmayan üye tanımı(ları) ClassName<Type, ......>::member(.....){ ........... }}///:~ //isimalanı kapatıldı

-- İki tane kalıp ilan edilmelidir; Önce kalıp sınıfının kalıp ilanıyapılmalıdır, daha sonra üye kalıbın kalıp ilanı yapılır. -- Tanımın kendisi, üye kalıbın uygun görüntüsünü belirlemelidir; üyekalıp, şekli kalıp değişken tipi Data için anlık durumu saptanmışSK::auto_ptr sınfının üyesi olarak tanımlanır. SK isimalanı içindebulunulduğu için işlev başlığı; auto_ptr<Data>::auto_ptr ile başlar-- Üye kalıplar, sınıflarının altında tanımlandığı zaman, uygulama vebildirimde yer alan şekli kalıp değişken adları aynı olmak zorundadır.

Şimdi hala küçük bir sorun kaldı. Sabit büyüklükteki bir dizidenSK::auto_ptr nesnesi inşa ederken, yukarıdaki yapıcı işlev kullanılmaz.Onun yerine

SK::auto_ptr<Data>::auto_ptr<Data* data>

yapıcı işlevi harekete geçirilir. Şimdiki yeni yapıcı işlev, üye kalıp değildir,SK::auto_ptr sınıfının çok özel bir yapıcı işlev sürümüdür. Her iki yapıcıişlev de dizi kabul ettiği için, derleyici auto_ptr(Data const (&array)[Size] den ziyade auto_ptr(Data*) çağırır.Bu küçük sorun da, üye kalıptaki auto_ptr(Data* data) yapıcı işlevi basitşekilde değiştirilerek halledilir. Böyle yapıldığında, kalıp tip değişkeniData olur. Kalan küçük ayrıntı ise, yani üye kalıpların kalıp değişkenleri,sınıflarının kalıp değişkenlerini gölgelemez. Data adının Data2 olarakyeniden adlandırılması bu inceliğe yardımcı olur. Şimdi auto_ptr(data*)yapıcı işlevinin (satıriçi) tanımını verelim, her iki yapıcı işlevin

Page 149: C++ 2 (Kitap - Selami KOÇLU)

kullanıldığı main( ) örneğine de bakabilirsiniz;

template <typename Data2>auto_ptr(Data2* data) //belleğe dinamik olarak yerleştirilmeli: d_data(data), d_n(0){}

//Her iki yapıcı işlevi çağıran main( );

int main( ){ int array[40] ; SK::auto_ptr<int> ap(array) ; SK::auto_ptr<int> ap2(new int[40]) ; return 0 ;}//:~

Static Veri ÜyelerKalıp sınıflarında static veri üyeleri tanımlandığı zaman, her yeni anlıkdurum için anlık durum tespit edilir. Aynı kalıp tipinin (veya tiplerinin)çok sayıda nesnesi tanımlandığında, üyeler static ise, buna uygun sadecebir üye bulunur. Örnek olarak aşağıdaki sınıfı ele alalım;

template <typename Type>class ClassMe{ static int s_objectsay ;};

Her özel tip için sadece bir tane ClassMe<Type>::s_objectsay bulunur.Bunla birlikte aşağıda, farklı nesnelerce bölüşülen sadece bir staticdeğişkenin anlık durumu saptanır.

ClassMe<int> ClassMeBir;ClassMe<int> ClassMeIki ;

static üyeleri arayüzde oluşturma, bu üyelerin aslında tanımlandığıanlamına gelmez; sadece sınıfları tarafından bildirilmiş olur, mutlaka başka

Page 150: C++ 2 (Kitap - Selami KOÇLU)

bir yerde tanımlanmalıdır. static üyeleri bulunan kalıp sınıfları için de, budurum değişik değildir. static üyelerin tanımları genellikle hemen kalıpsınıf arayüzünü takip eder. s_objectsay static üyesi aşağıdaki gibitanımlanır, yani hemen sınıf arayüzünün altında;

template <typename Type> //arayüden hemen sonra int ClassMe<Type>::s_objectsay=0 ; //tanım yapılır

Yukarıdaki durumda s_objectsay int olup, kalıp tip değişkeni Type danbağımsızdır.Liste benzeri inşaatlarda, sınıf nesne göstergeçlerinin kendileriningerektiği yerlerde, static değişkeni tanımlamak için kalıp tip değişkeniType mutlaka kullanılmalıdır. Aşağıdaki örneğe bakalım;

template <typename Type>class ClassMe{ static ClassMe* s_objectPtr ;};

template <typename Type>ClassMe<Type>* ClassMe<Type>::s_objectPtr=0 ;

Her zamanki gibi tanım, değişken adından geriye tanımın başlangıcınadoğru okunur; ClassMe<Type> sınıfının objectPtr si ClassMe<Type> ınnesnesine göstergeçtir.Sonuç olarak; kalıp tip değişkeninin static değişkeni tanımlandığı zaman,doğal olarak ilk değerinin sıfır (0) olmaması lazımdır. Varsayılan yapıcıişlev daha uygundur (burada Type( )) ;

template <typename Type> //s_type ın tanımı Type ClassMe<Type>::s_type=Type( ) ;

Sapmış Tipler İçin Kalıp Sınıf Özelleşmesi SK::auto_ptr sınıfı çok sayıda farklı tip için kullanılabilir. Bunların ortaközelliği, sınıfın d_data üyesine basit biçimde atanabilmeleridir. Örnek;auto_ptr(Data* data) gibi. Bununla birlikte bu iş her zaman göründüğükadar basit değildir. Data nın asıl tipi char* olursa ne olur?. char**örneklerinde olduğu gibi, data nın sonuç tipi, iyi bilinen; main( ) işlevinin

Page 151: C++ 2 (Kitap - Selami KOÇLU)

argv ve env, örnek; char** değişkenleri.Bu özel durumda biz sınıfın d_data üyesine yapıcı işlevin sırf yenidenatanması ile ilgilenmeyiz. Bütün char** yapısının kopyalanması ileilgileniriz.. Bunu gerçeklemek için, kalıp sınıf özelleştirmesi kullanılır.Kalıp sınıf özelleştirmesi, belli bir asıl kalıp değişken tipi için, kalıp üyeişlevlerin kullanılamadığı (veya kullanılması gerekmeyen) yerlerdekullanılır. Bu durumlarda asıl tipin özel ihtiyaçlarını gideren,özelleştirilmiş kalıp üyeleri inşa edilebilir.Kalıp sınıf üye özelleştirmeleri, varolan sınıf üyelerinin özelleşmesidir.Sınıf üyeleri bulunduğu için, özelleşme sınıf arayüzünün bir parçası olmaz.Daha ziyade bunlar, üyeler olarak arayüzden sonra tanımlanırlar. Açıkçatipleri belli edilerek üyeler yeniden tanımlanır. Bunlara ek olarak varolansınıf özelliklerindeki gibi, işlev öntipleri özellikleri için üye işlevlerinöntipleri tam olarak işlev öntiplerine uygun olmalıdır. Aşağıdaki tanımData=char* özelleşmesi için tasarlandı.

template < >auto_ptr<char*>::auto_ptr(char** argv): d_n(0) { char** tmp=argv ; while(*tmp++) d_n++ ; d_data=new char* [d_n] ; for(unsigned idx=0 ; idx<d_n ; idx++){ std::string str(argv[idx]) ; d_data[idx]=strcpy(new char[str.length( )+1], str.c_str( )) ; }}

Yukarıdaki özelleştirme, aşağıdaki SK::auto_ptr nesnesini oluşturmakiçin kullanılır;

int main(int argc, char** argv){ SK::auto_ptr<char* > ap3(argv) ; return 0;}

Kalıp üye özelleşme tanımı, yerine göre istisnai tipin kullanılmasını

Page 152: C++ 2 (Kitap - Selami KOÇLU)

sağlamasına rağmen, tek kalıp üye özelleşmesinin yeterli olmadığıdurumlar mümkündür. Bu durumlardan biri; char* özelleşmesi sırasında,özelleşmiş tip Data=char* için, kalıbın destroy( ) uygulamasının doğruolmamasıdır. Yani, çok sayıda üye belli bir tip için özelleştirildiğinde,ancak o zaman tam bir kalıp sınıf özelleşmesinden söz edilebilir. Tamamen özelleşmiş bir kalıp sınıfı aşağıdaki karakteristikleri taşır;

** Kalıp sınıf özelleşmesi kalıp sınıf tanımını izler. Bunların hepsindenöteye, derleyici özelleşmiş olanın ne olduğunu görebilmelidir ki, özelleşmeolsun.

** Bütün kalıp sınıf değişkenlerine belli tip adları verilir, veya (tipiolmayanlar için) belli değerler verilir. Bu belli değerler açık biçimde kalıpdeğişken özellik listesinde yer alır, yani, kalıp sınıf adını izleyen ayraçlararaına konur.

** Temel kalıp tanımında kullanılan temel kalıp değişkenlerindekiözelleşmiş tip ve değerleri, özelleşmiş kalıp üyelerinin tamamı belirler.

** Kalıp üyelerinin tamamı tanımlanmak zorunda değildir, ama,özelleşmenin genelliğini güvenceye almak için tanımlanması daha iyi olur,yani tanımlanması lazımdır. Eğer bir üye özelleşme dışında tutulursa, o üyeözelleşmiş tip veya tipler için kullanılamaz.

** Özelleşmede, ek üyelerde tanımlanabilir. Bunun yanında, temel kalıptada tanımlanan ek üyelerin, mutlaka temel kalıp sınıf tanımında karşılıküyeleri de (aynı öntipleri kullanarak, temel kalıp değişkenleri kullanılsa da)bulunmalıdır. Ek üyelerin tanımını derleyici sorun yapmaz, ve ayrıca sizinüyelere ait özelleşmiş kalıp sınıf nesnelerini kullanmanıza izin verir.

** Özelleşmiş kalıp sınıflarının üye işlevleri, ya onların özelleşmişsınıflarında tanımlanır veya özelleşmiş sınıfında bildirilir. Sadecebildirilmişlerse, tanımları özelleşmiş kalıp sınıf arayüzünün altındaverilmesi lazımdır. Böyle bir uygulama template < > ile başlayamaz, amahemen üye işlevin başlığı ile başlamalıdır.Aşağıda SK::auto_ptr kalıp sınıfının Data=char* tipi için tam özelleşmişhalini veriyoruz; özelleştirme temel kalıp sınıfını içeren dosyaya eklenir.Sadece bildirilmiş olan örnek üyelerin boyutunu küçültmek için, temelkalıpta kullanıldığı gibi, aynı uygulamalara sahip oldukları kabul edilir.

Page 153: C++ 2 (Kitap - Selami KOÇLU)

#includ <iostream>#include <algorithm>#include “auto_ptr1.h”namespace SK{ template < > class auto_ptr<char* >{ char** d_data ; unsigned d_n ; public: auto_ptr<char* >( ) : d_data(0), d_n(0) {} auto_ptr<char* >(auto_ptr<char* > &other){ copy(other) ; } auto_ptr<char* >(char** argv){ full_copy(argv) ; } //template<unsigned Size > //auto_ptr(char* const (&arr) [Size]) ~auto_ptr( ){ destroy( ) ; } auto_ptr<char* > &operator=(auto_ptr<char* > &rvalue) ; char*& operator[](unsigned index) ; char* const& operator[](unsigned index) const ; char** get( ) ; char* const* get( ) const ; char** release( ) ; void reset(char** argv){ destroy( ) ; full_copy(argv) ; } void additional( ) const //şimdi public bir ek üye {} private: void full_copy(char** argv){ d_n=0 ;

Page 154: C++ 2 (Kitap - Selami KOÇLU)

char** tmp=argv ; while(*tmp++) d_n++ ; d_data=new char* [d_n] ; for(unsigned idx=0; idx<d_n; idx++){ std::string str(argv[idx]) ; d_data[idx]=strcpy(new char[str.length( )+1], str.c_str( )) ; } } void copy(auto_ptr<char* >& other) ; void destroy( ) ; //uygulama yapıldı }; void auto_ptr<char* >::destroy( ){ while(d_n—) delete d_data[d_n] ; delete [] d_data ; }}

Kısmi ÖzelleştirmelerÖnceki bölümde kalıp sınıf özelleştirmelerinin mümkün olduğunu gördük. Görüldüki, hem kalıp sınıf üyeleri hem de bütün kalıp sınıflarıözelleştirilebilir. Bunlardan başka gördüğümüz özelleştirmeler, kalıp tipdeğişkenlerini özelleştirdi.Bu bölümde özelleştirmelerin başka bir türünü ele alacağız. Kalıpdeğişkenlerinin hem tip hem de sayı olarak özelleştirilmeleriniinceleyeceğiz. Çok sayıda kalıp değişkeni olan kalıp sınıfları için kısmiözelleştirmeler tanımlanır. Kısmi özelleştirme ile, kalıp tip değişkenlerininbir bölümüne (veya herhangi bir bölümüne) özel değerler verilir. Önceki kısımda kalıp tip değişkenlerinin özelleştirmelerini irdelemişken,tipi olmayan değişken özelleşmelerini burada göreceğiz. Kalıp tipiolmayan değişkenlerin kısmi özelleşmelerini, lineer cebirin bir dalı olanmatrikslerin bazı unsurlarını kullanarak açıklayacağız. Yaygın kullanıma göre matriksler, sayılarla doldurulmuş satır vesütunlardan oluşan bir tablodur. Kalıpları kullanmak için hemen bir açılışyapalım. Matrikste yer alan sayılar bildiğimiz double değerler olabilir.Aslında kompleks sayılarda olabilir. Kalıp sınıfında typename DataTypeta bulunur. Şimdi bazı matriks örneklerine bakalım; değerleri double olsun.

Page 155: C++ 2 (Kitap - Selami KOÇLU)

1 0 0 3x3 lük özdeş matriks 0 1 00 0 1 1.5 0 0 0 0 2.5 0 1.5 //2x4 lük matriks: 2 satır 4 kolon

4 6 8 9 0 //1x5 matriks satır matriks

Matriksler belli sayıda satır ve sütunlara sahiptir (bunlara matrikslerinboyutu denir), boyut matriksleri kullanırken değişmez, o yüzden bunlarıkalıp tipi olmayan değişken değerleri olarak alırız, DataType=doublematrikslerin çoğunda geçerli olduğundan, double varsayılan değer kabuledilir. Böylece Matriks kalıp sınıfımızın başlangıcı;

template <unsigned Rows, unsigned Columns, DataType=double>class Matriks .......

Matriksler üzerinde çok sayıda işlem tanımlanabilir. Toplanabilirler,çıkarılabilirler, çarpılabilirler. Şimdi bu işlemler üzerinde durmayacağız.Daha basit bir işlem üzerine yoğunlaşacağız; satır ve kolonlardakileri ayrıayrı toplama ve genel toplam. Örnekte matriksin kenar toplamları ve geneltoplamı gösterildi;

matriks satır kenar toplamı

1 2 4 7 5 6 7 18

kolon 6 8 11 25 (genel toplam) kenartoplamı

Kalıp sınıfımız için önereceklerimiz neler?.

Page 156: C++ 2 (Kitap - Selami KOÇLU)

** Önce matriks öğelerini saklayacak yer gerekli. Öğeler, DataTypetipinde, Rows adında satır, Columns adında kolon dizileri olaraktanımlanırlar. Matriks boyutları (satır ve kolon) temel öncelik olduğu içingöstergeç değil dizidir (array). Column öğelerinin vektörü (Matriks satırı)Row öğelerinin vektörü (Matriks kolonu) kadar sıklıkla kullanıldığı için,typedef ler de düşünülebilir. Sınıf arayüzünün başlangıcı aşağıdaki gibiolabilir;

public: typedef Matrix<1, Columns, DataType> MatrixRow ; typedef Matrix<Rows, 1, DataType> MatrixColumn ;private: MatrixRow d_matrix[Rows] ;

** Yapıcı işlevleri olması lazım; varsayılan yapıcı, ve örneğin; akıştanmatriksi başlatatn yapıcı işlev. Varsayılan kopya yapıcı işlev görevini tamolarak yerine getirdiği için kopya yapıcı işlev gerekmez. Benzer şekildebindirilmiş atama işleci veya yıkıcı işlev de gerekmez. İşte public bölümdebelirtilen yapıcılar;

Matrix(std::istream& str){ for(unsigned row=0; row<Rows; row++) for(unsigned col=0; col<Columns; col++) str>>d_matrix[row][col] ;}Matrix( ){ std::fill(d_matrix, d_matrix+Rows, MatrixRow( )) ;}

** Sınıfın operator[]( ) üyesi (ve const biçimi) sadece ilk dizini yönetir,bütün bir MatrixRow a dayanç döndürür. MatrixRow da geri kalanöğelerin yönetimini kısaca anlatacağız. Örneği kısa tutmak için, dizidesınır denetimleri yapılmayacak.

MatrixRow& operator[] (unsigned idx){ return d_matrix[idx];}

** Şimdi ilginç bir kısıma geldik; Matrix te kenar hesaplamaları ve geneltoplam. Kenar toplamalarını vektör olarak düşünelim. MatrixRow kolon

Page 157: C++ 2 (Kitap - Selami KOÇLU)

kenar toplamını, MatrixColumn satır kenar toplamlarını vermektedir.Satır kenar toplamı satırdaki öğelerin toplamı, kolon kenar toplamıkolondaki öğelerin toplamını verir. Yukarıdaki örneğe bakıp uygulamayıgörebilirsiniz. Kenar toplamlarını genel toplam olarak 1x1 lik matrikshalinde de yazılabilir. Artık konuyu anladınız, MatrixRow veMatrixColumn nesnelerini yönetebilmek için kısmi özelleştirmeyi inşaedebiliriz. Üyelerin uygulamaları aşağıda;

MatrixRow columnMarginals( ) const{ return MatrixRow(*this) ;}MatrixColumn rowMarginals( ) const{ return MatrixColumn(*this) ;}DataTypre sum( ) const{ return rowMarginals( ).sum( ) ;}

Kalıp sınıf kısmi özelleştirmeleri kalıp değişkenlerinden herhangi biriveya, bir grubu için tanımlanabilir. Bunlar kalıp tip değişkenleri veyabenzer şekilde, kalıp tipi olmayan değişkenleri için tanımlanabilir. İlkkısmi özelleştirmemiz temel matriks satır inşaatını (özellikle de kolonkenar hesaplamaları amacı ile, ama bununla sınırlı değil) tanımlar. İştekısmi özelleştirme inşaat aşamaları;

** Kısmi özelleştirme, kısmi özelleştirmede özelleşmemiş bütün kalıp tipdeğişkenlerini tanımlayarak başlar. Bu kısmi özelleştirme kalıp ilanı,herhangi bir varsayılanı belirtmez (DataType=double gibi). Zira,varsayılanlar temel kalıp sınıf tanımında belirtilir. Bundan öteye,özelleştirme temel kalıp sınıf tanımını takip etmek zorundadır, yoksaderleyici hangi sınıfın özelleştiğini bilemez ve bunu derleme esnasındasorun yapar. Kalıp ilanını takiben sınıf arayüzü başlar. Kalıp sınıf (kısmi)özelleşmesi olduğu için, sınıf adını, özelleştirmede belirtilen bütün kalıpdeğişkenleri için ana değerler veya tipleri gösteren kalıp tip değişkenlistesi izler. Ve kalıbın geri kalan değişkenleri için kalıbın temel tip (veyatipi olmayan) adları kullanılır. MatrixRow özelleştirmesinde Row 1olarak belirtilir, zira biz tek satırdan sözetmekteyiz. Hem Columns hem deDataType belitilecekler arasında kalırlar. MatrixRow kısmi özelleştirmesiaşağıdaki gibi başlar;

Page 158: C++ 2 (Kitap - Selami KOÇLU)

template <unsigned Columns, typename DataType> //varsayılanlaryokclass Matrix<1, Columns, DataType>

** MatrixRow tek satırın verisini taşır. Bu yüzden DataType tipindeColumn değerlerini saklamak için bir veri üyesi gerekir. Column sabitdeğer olduğundan, d_row veri üyesi dizi olarak tanımlanabilir.

DataType d_column[Columns] ;

** Yapıcı işlevlerin inşaatına biraz dikkat etmek lazım. Varsayılan yapıcıişlev basit. O, MatrixRow un veri üyelerini başlatır, DataType ınvarsayılan yapıcısını kullanarak ta;

Matrix{ std::fill(d_column, d_column+Columns, DataType( )) ;}

Bundan başka, temel Matrix nesnesinin kolon kenar hesaplarına sahipMatrixRow nesnesini başlatan bir yapıcı işleve gerek duyulur. Bu da,yapıcının özelleşmemiş Matrix değişkeni ile oluşturulmasını gerektirir.Buna benzer durumlarda ana kural, değişkenin genel yapısını koruyan üyekalıp tanımlamaktır. Temel Matrix kalıbı üç tane kalıp değişkeni istediğiiçin, ikisi kalıp özelleştirilmesince sağlandı, üçüncü değişken mutlaka üyekalıbın kalıp ilanında belirtilmelidir. Bu değişken temel Matrix in satırsayısına denk geldiği için, adına kısaca Rows deriz. Şimdi geldik ikinciyapıcı işlevin tanımına, bu da temel Matrix nesnesinin kolon kenarhesaplarını bulunduran MatrixRow un verilerini başlatır.

template <unsigned Rows>Matrix(Matrix<Rows, Columns, DataType> const& matrix){ std::fill(d_column, d_column+Columns, DataType( )) ; for(unsigned col=0; col<Columns; col++) for(unsigned row=0; row<Rows; row++) d_column[col]+=matrix[row][col] ;}

Yapıcı değişkenlerini belirleme biçimine dikkat edilmeli; Matrix kalıbınadayanç olduğu gibi, kısmi özelleştirmenin kalıp değişkenlerinin kendileri

Page 159: C++ 2 (Kitap - Selami KOÇLU)

kadar ek Row kalıp değişkeni de kullanılır.

** Ek üyeler gerçekte o anki ihtiyaçlar için istenmemektedir. Bindirilmişişleç operator[] ( ), MatrixRow veri öğelerine erişmekte işe yarar. Yine,const çeşit const olmayan çeşit gibi uygulanabilir. Uygulama aşağıda;

DataType& operator[] (unsigned idx){ return d_column(idx) ;}

Şimdi, tek satırı tanımlayarak yapılan kısmi özelleştirme kadar, temelMatrix sınıfını tanımlamaktayız. Derleyici satır özelleşmesini, Row=1kullanarak Matrix in tanımlandığı anlardan birinde seçer. Örnek;

Matrix<3, 6> matrix; //temel Matrix kalıbı kullanılırMatrix<1, 6> row ; //kısmi özelleştirem kullanılır

MatrixColumn için de kısmi özelleştirme benzer şekilde yapılır.

** Kalıp sınıf kısmi özelleşmesi yine kalıp ilanı ile başlar. Sınıf tanımınınkendisi, ikinci temel kalıp değişkeni için belli bir değer belirtir. Her birkalıp değişkeni için kısmi özelleştirme inşa edilebileceği gösterilir; sadeceilk veya sonuncu değil.

template <unsigned Rows, typename DataType>class Matrix<Rows, 1, DataType>

** Yapıcı işlevler tamamen MatrixRow yapıcılarına benzer şekildegerçeklenir. Bunun örneğini de artık siz yazın.!

** sum( ) ek üyesi MatrixColumn vektör öğelerini toplayarak tanımlanır.Bu uygulama accumulate( ) temel algoritması kullanılarak gerçeklenir.

DataType sum( ){ return std::accumulate(d_row, d_row+Rows, DataType( )) ;}

aşağıdaki matriks neye yol açar?.

Matrix<1, 1> cell ;

Page 160: C++ 2 (Kitap - Selami KOÇLU)

Bu MatrixRow özelleşmesimi yoksa MatrixColumn özelleşmesimi?.Cevap; hiçbiri. Tamamen belirsiz. Çünkü hem satır hem de kolon bir(farklı) kalıp kısmi özelleştirmesi ile kullanılabilir. Eğer böyle bir Matrixgerçekten istenirse, başka ikinci özelleştirilmiş kalıp tasarlanmakzorundadır. Bu kalıp özelleşmesi Matrix öğelerinin toplamında yararlıolduğu için, ayrıntılar aşağıda verildi;

** Bu kalıp sınıf kısmi özelleştirmesi ayrıca bir kalıp ilanına gerekduyar.Bu defa sadece DataType belirtilir. Sınıf tanımı iki sabit değeribelirtir. Kolon ve satır sayılarının her ikisi için de 1 (bir) değeri alınır;

template <typename DataType>class Matrix<1, 1, DataType>

** Özelleştirme yapıcı işlevlerin hepsini tanımlar. Yine, daha genelMatrix tipinden kaynaklanan yapıcı işlevler, üye kalıplar olarakgerçeklenir. Örnek;

template <unsigned Rows, unsigned Columns>Matrix(Matrix<Rows, Columns, DataType> const& matrix): d_cell(matrix.rowMarginals( ).sum( )){}template <unsigned Rows>Matrix(Matrix<Rows, 1, DataType> const& matrix): d_cell(matrix.sum( )) {}

** Matrix<1, 1>, DataType değeri etrafında bir örtücü olduğu için, dahasonraki üyelere erişmek için üyelere gerek duyulur. Bir tip çevrim işleciyararlı olabilir, ama derleyici çevrim işlecini kullanmazsa, ilgili değeri eldeetmek için ayrıca get( ) üyesine gerek vardır. İşte erişimciler (constçeşitleri yazılmadı);

operator DataType &( ){ return d_cell ;}DataType &get( ){

Page 161: C++ 2 (Kitap - Selami KOÇLU)

return d_cell( ) ;}

Aşağıdaki main( ) işlevi Matrix kalıp sınıfı ve özelleştirmelerininkullanım biçimini göstermektedir;

#include <iostream>#include “matrix.h”using namespace std ;int main(int argc, char** argv){Matrix<3, 2> matrix(cin) ;Matrix<1, 2> colMargins(matrix) ;cout<<”kolon kenarlari: \n” ;cout<<colMargins[0]<<” “<<colMargins[1]<<endl ;Matrix<3, 1> rowMargins(matrix) ;cout<<”satir kenarlari: \n” ;for(unsigned idx=0; idx<3; idx++) cout<<rowMargins[idx]<<endl ;cout<<”genel toplam: “<<Matrix<1, 1>(matrix)<<endl ;return 0 ;}///:~//girdiden üretilen çıktılar: 1 2 3 4 5 6

kolon kenarlari:9 12satir kenarlari:3711genel toplam: 21//

Kalıp Sınıfların Anlık DurumlarıBir kalıp sınıf nesnesi tanımlandığı zaman, kalıp sınıfının anlık durumusaptanmış olur. Bir kalıp sınıf nesnesi tanımlandığında veyabildirildiğinde, kalıp değişkenleri mutlaka açıkça belirlenmelidir. Bir kalıp sınıfı, varsayılan kalıp değişken değerlerini tanımladığı zaman, odurumda derleyici varsayılanları sağlasa bile, kalıp değişkenleri ayrıcabelirtilir. Kalıp işlevlerindeki gibi, kalıp değişkenlerinin asıl değerleri veya

Page 162: C++ 2 (Kitap - Selami KOÇLU)

tipleri, hiç bir zaman çıkarımda bulunarak elde edilemez. Kompleksdeğerlerden oluşan bir Matrix tanımlamak için aşağıdaki yapı kullanılır.

Matrix<3, 5, std::complex> complexMatrix ;

Aşağıdaki yapı double değerlerden oluşan Matrix tir.

Matrix<3, 5> doubleMatrix ;

Kalıp sınıf nesnesi, extern anahtar kelimesi kullanılarak bildirilebilir.Örnek olarak, aşağıdaki yapı complexMatrix i bildirir;

extern Matrix<3, 5, std::complex> complexMatrix ;

İşlev bildirimleri, kalıp sınıf nesneleri, göstergeçleri, dayançları olandeğişkenlere veya geri dönüş değerlerine sahip işlevlerden derlenmişse,kalıp sınıf bildirimi yeterlidir. Aşağıdaki kaynak dosyası, derleyici matrixkalıp sınıf tanımına rastlamasa bile derlenebilir. Temel kalıp sınıflarıözelleştirmeler (kısmi) kadar bildirilebilir. Daha da öteye, bir kalıp sınıfnesnesi, dayancı, veya değişkeni bekleyen veya döndüren işlevin kendisiotomatik kalıp işlevi olur. Bu, farklı kalıp değişkenlerin huylarınıbelirtirken gereklidir.

template <unsigned Rows, unsigned Columns, typenameDataType=double> class Matrix;

template <unsigned Columns, typename DataType>class Matrix<1, Columns, DataType>;

template <unsigned Columns, typename DataType>class Matrix<1, Columns, DataType> function(Matrix<Rows,Columns, DataType> mat){}

Derleyici, kalıp sınıflar kullanıldığında, önce onları derlemek zorundadır.Bu yüzden kalıbın anlık durumu saptandığı zaman derleyici kalıp üyeişlevleri bilmek zorundadır. Tabii bu, kalıp sınıf nesnesi tanımlandığında,kalıp sınıfının bütün üyelerinin anlık durumlarının saptanacağı anlamınagelmez. Derleyici sadece gerçekten kullanılan üyelerin anlık durumlarını

Page 163: C++ 2 (Kitap - Selami KOÇLU)

saptar. Bu, aşağıdaki iki yapıcı işlevi ve iki de üyesi bulunan Ayan sınıförneği ile gösterildi; bir yapıcı işlevin kullanıldığı ve bir üyenin çağrıldığımain( ) işlevi oluşturulduğunda, sonuç hedef dosya ve çalıştırılabilirprogramın akordunu yapabiliriz. Daha sonra, kullanılmayan yapıcı işlev veüyenin yorum dışı kalır ve sınıf tanımı değiştirilir. Yine, main( ) derlenirve ilişimlenir, elde edilen boyut daha öncekiler ile aynıdır. Sadecekullanılan üyelerin anlık durumlarının saptandığı başka uygulamalardamevcuttur. Bunlardan erişilen sonuç; sadece asıl kullanılan kalıp üyeişlevlerinin ilk değer ataması yapılır. Küçük bir deneme için kullanılanAyan kalıp sınıf örneği;

#include <iostream>template <typename Type>class Ayan{ Type d_data ; public: Ayan( ) : d_data(Type( )) {} void member1( ){ d_data+=d_data ; }//aşağıdaki üyeler ikinci program derlenmeden evvel gözardı edilir. Ayan(Type const& value) : d_data(value) {} void member2( ){ d_data+=value ; }};

int main( ){ Ayan<int> ayan ; ayan.member1( ) ;}///:~

Kalıp Sınıf Ve Anlık Durumların İşlemlenmesi

Page 164: C++ 2 (Kitap - Selami KOÇLU)

Daha önceki bölümde (kalıp işlevleri), kalıp değişkenlerine bağlı kodlarve, kalıp değişkenlerine bağlı olmayan kodlar arasındaki farklaranlatılmıştı. Aynı farklılıklar kalıp sınıfları tanımlandığında vekullanıldığında da geçerlidir.Kalıp tanımlandığı zaman, kalıp değişkenlerine bağlı olmayan kod,derleyici tarafından onaylanır. Örnek; kalıp sınıfındaki üye işlev qsort( )işlevini kullanırsa, o zaman qsort( ) kalıp değişkenine bağımlı değildir. Bunedenle derleyici qsort( ) işlev çağrısına rastladığında, daha öncedenmutlaka onu tanıyor olmalıdır. Uygulamada taşıdığı anlam ise; cstdlib(veya stdlib.h), işlemleyeceği kalıp sınıf tanımından önce, mutlakaderleyici tarafından işlemlenmelidir.Öte yandan, bir kalıp <typename Type> kalıp tip değişkeni tanımlarsa,bazı kalıp üye işlev dönüş tipi, örneğin;

Type member( ) ......

olur, daha sonra aşağıdaki durumların bulunduğu derleyicinin karşılaştığımember( ) üye işlevi veya member( ) in ait olduğu sınıflar arasındakifarkları görürüz;

__Kalıp sınıf nesnelerinin tanımlandığı noktada, yani kalıp sınıf nesnesininanlık durumunun saptandığı noktada, derleyici kalıp sınıf tanımını okur,member( ) benzeri üye işlevlerin doğru yazılıp yazılmadıklarını denetler.Bu yüzden derleyici, Type&& member( ) benzeri bildirim ve tanımlarıkabul etmez, zira C++ dayanca dayanç döndüren işlevleri desteklemez.Bundan başka, nesnenin anlık durumunu saptamada kullanılan asıl tipadının varolup olmadığı denetler. Bu tip adı, nesne anlık durumusaptandığı noktada derleyici tarafından mutlaka bilinmelidir.__Kalıp üye işlevlerin kullanıldığı noktada, yani kalıp üye işlevinin anlıkdurumunun satandığı noktada, Type değişkeni hala bilinmek zorundadır,Type kalıp değişkenine bağımlı member( ) deyimlerinin doğru yazılıpyazılmadığı denetlenir. Örnek olarak member( ) aşağıdaki deyimi ihtivaederse;

Type tmp(Type( ), 10) ;

bu yazım biçimi temelde geçerlidir. Bununla beraber, Type=int ile member( ) çağrıldığı zaman, anlık durum saptaması gerçeklenemez, çünkü

Page 165: C++ 2 (Kitap - Selami KOÇLU)

int, iki int değişken taşıyan yapıcı işleve sahip değildir. Hemen belirtelim,member( ) bulunan sınıf nesnesinin anlık durumu saptanırken, derleyiciiçin bu sorun olmaz; nesnenin anlık durum saptama noktasında, member( )üye işlev anlık durumu belirlenemez, ve böylece geçersiz int inşaatıanlaşılamaz.

Friend' lerin Bildirimi

Friend işlevler, sınıf destek işlevleri olarak inşa edilirler ve kendileri sınıfüyeleri olarak inşa edilemezler. Çıktı akışları için iyi bilinen yerleştirmeişleci (<<) bunun bir uygulama noktasıdır. Friend sınıflar çoğu kez yuvalısınıflarla bağıntılıdır, yani içerideki sınıf dışarıdaki sınıfı friend olarakbildirir (veya tersi). İşte yine destek mekanizmasına rastladık; içeridekisınıf dışarıdaki sınıfı desteklemek için inşa ediliyor.Hepimizin bildiği sınıflardaki gibi, kalıp sınıflarda da, başka işlevler vesınıflar friend olarak bildirilebilir. Bunun tersine de bildik sınıflar kalıpsınıfları friend olarak bildirebilir. Burada da friend, bildirilen sınıfın işlevyeteneğini artırmak, desteklemek, özel işlev kazandırmak ve sınıfbüyütmek amacı ile inşa edilir. Friend anahtar kelimesi herhangi tipsınıfta (bildik ve kalıp sınıflar), herhangi bir işlev veya sınıfı, friend olarakbildirmekte kullanılmasına rağmen, kalıp sınıflar kullanılırken aşağıdakidurumların ayırdedimelidir;

Bir kalıp sınıfı, kalıp olmayan bir işlev veya sınıfı friend olarakbildirebilir. Bu yaygın şekilde kullanılan friend bildirimidir. Örnek;ostream nesnelerinde kullanılan yerleştirme (<<) işlecidir.

Bir kalıp sınıfı, başka bir kalıp işlev veya sınıfı, friend olarak bildirebilir.Bu durumda friend kalıp değişkenleri mutlaka belirlenmek zorundadır.Friend in kalıp değişken asıl değerleri friend i bidiren sınıfın kalıpdeğişkenleri ile aynı olmak zorundaysa, o zaman friend; “bağlı friendkalıp” işlev veya sınıfı olarak adlandırılır. Bu durumda, friendbildirimindeki kalıbın kalıp değişkenleri, friend sınıf veya işlevin kalıpdeğişkenlerini saptamada kullanılır. Burada kalıp değişkenleri ile friend inkalıp değişkenleri arasında bire bir denklik sağlanır.

En genel uygulamada, bir kalıp sınıfı, başka bir kalıp işlev veya sınıfınıfriend i olarak bildirir, burada friend in asıl kalıp değişkenleri dikkatealınmaz. Böylece “bağlı olmayan friend kalıp” sınıf veya işlevi bildirilir;

Page 166: C++ 2 (Kitap - Selami KOÇLU)

friend kalıp sınıf veya işlevin kalıp değişkenleri belirlenecekler arasındabırakılır, friend i bildiren sınıfın kalıp değişkenleri ile önceden tanımlıolarak ilişkili değildir. Örnek verecek olursak; bir sınıfta, kalıpdeğişkenlerince belirtilen değişik tipte veri üyeleri varsa, ve başka bir sınıfilgilendiği bu sınıfın üyelerine erişmek (bunun için friend olmalıdır)istiyorsa, böyle bir friend in anlık durumunu saptamak için gereken kalıpdeğişkenini belirtmeliyiz.. Çok sayıda bağlı friend belirtmekten ziyade, birkalıp (bağlı olmayan) friend bildirilir, sadece gerektiği zaman friend inasıl kalıp değişkenleri belirtilir.

Yukarıdaki durumlara, yani kalıbın friend olarak bildirildiği, bildiksınıflar (normal C++ sınıfları) kullanıldığı zaman da rastlanır. İlkkitabımızda sınıfları anlatırken, bildik sınıfların bildik sınıfları veyaişlevleri, nasıl friend olarak bildirdiğini incelemiştik. Normal bir C++sınıfı, tipi belli asıl kalıp değişkenlerini friend olarak bildirdiği zaman,bağlı friend ler, daha önceki bildik sınıflardaki gibi, aynısı olur. Bildik birsınıf, kalıp sınıf veya işlevi friend olarak bildirirse, bağlı olmayan friendlerin aynısı oluşur. Burada bir şeyi daha hatırlatalım; bildik sınıf ile,normal, kalıp olmayan C++ sınıfından bahsediyoruz.

Kalıp Olmayan İşlev ve Sınıfların friend oluşuBir kalıp sınıf bildik bir işlevi bildirebilir, bildik üye işlevler veya bütünsınıf onun friend i olabilir. Böyle bir friend, kalıp sınıfın bütün privateüyelerine erişir.Normal sınıflar veya normal işlevler friend olarak bildirilebilir elbette,ama tek başına sınıf üye işlevi friend olarak bildirilmeden önce, derleyicimutlaka o üyeyi bildireni sınıf arayüzünde görmelidir. Şimdi değişikolasılıkları inceleyelim;

Kalıp sınıfı bildik işlevi friend olarak bildirir. Bildik işlevin friendolarak bildirilme sebebi, tamamı ile açık değildir. Normal koşullarda sınıfnesnesi friend olarak bildirilen işleve aktarılır. Bu sefer de, tipleribelirtilmemiş kalıp değişkeni bulunan işlev lazım olur. C++ aşağıdakiyazım biçimini desteklemez.

void fonksiyon(std::vector<Type>& vector)

fonksiyon( ) un kendisi kalıp değilse, böyle bir friend in nasıl ve neden

Page 167: C++ 2 (Kitap - Selami KOÇLU)

inşa edileceği açık değildir. Bir sebep, herhalde, işlev sınıfın static pivate üyelerine erişsin diyedir. Bundan başka, böyle friend ler kendileri, onlarıfriend olarak bildiren sınıfların nesne anlık durumlarını tespit edebilir vebu çeşit nesnelerin private üyelerine doğrudan erişebilir. İşte önek;

template <typename Type>class Storage{ friend void basic( ) ; static unsigned s_time ; std::vector<Type> d_data ; public: Storage( ){ }};template <typename Type>unsigned Storage<Type>::s_time=0 ;void basic( ){ Storage<int>::s_time=time(0) ; Storage<double> storage ; std::random_shuffle(storage.d_data.begin( ), storage.d_data.end( )) ;}

Bildik bir sınıfı kalıp sınıfın friend i olarak bildirmek, daha çok pratikamaç taşıdığını gösterir. Burada friend sınıf, kalıp sınıfın herhanginesnesinin anlık durumlarını tespit eder ve daha sonra, bütün privateüyelerine erişmek amacını taşır. Kalıp sınıf tanımının önünde yer alan basitbir öncü friend sınıf bildirimi, bunu gerçekleştirmek için yeterlidir.

class Friend ;template <typename Type>class Besteci{ friend class Friend ; std::vector<Type> d_data ; public: Besteci( ) ;};class Friend{ Besteci<int> d_ints ; public: Friend (std::istream& input){

Page 168: C++ 2 (Kitap - Selami KOÇLU)

std::copy(std::istream_iterator<int> (input), std::istream_iterator<int> ( ), back_inserter(d_ints.d_data)); }};

Başka bir seçenek olarakta, bildik sınıfın sadece bir üye işlevi friendolarak bildirilir. Bu da derleyicinin, friend bildirilmeden önce friend sınıfarayüzünün okumasını gerektirir. Yıkıcı işlev ve bindirilmiş atama işleciatlanarak aşağıdaki örnek, randomizer( ) üye işlevi olan bir sınıf Bestecisınıfının friend i olarak bildiriliyor;

template <typename Type>class Besteci ;class Friend{ Besteci<int>* d_ints ; public: Friend(std::istream& input) ; void randomizer( ) ;};template <typename Type>class Besteci{ friend void Friend::randomizer( ) ; std::vector<Type> d_data ; public: Besteci(std::istream& input){ std::copy(std::istream_iterator<int> (input), std::istream_iterator<int> ( ), back_inserter(d_data)); }};Friend::Friend(std::istream& input): d_ints(new Besteci<int> (input)){}void Friend::randomizer( ){ std::random_shuffle(d_ints->d_data.begin( ), d_ints->d_data.end( )) ;}

Burada Friend::d_ints bir göstergeç üyedir. Derleyici Friend in

Page 169: C++ 2 (Kitap - Selami KOÇLU)

arayüzünü okurken Besteci sınıf arayüzünü göremez, bundan dolayıBesteci<int> nesnesi oluşturulamaz. Bu gözardı edilip Besteci<int> d_ints veri üyesi tanımlamak, aşağıdaki derleyici hatasınayolaçar.

error: field 'd_ints' has incomplete type

Bu noktada derleyici Besteci sınıfından haberdar olurken, Besteci ninarayüzünü görememekte, dolayısı ile d_ints veri üyesinin boyutunubilememektedir.

Belli Tiplerin Kalıp Anlık Durumları

'Bağlı kalıp' sınıfları veya işlevlerinde, 'kalıp friend' lerin kalıpdeğişkenlerinin asıl değerleri ile, onları friend olarak bildiren kalıpsınıfların kalıp değişkenleri arasında, bire bir karşılıklılık bulunur. Konuile ilgili farklı olasılıkları verelim;

Bir kalıp işlev bir kalıp sınıfın friend i olarak bildirilebilir. Bu durumdakalıp sınıfların friend i olarak bildirilen bildik işlevlerden kaynaklanansorunlar hakkında bilgimiz olmaz. Kalıp işlevin kendisi bizzat kalıpolduğu için, izin verilen gerekli kalıp değişkenleri kalıp sınıf değişkeninibelirtir. Böylece kalıp işlevini friend olarak bildiren sınıfın nesnesi kalıpişlevine aktarılır. Böylece değişik bildirimlerin düzenleri aşağıdaki gibiolur;

__ friend i bildiren kalıp sınıfın kendisi bizzat bildirilir.__ Kalıp işlevi (friend olarak bildirilir) bildirilir.__ 'Bağlı kalıp friend' işlevini bildiren kalıp sınıfı tanımlanır.__ Kalıp (friend) işlevi tanımlanır, şimdi bütün kalıp sınıf (private)üyelerine erişir.

Kalıp friend bildirimi, kalıp işlev adını hemen takip eden kalıpdeğişkenlerini belirtir. İşlev adını takip eden işlev değişkenleri bulunmazsao zaman bildik (normal C++ friend işlev) friend işlevle karşılaşırız. Şimdivereceğimiz örnek bağlı friend kullanımını gösteren sözlük alt kümegirişlerini oluşturur. Gerçek hayat örneklerinde !key1.find(key2) dönüşüdaha uygun olmasına rağmen, şimdilik operator== ( ) ile idare edeceğiz.

Page 170: C++ 2 (Kitap - Selami KOÇLU)

template <typename Key, typename Value>class Dictionary;template <typename Key, typename Value>Dictionary<Key, Value> subset<Key const& key, Dictionary<Key, Value> const& dict) ;template <typename Key, typename Value>class Dictionary{ friend Dictionary<Key, Value> subset<Key, Value> (Key const& key, Dictionary<Key, Value> const&dict); std::map<Key, Value> d_dict ; public: Dictionary( ) ;};template <typename Key, typename Value>Dictionary<Key, Value> subset(Key const& key, Dictionary<Key, Value> const& dict){ Dictionary<Key, Value> ret; std::remove_copy_if(dict.d_dict.begin( ), dict. d_dict.end( ), std::inserter(ret.d_dict, ret.d_dict.begin( ), std::bind2nd(std::equal_to<Key> ( ), key)); return ret ;}///:~

Bütün bir kalıp sınıfı bir kalıp sınıfın friend i olarak bildirilirse, friendsınıfın bütün üyeleri, kendisini friend olarak bildiren sınıfın bütün privateüyelerine erişir. Friend sınıf için gereken sadece bildirilmektir. Bununbildirim düzeni, bir kalıp işlevin friend olarak bildirilmesinden çok dahakolaydır. Aşağıdaki örnekte Iterator sınıfı, Dictionary sınıfının friend iolarak bildirilecektir, böylece Iterator Dictionary sınıfının bütün privateüyelerine erişir. Burada belirtmemiz gereken bazı ilginç noktalarbulunmaktadır;

__ Bir kalıp sınıfı friend olarak bildirmek için, önce onu kalıp sınıf olarakbildirmeli, daha sonra da aynı sınıfı friend olarak bildirmelidir. Aşağıdakigibi;

template <typename Key, typename Value>class Iterator;

Page 171: C++ 2 (Kitap - Selami KOÇLU)

template <typename Key, typename Value>class Dictionary{ friend class Iterator<Key, Value> ;

__ Bununla birlikte, friend sınıfın arayüzü, derleyici bu arayüzü görmedenönce kullanılabilir.

Iterator <Key, Value> begin( ){ return Iterator<Key, Value> (*this) ; //friend'in yapıcısı kullanılırIterator <Key, Value> subset(Key const& key){ return Iterator<Key, Value> (*this).subset(key) ; //üye işlev kullanır }

__ Elbette friend in arayüzü hala derleyici tarafından görülmekzorundadır. Dictionary nin destek sınıfı olduğundan std::map veri üyesinidüzgün biçimde tanımlar, yapıcı işlevi ilk değerini atar ve Dictionary ninprivate veri üyesi d_dict e erişebilir;

template <typename Key, typename Value>class Iterator{ std::map<Key, Value>& d_dict ; public: Iterator(Dictionary<Key, Value>& dict) : d_dict(dict.d_dict) {}

__ Iterator üyesi begin( ) bir map yineleyicisi döndürür. Map anlıkdurumunun nasıl bir şey olduğu derleyici tarafından bilinmediğinden, map<Key, Value>::iterator bir (değeri düşük) iç tip adıdır. Tip adını(typename) açık bir hale getirmek için, begin( ) dönüş başlangıcınatypename eklenir.

typename std::map<Key, Value>::iterator begin( ){ return d_dict.begin( ) ;}

__ Önceki örnekte Dictionary nin Iterator u inşa etmesine kararvermiştik, Iterator Dictionary ye çok iyi bağlanmıştı. Bu, Iterator un

Page 172: C++ 2 (Kitap - Selami KOÇLU)

yapıcı işlevini onun private bölümünde tanımlayarak, ve Dictionary yiIterator un friend i bildirerek gerçeklenir. Yerine göre sadece Dictionarykendi Iterator unu oluşturabilir. Iterator un yapıcı işlevini 'bağlı friend'bildirerek, sadece aynı kalıp değişkenlerini kullanan Iterator larıoluşturmak güvenceye alınır. İşte örnek;

template <typename Key, typename Value>class Iterator{ friend Dictionary<Key, Value>::Dictionary( ); std::map<Key, Value>& d_dict ; Iterator(Dictionary<Key, Value>& dict) : d_dict(dict.d_dict) {} public: Bu örnekte, Dictionary nin yapıcı işlevi, Iterator un friend i olaraktanımlanıyor. Burada friend bir kalıp üyedir. Öteki üyeler sınıf friend leriolarak bildirilebilir, öntipleri mutlaka kullanılmalı, tabii geri dönüş tipleriile beraber. Kabul edeceğimiz;

std::vector<Value> sortValues( )

Dictionary nin bir üyesidir. Değerleri bakımından sıraya dizili vector,daha sonra denk gelen 'bağlı friend' bildirimi;

std::vector<Value> Dictionary<Key, Value>::sortValues( )

Bağlı Olmayan Kalıp Friend'ler Bir friend 'bağlı olmayan friend' olarak bildirildiği zaman, varolan birkalıbı friend olarak bildirmiş olur. Onun anlık durum saptamasının nasılı,önemli değildir. Friend, friend i bildiren kalıp sınıf nesnelerinin anlıkdurum saptamalarında kullanılırken yararlı olur, friend e anlık durumlarısaptanmış nesnelerin, private üyelerine erişim izni verilir. Yine, işlevler,sınıflar ve üye işlevler bağlı olmayan friend ler olarak bildirilirler.Şimdi 'bağlı olmayan friend' lerin bildirim yazılışlarını ele alalım;

__ Bağlı olmayan kalıp işlevini friend olarak bildirmek için; kalıp

Page 173: C++ 2 (Kitap - Selami KOÇLU)

işlevinin anlık durumu kalıp sınıf nesnelerinin anlık durumlarını verir, veonun private üyelerine erişir. Aşağıdaki kalıp işlevinin tanımlandığınıkabul edelim;

template <typename Iterator, typename Class, void (Class::*member) (Data&)>Class& ForEach(Iterator begin, Iterator end, Class& object, void (Class::*member) (Data&)) ;

Bu kalıp işlevi, aşağıdaki kalıp sınıfı Vector2 de, bağlı olmayan friendolarak bildirilir.

template <typename Type>class Vector2: public std::vector<std::vector<Type>>{ template <typename Iterator, typename Class, typename Data> friend Class& ForEach(Iterator begin, Iterator end, Class& object, void (Class::*member) (Data&)) ;

...};

Kalıp işlevi bir isim alanında (namespace) tanımlanmışsa, isim alanımutlaka belirtilmelidir. Örnek olarak eğer ForEach( ) işlevi SK isimalanında tanımlanmışsa, o zaman onun friend bildirimi;

template <typename Iterator, typename Class, typename Data>friend Class& SK::ForEach(Iterator begin, Iterator end, Class& object,void (Class::*member) (Data&)) ;

Aşağıdaki örnekte bağlı olmayan bir friend kullanımı gösterilmektedir.Vector2 sınıfı, Type tipinde kalıp değişken öğelerinin vector'lerini saklar.Sınıfın process( ) üyesi, ForEach( ) i private üye rows( ) u çağırmaktakullanır. Tekrar dönüşte de ForEach( ) private üye columns( ) çağırır.Yerine göre Vector2, ForEach( ) işlevinin iki tane anlık durumunukullanır, ve bundan dolayı burası için bağlı olmayan friend daha uygunolur. Type sınıf nesnelerinin ostream nesnelerine yerleştirildiği kabulediliyor. Daha önce bahsi geçen ForEach( ) kalıp işlev tanımı bir sonrakibölümde verilecek. İşte program;

template <typename Type>

Page 174: C++ 2 (Kitap - Selami KOÇLU)

class Vector2: public std::vector<std::vector<Type>>{ template <typename Iterator, typename Class, typename Data> friend Class& ForEach( )(Iterator begin, Iterator end, Class& object, void (Class::*member) (Data&)) ; public: void process( ){ ForEach(begin( ), end( ), *this, &Vector2<Type>::rows); } private: void rows(std::vector<Type>& row){ ForEach(begin( ), end( ), *this, &Vector2<Type>::columns); std::cout<<std::endl ; } void columns(Type& str){ std::cout<<str<<” “ ; }};

using name space std;int main( ){ Vector2<string> c ; c.push_back(vector<string> (3, “Merhaba”)) ; c.push_back(vector<string> (2, “Dunya”)) ; c.process( ) ;}///:~

//çıktı:Merhaba Merhaba MerhabaDunya Dunya//

__ Benzer şekilde bütün bir kalıp sınıfta, friend olarak bildirilebilir. Bu,friend üye işlevlerin hepsinin anlık durumlarına, friend sınıfı bildirenkalıbın anlık durumunu saptama izni verir. Bu durumda, friend i bildirensınıf, friend sınıfının farklı anlık durumları için yararlı davranışlar (kalıpdeğişkenleri için farklı veri tipleri kullanmak gibi) teklif etmelidir. Yazımbiçimini, bağlı olmayan friend kalıp işlev bildiriminin yazımı ilekarşılaştırabilirsiniz;

template <typename Type>

Page 175: C++ 2 (Kitap - Selami KOÇLU)

class PtrVector{ template <typename Iterator, typename Class> friend class Wrapper ; //bağlı olmayan friend sınıfı};

Wrapper kalıp sınıfının bütün üyeleri, Type kalıp değişkeni için herhangibir asıl tipi kullanarak, PtrVector lerin anlık değerlerini saptar. Aynızamanda Wrapper in anlık durumları, PtrVector ün bütün privateüyelerine erişim hakkı kazanır.

__ Kalıp sınıfın sadece bazı üyeleri başka bir kalıp sınıfın privateüyelerine erişim ihtiyacı duyduğu zaman, (yani private yapıcı işlevleriolan öteki kalıp sınıf, birinci kalıp sınıfın sadece bazı üyeleri ikinci kalıpsınıfın nesnelerinin anlık durumlarını saptıyor) o zaman, sonra gelen kalıpsınıf ana sınıfın sadece o üyelerini, kendi private üyelerine erişebilmesiiçin friend olarak bildirir. Yine, friend sınıfın arayüzü belirtilmedenbırakılır. Bunun yanında, derleyici friend üyenin sınıfı hakkında mutlakabilgilendirilmelidir. Yani o bir sınıftan geliyor diye. Bundan dolayı ilgilisınıfın öncü bildirimi uygun yerde mutlaka verilmelidir. Aşağıdaki örnektePtrVector, Wrapper::begin( ) i friend olarak bildiriyor. Wrappersınıfının öncü bildirimine dikkat edin;

template <typename Iterator>class Wrapper;

template <typename Iterator>class PtrVector{ template <typename Iterator> friend PtrVector<Type> Wrapper<Iterator>::begin(Iteratorconst& t1);...};

Kalıp Sınıf Türetimi Kalıp sınıfları sınıf türetimlerinde de kullanılabilir. Bir kalıp sınıfı sınıftüretiminde kullanıldığı zaman aşağıdaki farklı durumlarla karşılaşılır;

__ Varolan bir kalıp sınıfını ana sınıf olarak kullanıp, bildik (normal C++

Page 176: C++ 2 (Kitap - Selami KOÇLU)

sınıfı) bir sınıfı türetmek. Bu durumda türetilen bildik sınıf, hala kısmenkalıp sınıftır. Ama bu özellik, türetilmiş sınıftan nesne elde edilirkengözden kaçar.__ Varolan bir kalıp sınıfı ana sınıf olarak kullanıp, başka bir kalıp sınıftüretmek. Burada kalıp sınıf özellikleri açık olarak görünür.__ Bildik (normal C++ sınıfı) bir sınıfı ana sınıf olarak kullanıp, bir kalıpsınıf türetmek. Bu ilginç karışım, kalıp sınıfların kısmen önderlemeliolarak inşasını sağlar.

Bu üç değişik uygulamanın, şimdi biraz daha ayrıntısını verelim; aşağıdaki ana sınıftır,

template <typename T>class Ana{ T const& t; public: Ana(T const& t) : t(t) {}};

Yukarıdaki sınıf bir kalıp sınıftır, ve aşağıdaki türetilmiş Derived kalıpsınıfının ana sınıfıdır.

template <typename T>class Derived: public Ana<T>{ public: Derived(T const& t) : Ana(t) {}};

Diğer olasılıklarda mümkün. Ana sınıfın bildik kalıp tip değişkenleribelirtilerek, ana sınıfın anlık durumu saptanır ve türetilen sınıf bildik(normal C++ sınıf, yani kalıp sınıf değil) bir sınıf olur.

class Ordinary: public Ana<int>{ public:

Page 177: C++ 2 (Kitap - Selami KOÇLU)

Ordinary(int x) : Ana(x) {}};

//aşağıdaki nesne tanımı ile Ordinaryo(5) ;

Bu inşaat kalıp sınıfına özel durumlarda yeni davranışlar eklenmesine izinverir. Bu sırada türetilmiş bir kalıp sınıfına da gerek duyulmaz.

Kalıplıdan, Kalıp Olmayan Sınıf Türetimi Bir kalıp sınıfını ana sınıf olarak kullanıp kalıp olmayan bir sınıf türetildiğizaman, kalıp sınıf değişkenleri türetilmiş sınıf arayüzü tanımlanırkenbelirtilir. Bazı durumlarda varolan kalıp sınıfın işlevlerinde eksiklikgörülürse, kalıp sınıftan bildik sınıf türetimi yararlı olur. Örnek olarak;belli bir öğenin yerini belirlemede map sınıfı ile find_if( ) kalıpalgoritması birlikte kullanılmasına rağmen, ayrıca bir sınıf inşası ve osınıfın en az iki ek işlev nesnesi gerekir. Bazı açılardan bu masraflıgözükse de, bir kalıp sınıfı siparişe uygun işlevlerle genişletmek dahaakıllıcadır.Klavyeden girilen komutları çalıştıran bir program, tanımlanmışkomutların kısaltılmış yazımlarını da kabul edebilir. Örnek olarak list gibibir komutu l, li, lis veya list yazarak çalıştırabiliriz. Aşağıdan Handlersınıfını türetiriz;

map<string, void (Handler::*) (string const& cmd)>

ve asıl komut işlemleri için process(string const& cmd) tanımlanır.Program aşağıdaki main( ) i basit bir biçimde çalıştırır;

int main( ){ string line ; Handler cmd ; while(getline(cin, line) cmd.process(line) ;

Page 178: C++ 2 (Kitap - Selami KOÇLU)

}///:~

Handler sınıfının kendisi karmaşık map sınıfından türetilir. Map değerleriHandler sınıf üye işlevlerinin göstergeçleridir.Ve bunlar kullanıcınınklavyeden girdiği komut satırını beklerler. Şimdi Handler in özellikleriniverelim;

__ std::map ten türetilen sınıf, klavye tuşu olarak geçen komut işlemlemeüyesinin komutunu bekler. Handler map i komutlarla, çalışan üye işlevarasındaki ilişkiyi tanımlamak için kullandığından, burada private türetimuygularız;

class Handler : public std::map<std::string, void (Handler::*) (std::string const& cmd)>

__ Asıl ilişki, static private veri üyeler kullanarak tanımlanabilir; s_cmds,Handler::value_type değerlerinin bir dizisidir, s_cmds_end ise dizininson öğesinden öteyi işaret eden bir const göstergeçtir.

static value_type s_cmds[] ;static value_type* const s_cmds_end ;

__ Yapıcı işlev bu iki static veri üyesinden map i başlatır. Satıriçi (inline)olarak uygulanabilirler;

Handler( ): std::map<std::string, void (Handler::*) (std::string const& cmd)> (s_cmds, s_cmds_end){}

__ process( ) üyesi map öğeleri boyunca yinelemeler yapar. İlk kez,komut satırındaki birinci kelime komutun başlangıç karakterlerine uyar, vedenk gelen komut çalıştırılır. Eğer böyle bir komut yoksa, hata iletisiüretilir.

void Handler::process(std::string const& line){ istringstream(istrline); string cmd ;

Page 179: C++ 2 (Kitap - Selami KOÇLU)

istr>>cmd ; for(iterator it=begin( ); it!=end( ); it++){ if(it->first.find(cmd)==0){ (this->*it->second)(line) ; return; } } cout<<”bilinmeyen komut: “<<line<<endl ;}///:~

Kalıp Sınıftan Kalıp Sınıf Türetmek Bir kalıp sınıftan bildik sınıf türetmek oldukça mükemmelgerçeklenmesine rağmen, türetilen sınıf türetildiği ana kalıp sınıftan dahasınırlı genel özelliklere sahiptir. Eğer genellik önemliyse, o zaman bir kalıpsınıftan başka bir kalıp sınıf türetmek, herhalde daha iyi bir fikirdir.Böylece, varolan bir kalıp sınıf ek işlevlerle genişletilip, öğeler hiyerarşiksıralamaya sokulur. Aşağıdaki SortVector kalıp sınıfı varolan Vector kalıp sınıfındantüretilmiştir. Bunun yanında, veri öğelerini içeren üyelerin herhangi birsıralaması kullanılarak, öğelerin hiyerarşik düzeni sağlanır. Bunuyapabilmek için ama bir gerek şart bulunmaktadır; SortVector ün veri tipi,diğer üyelerine göre başka belli bir görevi yerine getiren, üye işlevleresahip olmak zorundadır. Örnek; SortVector ün veri tipi MultiDatasınıfının bir nesnesidir, daha sonra MultiData veri üyelerinin her biri içinaşağıdaki öntiplere sahip üye işlevi uygular;

bool (MultiData::*) (MultiData const& rhv)

Bu yüzden eğer MultiData iki veri üyeye sahip olursa, (int d_value vestd::string d_text) her ikisi de hiyerarşik sıralamaya göre düzenlenir, dahasonra MultiData üyeleri aşağıdaki gibi olur;

bool intCmp(MultiData const& rhv); //d_value<rhv.d_value değeridönerbool textCmp(MultiData const& rhv); // d_text<rhv.d_text değeridöner

Bunlardan başka, gerektiği için operator<<( ) ve operator>>( ) işleçleri

Page 180: C++ 2 (Kitap - Selami KOÇLU)

MultiData nesneleri için tanımlandığı kabul edilir. Ama bu kabul, şu ankiincelememizden bağımsızdır. SortVector kalıp sınıfı doğrudan std::vector kalıp sınıfından türetilir.Uygulamamız iki basit yapıcı işlev kadar, ana sınıftan gelen bütün üyelerimiras alır;

template <typename Type>class SortVector: public std::vector<Type>{ public: SortVector( ) {} SortVector(Type const* begin, Type const* end) : std::vector<Type>(begin, end) {}

Bunun yanında, hierarchicalSort( ) üye işlevi sınıfın asıl varlık nedenidir.Bu sınıf hiyerarşik sıralanma koşullarını tanımlar. Sınıfın üye işlevleriniişaret eden göstergeçler dizisi (array), sortVector ün kalıp Type değişkenitarafından belirtilir. Ayrıca dizi büyüklüğü de unsigned tarafındanbelirtilir. Dizinin ilk öğesi sınıfın en büyük değerini (most significant)veya ilk sıralama koşulunu belirtir. Dizinin son öğesi ise, sınıfın en küçükdeğerini (least significant) veya son sıraya koyma koşulunu belirtir. stable_sort( ) kalıp algoritması hiyerarşik sıralamayı desteklemek içintasarlanmış olduğundan, üye bu algoritmayı kullanarak SortVectoröğelerini sıraya koyar. Hiyerarşik sıralamada en küçük değer koşulu öncehizaya girer. HierarchicalSort( ) uygulaması bundan dolayı kolaydır.HierarchicalSort( ) a aktarılan üye işlev adreslerince ilk değerleribaşlatılan nesnelerin SortWith destek sınıf varlığı bir kabuldur;

template <typename Type>class SortWith{ bool (Type::*d_ptr)(Type const& rhv) const ;

SortWith sınıfı hüküm işlevine göstergeçin etrafında bulunan basit birörtü (wrapper) sınıfıdır. SortVector ün asıl veri tipine bağımlı olduğu içinSortWith in kendisi de bir kalıp sınıftır;

template <typename Type>class SortWith{

Page 181: C++ 2 (Kitap - Selami KOÇLU)

bool (Type::*d_ptr)(Type const& rhv) const ;

Böyle bir göstergeci yapıcı işlev taşır ve sınıfın d_ptr üyesinin ilkdeğerini atar;

SortWith(bool (Type::*ptr)(Type const& rhv) const): d_ptr(ptr){}

Bunun ikici (binary) yüklem üyesi operator( ) ( ), ilk veri ikinci veridenönce sıraya konursa, true (doğru=1) döndürür;

bool operator( ) (Type const& lhv, Type const& rhv) const{ return (lhv.*d_ptr)(rhv) ;}

Son olarak aşağıdaki main( ) işlevince uygulama sağlanır;

** Önce, MultiData nesneleri için bir SortVector nesnesi oluşturulur,programın standart akışından bilgileri SortVector nesnesine yerleştirmekiçin copy( ) kalıp algoritması kullanılır. Nesnenin ilk değerleri atanmışolduğundan, öğeleri standart çıktı akışında görülür.

SortVector<MultiData> sv ;copy(istream_iterator<MultiData>(cin), istream_iterator<MultiData>( ), back_inserter(sv));** Üyelerin göstergeç dizileri iki üye işlev adresi ile başlatılır (ilk değerataması yapılır). Metin karşılaştırması en büyük sıraya koyma koşuluolarak düşünülür;

bool (MultiData::*arr[])(MultiData const& rhv) const={ &MultiData::textCmp, &MultiData::intCmp,};

** Bir sonra dizi öğeleri sıraya konur, ve standart çıktı akışında gösterilir.

Page 182: C++ 2 (Kitap - Selami KOÇLU)

sv.hierarchicalSort(arr, 2) ;

** Daha sonra MultiData üye işlevleri göstergeç dizisinin iki üyesi yerdeğiştirir, ve önceki adım tekrarlanır.

swap(arr[0], arr[1]) ; sv.hierarchicalSort(arr, 2) ;

Programın derlenmesinden sonra aşağıdaki komut verilebilir;

echo a 1 b 2 a 2 b 1 | a.out

Sonuçlar aşağıda;

a 1 b 2 a 2 b 1a 1 a 2 b 1 b 2 a 1 b 1 a 2 b 2

Kalıplı Olmayandan Kalıp Sınıfı Türetmek Varolan bildik sınıfı kullanarak bir kalıp sınıf türetilebilir. Böyle birkalıtım ağacının yararı; bütün ana sınıf üyelerinin hepsinden öncederlenmesidir. Böylece kalıp sınıf nesnelerinin anlık durumlarısaptanırken, sadece türetilen sınıfın kullanılan üyelerinin anlık durumlarıtespit edilir. Bu yaklaşım, uygulamaları kalıp değişkenlerine bağımlı olmayan üyeişlevlere sahip, bütün kalıp sınıfları için kullanılabilir. İlgili üyeler, kalıpsınıfın türetileceği ana sınıf olarak kullanılacak, ayrı bir sınıfta tanımlanır. Bu yaklaşımı tanıtmak amacı ile, bu bölümde böyle bir kalıp sınıftüreteceğiz. TableType sınıfından türetilmiş Table kalıp sınıfınıgeliştireceğiz. Burada TableType kalıp sınıfı olmayıp bildik C++ sınıfıdır.Table sınıfı, ayarlanabilir sayıda kolonu olan bazı tip öğelerin bulunduğutabloyu gösterir. Öğeler ya yatay (ilk k öğesi ilk satırı doldurur) ya dadikey (ilk r öğesi ilk kolonu doldurur) olarak düzenlenip gösterilir. Tablo öğeleri gösterilirken, akışa yerleştirilir. Bu bize ayrı bir sınıfta(TableType), tablo kullanım tanımına izin verir. Böylece tablo tanıtımıyapılır. Tablo öğeleri akışa yerleştirildiği için, metine çevrim (string)Table içinde gerçeklenebilir, ama string lerin yönetimi TableType abırakılır. TableType özelliklerini, öncelikle Table arayüzünde

Page 183: C++ 2 (Kitap - Selami KOÇLU)

yoğunlaşarak, kısaca anlatacağız;

** Table sınıfı kalıp sınıftır, sadece bir tane kalıp tip değişkeni gerekir;Iterator bazı veri tiplerinin yineleyicisidir.

template <typename Iterator>class Table: public TableType{...}

** Veri üyeleri gerektirmez; bütün veri işlemleri TableType tarafındangerçeklenir.

** İki tane yapıcı işlevi bulunur; Yapıcının ilk iki değişkeni olan Iteratorlar, tabloya girecek öğeler arasında yinelemeler için kullanılır. Bundan daöteye, tablomuzdaki FillDirection kadar kolon sayısını da belirtmemiziister. FillDirection aslında Horizontal ve Vertical değerlerine sahipTableType tarafından tanımlanır. Ayrıca TableType kullanıcılarıbaşlıkları, alt kısımları, üstlükleri, yatay ve dikey ayırıcıları denetlemeleriiçin, yapıcılardan birinde TableSupport dayanç değişkenleri bulunur.TableSupport kullanıcıların bu denetimi sağlamaları için sanal bir sınıfolarak daha sonra geliştirilecektir. Yapıcılar işte aşağıda;

Table(Iterator const& begin, Iterator const& end, unsigned nColumns, FillDirection direction) ;Table(Iterator const& begin, Iterator const& end, TableSupport& tableSupport, unsigned nColumns, FillDirection direction);

** Yapıcılar Table ın sadece iki public üyesidir. Her iki yapıcı, ana sınıfbaşlatıcıları kullanarak onların TableType ana sınıfını başlatacak ve dahasonra da, sınıfın private üyesi fill( ) çağrılarak TableType ana sınıfnesnesine veriler yerleştirilecektir. İşte yapıcıların uygulaması;

template <typename Iterator>Table<Iterator>::Table(Iterator const& begin, Iterator const& end, TableSupport& tableSupport, unsigned nColumns, FillDirection direction): TableType(tableSupport, nColumns, direction){

Page 184: C++ 2 (Kitap - Selami KOÇLU)

fill(begin, end) ; }template <typename Iterator>Table<Iterator>::Table(Iterator const& begin, Iterator const& end, unsigned nColumns, FillDirection direction): TableType(nColumns, direction){ fill(begin, end) ; }

** Sınıfın fill( ) üyesi [begin, end) alanındaki öğeleri yineleyip durur.Aynen yapıcı işlevin ilk iki değişkenince tanımlandığı gibi. Kısa birşekilde göreceğimiz üzere, TableType, protected veri üyestd::vector<std::string> d_string i tanımlar. Veri tipi için gerekenlerdenbiri, yineleyiciler noktasından bu veri tipinin akışlarayerleştirilebileceğidir. Böylece, fill( ), verinin metin şeklini sağlamak içinostringstream nesnesini kullanır. İşte o zaman d_stringe eklenir;

template <typename Iterator>void Table<Iterator>::fill(Iterator it, Iterator const& end){ while(it != end){ std::ostringstream str ; str<<*it++ ; d_string.push_back(str.str( )) ; } init( ) ;}

Bu Table sınıf uygulamasını tamamlar. Burada dikkat edilecek nokta;Table sınıfının üç (3) üyesi olduğudur, bunlardan ikisi yapıcı işlevdir.Bundan dolayı birçok durumda, sadece iki kalıp işlevinin anlık durumusaptanır; bir yapıcı işlev ve fill( ) sınıf üyesi. Verilen aşağıdaki örnektedört kolona sahip tablo standart girdi akışından elde edilen string lerledikey olarak dolduruluyor;

Table<istream_iterator<string> > table(istream_iterator<string>(cin), istream_iterator<string>( ), 4, TableType::Vertical);

Burada öğelerin yerleştirme doğrultusu TableType::Vertical ile

Page 185: C++ 2 (Kitap - Selami KOÇLU)

belirlenir.Tabii Table sınıfını da kullanarak bunu belirtmek mümkündür.Ama Table sınıfı kalıp sınıf olduğundan, yazım biraz daha karışıktır;

Table<istream_iterator<string> >::Vertical

Şimdi Table türetilmiş sınıfı tasarlandı, artık dikkatimizi TableTypesınıfına çevirebiliriz. İşte temel özellikler;

** Bu bir bildik sınıftır, Table in ana sınıfı olarak işlem yapması içintasarlandı.** Bu değişik private veri üyeleri kullanır. Bunların arasında; herkolondaki en büyük öğenin büyüklüğünü saklayan vektör d_colWidth, vetable [row][col] daki öğeyi döndüren sınıf üye işlevini işaret eden;d_indexFun, tablonun hangi koşullarda nasıl sıralama ile doldurulacağınıverir. TableType ayrıca bir TableSupport göstergeç ve dayancı dakullanır. TableSupport nesnesi gerekmeyen yapıcı, TableSupport(varsayılan) nesnesini belleğe yerleştirebilmek için TableSupport*kullanır, ve daha sonra da TableSupport& u nesnenin zarfı olarakkullanır. Diğer yapıcı, göstergeci sıfır (0) değerinden başlatır, vedeğişkenince belirlenen TableSupport nesnesine karşılık gelen dayançveri üyeyi kullanır. İkinci bir seçenekte; yapıcıdaki dayanç veri üyeyibaşlatmak için statik Table Support nesnesi kullanılmasıdır. Öteki veriüyeleri ise kendileri hakkında yeterli bilgiyi veriyor işte;

TableSupport* d_tableSupportPtr;TableSupport& d_tableSupport ; unsigned d_maxWidth ;unsigned d_nRows ;unsigned d_nColumns ;WidthType d_widthType ;std::vector<unsigned> d_colWidth ;unsigned (TableType::*d_widthFun) (unsigned col) const ;std::string const &(TableType::*d_indexFun) (unsigned row, unsigned col) const ;

** Tabloya sürekli yerleştirilen asıl string nesneler, protected veri üyedesaklanır.

Page 186: C++ 2 (Kitap - Selami KOÇLU)

std::vector<std::string> d_string ;

** Yapıcılar (protected) temel görevleri yerine getirir; nesne veri üyesininilk değer atamasını yaparlar.İşte TableSupport nesnesine dayanç bekleyen yapıcı işlev;

#include “tabletype.ih”TableType::TableType(TableSupport& tableSupport, unsignednColumns, FillDirection direction): d_tableSupportPtr(0), d_tableSupport(tableSupport), d_maxWidth(0), d_nRows(0), d_nColumns(nColumns), d_widthType(ColumnWidth), d_colWidth(nColumns), d_widthFun(&TableType::columnWidth), d_indexFun(direction==Horizontal ? &TableType::hIndex : &TableType::vIndex){ }

** d_data bir kez doldurulduğunda, tablo Table::fill( ) ile başlatılır. init( )protected üyesi, d_data yı öyle ölçeklendirirki boyut tamı tamınasatırxsütun kadar olur. Ek olarak, sütundaki öğelerin olabilecek en büyükgenişliğini saptar. Uygulama gayet basittir;

#include “tabletype.ih”void TableType::init( ){ if(!d_string.size( )) return ; d_nRows=(d_string.size( )+ d_nColumns-1)/d_nColumns ; d_string.resize(d_nRows*d_nColumns) ; for(unsigned col=0; col<d_nColumns; col++){ unsigned width=0 ; for(unsigned row=0; row<d_nRows; row++){ unsigned len=stringAt(row, col).length( ) ; if(width<len)

Page 187: C++ 2 (Kitap - Selami KOÇLU)

width=len ; } d_colWidth[col]=width ; if(d_maxWidth<width) d_maxWidth=width ; }}///:~

** Public üye olan olan insert( ) Tabloyu akışa yerleştirmek içinyerleştirme işlecince (operator<<( )) kullanılır. Önce TableSupportnesnesi tablo boyutları hakkında bilgilendirilir. Daha sonra tablodaTableSupport nesnesine, başlıkları, alttakileri ve ayırıcıları yazması içinizin verilir.

#include “tabletype.ih”ostream &TableType::insert(ostream& ostr) const{ if(!d_nRows) return ostr ; d_tableSupport.setParam(ostrd_nRows, d_colWidth, d_widthType==EqualWidth ? d_maxWidth : 0) ; for(unsigned row=0; row<d_nRows; row++){ d_tableSupport.hline(row) ; for(unsigned col=0; col<d_nColumns; col++){ unsigned colwidth=width(col) ; d_tableSupport.vline(col) ; ostr<<setw(colwidth)<<stringAt(row, col) ; } d_tableSupport.vline( ) ; } d_tableSupport.hline( ) ; return ostr ;}///:~

** TableSupport uygulamasının geri kalan çalışmalarını sizebırakıyorum. Geri kalan üyelerin çoğu private dir. Bunların arasındaaşağıdaki iki üye [row][column] tablo öğesini döndürür. Yani yatay vedikey doldurulan tablo öğeleri.

std::string const& hIndex(unsigned row, unsigned col) const{ return d_string[row*d_nColumns+col] ;

Page 188: C++ 2 (Kitap - Selami KOÇLU)

}std::string const& vIndex(unsigned row, unsigned col) const{ return d_string[col*d_nRows+row] ;}

Destek sınıfı TableSupport başlıklar, alt kısımdakiler, ayırıcılar vediğerleri için kullanılır. Bu görevlerin yerine getirilebilmesi için 4 sanalüye bulunur (ve sanal yapıcı tabii ki).

-- hline(unsigned rowIndex); bu, öğeler rowIndex satırında görünmedenhemen önce çağrılır.-- hline( ); son satır gösterildikten hemen sonra çağrılır.-- vline(unsigned colIndex); bu, öğe colIndex kolonunda gösterilmedenhemen önce çağrılır. -- vline( ); bu, satırda bütün öğeler gösterildikten hemen sonra çağrılır.

Kalıp Sınıflar Ve YuvalanmaBir sınıf bir kalıp sınıfta yuvalandığı zaman, kendisi de otomatik olarakkalıp sınıf olur. Yuvalanmış sınıf kendisini kuşatan kalıp sınıfındeğişkenlerini kullanabilir. Bu aşağıda yer alan iskelet programda görülür.PtrVector sınıfının içinde bir iterator sınıfı tanımlanıyor. Yuvalanmışsınıf, bilgilerini kendisini kuşatan PtrVector<Type> sınıfından elde eder.Bu kuşatan sınıf yineleyicileri inşa eden tek sınıf olduğu için, iterator unyapıcı işlevi private yapılır. Kuşatan sınıfa reverse_iterator un privateüyelerine erişmesi için kısıtlı friend bildirimi yapılır. İşte PtrVector sınıfarayüzünün ilk kısmı;

template <typename Type>class PtrVector: public std::vector<Type* >

std::vector ana sınıfı Type değerleri değil, değerlerinin göstergeçlerinisaklar. Artık tabii bir yıkıcı işleve gerek var. Zira belleğin (dışarıdangetirilip yerleştirilmiş) Type nesnelerinden temizlenmesi gerek. Başka birseçenek olarak; yeni öğelerin saklanması PtrVector ün görevleri arasınayerleştirilir. PtrVector ün müşterisi gereken bellek yerleşimini yaptığıkabul edilir, daha sonra da yıkıcı işlev uygulaması gerçeklenir.Yuvalanmış sınıf, yapıcısını private üye olarak tanımlar. İzin verildiği içinPtrVector<Type> nesneleri onun private üyelerine erişebilir. Bundan

Page 189: C++ 2 (Kitap - Selami KOÇLU)

dolayı sadece kuşatan sınıf PtrVector<Type> tipinin nesnelerine, onlarıniterator nesnelerini inşa etmelerine izin verilir. Bunun yanında,PtrVector<Type> müşterileri kullandıkları PtrVector<Type>::iteratornesnelerinin kopyalarını inşa edebilirler. Burada yuvalanmış sınıf iterator,gereken friend bildirimini de ihtiva eder. typename anahtar kelimesi içinbir bilgi verelim; std::vector<Type* >::iterator bir kalıp değişkeninebağlı olduğu için, evet bir anlık sınıf durumu değildir, bu yüzden iteratorbir iç typename dir. typename atlanacak olursa, derleyici unsurlarıbundan dolayı uyarılarda bulunur. Bu gibi durumlarda yapılması gerekentypename yazmaktır. Bunlardan başka, ana sınıfın begin( ) üyesiçağrılarak da d_begin başlatılır;

class iterator{ friend class Ptrvector<Type> ; typename std::vector<Type* >::iterator d_begin ; iterator(PtrVector<Type>& vector) : d_begin(vector.std::vector<Type* >::begin( )) {} public: Type& operator*( ){ return **d_begin ; }};

Sınıfın geri kalan bölümü gayet kolay. Uygulanan diğer bütün işlevleratlanır, begin( ) yeni inşa edilmiş PtrVector<Type>::iterator nesnesidöndürür. iterator sınıfı kendisine friend olan kuşatan sınıfı çağırmışolduğundan, yapıcı işlev çağrılır;

iterator begin( ){ return iterator(*this) ;}

Şimdi de main( ) i verelim, burada yuvalanmış sınıf iterator kullanılıyor.

int main( ){ PtrVector<int> vi ; vi.push_back(new int(8888))) ;

Page 190: C++ 2 (Kitap - Selami KOÇLU)

PtrVector<int>::iterator begin=vi.begin( ) ; std::cout<<*begin<<endl ;}

Kalıp sınıflarda yuvalanmış enum lar ve typedef ler da tanımlanabilir.Table sınıfı daha önceki bölümlerden birinde belirtildiği üzereTableType::FillDirection sayılaştırılmasından (enumeration) ortaya çıkar.Eğer Table tam bir kalıp sınıfı olarak uygulansaydı, o zaman sayılaştırmaaşağıdaki gibi Table ın kendisinde tanımlanırdı;

template <typename Iterator>class Table::public TableType{ public: enum FillDirection{ Horzontal ; Vetical ; }; .....};

Bu durumda kalıp tip değişkeninin değeri mutlaka FillDirection değerine veya tipine ait olduğu zaman belirtilmekzorundadır. Örnek (iter ve nCols değerlerinin tanımlandığı kabul edilerek);

Table<istream_iterator<string> >::FillDirection direction = argc == 2 ? Table<istream_iterator<string> >::Vertical : Table<istream_iterator<string> >::Horizontal ;Table<istream_iterator<string> > table(iter, istream_iterator<string>( ), nCols, direction) ;

Yineleyicilerin İnşası

Bu kısmı Standart Kalıp Sınıflardan (SKK) sonra okursanız daha iyipişirirsiniz.. Yine de seçim sizin. Yineleyiciler (iterators) göstergeç gibi davranan nesnelerdir. Çok sayıdadeğişik tipte yineleyici bulunur; InputIterators, OutputIterators,ForwardIterators, BidirectionalIterators ve RandomAccessIterators.

Page 191: C++ 2 (Kitap - Selami KOÇLU)

Bunların bütün ayrıntıları daha sonraki bölümde, standart kalıpkütüphanelerinde (SKK) ele alınacak. Ama bütün yineleyiciler artmaişlemini, dayanç kurtarma işlemini ve eşitlik (veya eşitsizlik) karşılaştırmaişlemlerini desteklerler belirtelim.Bunun yanında, yineleyiciler kalıp algoritmaları ile bağlantılıkullanıldıklarında bazı özel gerekleri de karşılamak zorundadırlar. Bununnedeni; kalıp algoritmalarının yineleyicilerin aldıkları tipleridenetlemelerinden kaynaklanır. Basit göstergeçler genel olarak kabuledilir, ama eğer yineleyici nesne kullanılırsa, yineleyicinin gösterdiği tipinçeşidi belirtilebilir olmalıdır.Bir sınıf nesnesini yineleyicinin belli bir tipi olarak yorumlamak için, sınıfmutlaka class iterator dan türetilmelidir. Kalıp sınıfın ilk değişkeniyineleyicinin özel tipini tanımlar, ve yineleyicinin işaret ettiği özel veri tipide kalıp sınıfın ikinci değişkenince tanımlanır. Bir sınıf iterator sınıfındantüretilmeden önce aşağıdaki başlık dosyası mutlaka eklenmelidir;

#include <iterator>

Türetilen sınıf tarafından uygulanan yineleyicinin özel tipi iterator_tagkullanılarak belirtilir. Bu da iterator sınıfının ilk kalıp değişkenincesağlanır. Bu eklentiler (tag ler) ;

__ std::input_iterator_tag: InputIterator u tanımlar. Böyle yineleyicilerokuma işlemlerini yapar. Bunu ilk öğeden son öğeye kadar yineleyiciyineleyerek gerçekler.__ std::output_iterator_tag: OutputIterator u tanımlar. Atamaişlemlerini yapar. Aynı şekilde ilk öğeden son öğeye kadar yinelemeylegerçeklenir.__ std::forward_iterator_tag: ForwardIterator u tanımlar. Atama veokuma işlemlerine izin verir. Yukarıdakiler gibi ilk öğeden son öğeyekadar yinelenerek gerçeklenir.__ std::bidirectional_iterator_tag: BidirectionalIterator u tanımlar.Okuma ve atama işlemlerine izin verir. Adım adım her iki yöndedeolabilen yineleme ile bütün öğeler yineleyici tarafından işlemlenir.__ std::random_access_iterator_tag: RandomAccessIterator utanımlar. Okuma ve atama işlemlerini gerçekler. Gelişigüzel adımlarla heriki yöndede olabilen yineleme ile bütün öğeler yineleyici tarafındanişlemlenir. Buradaki gelişigüzel adım mutlaka uygun olmalıdır.Her yineleyici eklentisi (tag) belli bazı işleçlerin olduğunu baştan kabuleder. RandomAccessIterator en karmaşık yineleyici olup aslında bütün

Page 192: C++ 2 (Kitap - Selami KOÇLU)

diğer yineleyicileri ima eder.Yineleyiciler her zaman belli bir bölgede tanımlanır. Örnek; [begin, end).Sonuç yineleyici değeri bu değerlerin dışına çıkarsa, artırma ve eksiltmeişlemleri tanımsız olur. Çoğunlukla yineleyiciler sadece ait oldukları dizilerin öğelerine erişirler.İçerde ise bir yineleyici bildik göstergeç kullanabilir. Ama bir yineleyiciiçin kendi belleğine yerleşim hemen hemen hiç gerekmez. Bundan dolayıkopya yapıcı işlev ve bindirilmiş atama işleci belleğe yerleştirmezorunluluğu duymazlar. Bindirilmiş atama işleci ile kopya yapıcı işlevinvarsayılan uygulaması yeterli olur. Yani genellikle bu üyeler hiçkullanılmazlar. Ayrıca bunun bir sonucu olarak, yıkıcı işlev de ayrıcagerekmez. Yineleyicileri döndüren üyeleri öneren sınıflarda çoğunluğunyaptığı; gereken yineleyicileri inşa eden üyelere sahip olmaktır, ki hemenbu üye işlevler tarafından nesne olarak döndürülür. Bu üye işlevlerinçağırıcısı olarak, ya sadece dönen yineleyici nesneler kullanılmakzorundadır ya da, arasıra dönen yineleyici nesneler kopyalanmakzorundadır. Olağan koşullarda public yapıcı işlevlere gerek olmamasınarağmen, kopya yapıcı işlevler bundan istisnadır. Bu yüzden bu yapıcıişlevler ya private veya protected üyeler olarak tanımlanırlar. Kuşatansınıfa yineleyici nesneler oluşturma izni vermek için, yineleyici sınıfdışarıdaki kuşatan sınıfı friend olarak bildirir.Aşağıdaki bölümlerde en karışık yineleyiciler olan RandomAccessIteratorve ters RandomAccessIterator un inşa süreçleri incelenecek. Kendisi içingeliştirilen rastgele erişimli yineleyici zorunluluğundan, kap sınıfı veriöğelerini değişik şekillerde saklar. Örneğin; farklı kaplar kullanmak,göstergeçlere göstergeçler kullanmak gibi.. Bundan dolayı çok genişkapsamlı bildik (kap) sınıflar için genel bir kalıp yineleyici sınıf inşaetmek çok zordur.Aşağıdaki bölümlerde eldeki std::iterator sınıfı, rastgele erişimyineleyicisini temsil eden iç kısımdaki sınıfı inşa eder. Olaya bu şekildeyaklaşmak yineleyici sınıfın inşa edilme sürecini açıkça gösterir. Okur,ötekilerle bağlantılı yineleyicileri inşa ettiği zaman ya bu yaklaşımı izler,ya da bütün bir kalıp yineleyici sınıfı inşa edebilir. Buna benzer bir örneğide ileride vereceğiz.Aşağıda inşa edeceğimiz yineleyicinin amacı; sadece erişebilengöstergeçler aracılığı ile, öğeler dizisinin belli öğelerine varanyineleyicileri gerçeklemektir. Yineleyici sınıf, string göstergeçlerinvektöründen türetilen bir sınıfın iç sınıfı olarak inşa edilir.

Page 193: C++ 2 (Kitap - Selami KOÇLU)

'RandomAccessIterator' Uygulaması

Bir sonraki bölümde kap sınıflardan bahsedilirken göreceğiniz gibi, kapsınıflar içerdikleri bilgilerin sahibidirler. Kap sınıflarda nesneler bulunursao zaman bu nesneler kap sınıflar yok edildiğinde yok olurlar. Göstergeçlernesne değillerse, auto_ptr nesneleri kaplarda saklanamıyorsa, kaplar içingöstergeç veri tipleri kullanmak yanlış olurdu. Herşeye rağmen bellidurumlar için göstergeç veri tiplerini kullanabiliriz. Aşağaıdaki StringPtrbildik sınıfı std::vector kap sınıfından türetilmiş olup, veri tipi olarakstd::string* kullanılmıştır.

#ifndef _ INCLUDED_STRINGPTR_H#define _ INCLUDED_STRINGPTR_H

#include <string>#inclcude <vector>

class StringPtr: public std::vector<std::string* >{ public: StringPtr(StringPtr const& other) ; ~StringPtr( ) ; StringPtr& operator=(StringPtr const& other) ;};#endif ///:~STRINGPTR~://

Yıkıcı işleve dikkat edelim; nesneler string göstergeçleri saklarken,StringPtr nesnesi kendini imha ettiği zaman (yani sildiğinde) string lerisilmek için bir yıkıcı gerekir. Aynı şekilde bir kopya yapıcı işlev ve bir deatama işleci gerekir. Öbür üyeler (özellikle yapıcılar) bu bölüme lazımolmadıklarından açıkça belirtilmeleri gerekmemiştir.Şimdi StringPtr nesneleri ile sort( ) kalıp algoritmasının kullanıldığınıkabul edelim. Bu algoritma iki tane RandomAccessIterator gerektirir.Anılan yineleyiciler bulunmasına rağmen (std::vector ün begin( ) ve end( ) üyeleri aracılığı ile), bunlar std::string* e yineleyicilerdöndürürler, ki bu bize uygun değildir.Anılan sorunu çözmek için StringPtr::iterator iç tipini tanımladığımızıkabul edelim, bu göstergeçlerin yineleyicilerini döndürmeyip, anılangöstergeçlerin işaret ettiği nesnelerin yineleyicilerini döndürür. Bu

Page 194: C++ 2 (Kitap - Selami KOÇLU)

iterator tipi bir kez sağlandımı, aşağıdaki üyeleri StringPtr sınıfarayüzüne benzer adları gizleyerek ekleriz, ama bunlar onun ana sınıfınınfaydasız üyeleridir;

StringPtr::iterator begin( ) ; //ilk öğenin yineleyicisini döndürür.StringPtr::iterator end( ) ; //son öğeden sonrasının yineleyiciyisini döndürür.

Bu iki üye uygun yineleyicileri döndürdüğü için, StringPtr nesnesindebulunan öğeler kolayca sıraya konabilir.

int main( ){ StringPtr sg ; //sg nin bir şekilde doldurulduğu kabulediliyor sort(sg.begin( ), sg.end( )) ; //sg sıraya dizildi return 0 ;}///:~

Bunun tam olarak çalışması için StringPtr::iterator tipinin mutlakatanımlanması gerekir. Tipin adında da anlaşılacağı üzere iterator,StringPtr nin yuvalanmış sınıfıdır. Bunun yanında StringPtr::iterator usort( ) kalıp algoritması ile kullanılabilmesi için, yineleyici mutlakaRandomAccessIterator olmalıdır. Bundan dolayı StringPtr::iterator unkendisi bizzat varolan std::iterator dan türetilmiş olmalıdır. Bu daaşağıdaki önişlemleyici yönergesi tarafından belirtilir;

#include <iterator>

Bir sınıfı std::iterator sınıfından türetmek için, hem yineleyici tipi hem deyineleyicinin işaret ettiği veri tipi mutlaka belirtilmelidir. Dikkat edileceknokta; yineleyicimiz string* dayançtan kurtarma işini görüyor, bu yüzdengerekli veri tipi std::string olur, std::string* değil. Böylece iterator sınıfarayüzü aşağıdaki şekilde başlar;

class iterator: public std::iterator<std::random_access_iterator_tag, std::string>

Bunun ana sınıf belirtimi çok karışık olduğu için typedef kullanarak bu tipadını kısaltacağız.

Page 195: C++ 2 (Kitap - Selami KOÇLU)

typedef std::iterator<std::random_access_iterator_tag, std::string> Iterator ;

Artık StringPtr sınıf arayüzünü yuvalanmış sınıf iterator a kadar yenidentasarlayabiliriz;

class StringPtr: public std::vector<std::string* >{ typedef std::iterator<std::random_access_iterator_tag, std::string> Iterator ; public: class iterator: public Iterator ;

Şimdi StringPtr::iterator un özelliklerine bakalım;

__ iterator, StringPtr yi friend olarak tanımlar, bu yüzden iterator unyapıcı işlevi private olabilir; yapılabilecek en anlamlı şey StringPtr ninsadece bizzat kendisinin iterator ları inşa edebileceğidir. Bunlardan başkaStringPtr nin ana sınıfı bir iterator sağlamış olduğundan, StringPtrnesnelerindeki bilgilere bu iterator la erişilebilir, böylece iterator un privatekısmı aşağıdaki gibi olur;

friend class StringPtr ;std::vector<std::string* >::iterator d_current ;iterator(std::vector<std::string* >::iterator const& d_current): d_current(current) {}

__ iterator nesneleri StringPtr::begin( ) ve StringPtr::end( ) üyelerincedöndürülür. Bunlar StringPtr sınıfında satıriçi olarak tanıımlanırlar. İşte;

iterator begin( ){ return iterator(this->std::vector<std::string* >::begin( )) ;}iterator end( ){ return iterator(this->std::vector<std::string* >::end( ) ) ;}

__ iterator un geriye kalan bütün üyeleri public tir. Bunların

Page 196: C++ 2 (Kitap - Selami KOÇLU)

gerçeklenmeleri çok kolaydır. Temelde, verilen d_current yineleyicisinidayançtan kurtarır ve özel hallere sokar. Bir RandomAccessIterator utanımlarken, en azından mutlaka aşağıdaki işleçler kullanımdabulunmalıdır;

iterator& operator++( ) : önartım işleci

iterator& operator++( ){ ++d_current ; return *this ; }

iterator& operator--( ) : önazaltım işleci

iterator& operator--( ){ --d_current ; return *this ; }

iterator operator--( ) : sonradan azaltım işleci

iterator const operator--(int){ return iterator(d_current--) ; }

iterator& operator=(iterator const& other) : Bindirilmiş atama işleci.iterator nesnelerin kendileri bellek alanına yerleşim yapmadıklarından,bunu varsayılan atama işleci gerçekler.

bool operator==(iterator const& rhv) const : iki iterator nesnesinineşitliğini sınamak için kullanılır.

bool operator==(iterator const& other) const{ return d_current == other.d_current ; }

bool operator<(iterator const& rhv) const : sağ ve solyanlardakiyineleyicilerin öğelerini sınamakta kullanılır:

Page 197: C++ 2 (Kitap - Selami KOÇLU)

bool operator<(iterator const& other) const{ return **d_current < other.**d_current ; }

int operator-(iterator const& rhv) const : solyan yineleyicisi ilesağyan yineleyicisi arasında işaret edilen öğelerin sayısını döndürür.

int operator-(iterator const& rhs) const{ return d_current-rhs.d_current ; }

Type& operator*( ) const : O anki yineleyicinin işaret ettiği nesnenindayancını döndürür. Bir InputIterator ve bütün const_iterator lerle, bubindirilmiş işlecin dönüş tipi Type const& olması lazımdır. Bu işleç birstring e dayanç döndürür. Bu string ise d_current değerinin dayançsızhalinin dayancı aracılığı ile sağlanır. d_current, string* öğelerinin biryineleyicisidir, string in kendisine varabilmek için iki tane dayançtankurtarma işlemi gerekir:

std::string& operator*( ) const{ return **d_current ; }

iterator const operator+(int stepsize) const : Bu işleç o ankiyineleyiciyi adım büyüklüğü kadar ilerletir.

iterator const operator+(int step) const{ return iterator(d_current+step) ; }

iterator const operator-(int stepsize) const : Bu işleç o ankiyineleyiciyi adım büyüklüğü kadar azaltır.

iterator const operator-(int step) const{ return iterator(d_current-step) ; }

Yineleyicileri varolan yineleyicilerden de inşa etmek mümkündür.Burada yapıcı kullanmak zorunluluğu yoktur, bunun yerine varsayılan

Page 198: C++ 2 (Kitap - Selami KOÇLU)

kopya yapıcı kullanılabilir.

std::string* operator->( ) const: ilave bir eklenme işlecidir. Buradasadece bir tane dayançtan kurtarma işleci gerekir, string göstergeçi döner,göstergeç aracılığı ile string öğelerine erişime izin verilir.

std::string* operator->( ) const{ //yineleyicinin işaret return *d_current ; //ettiği struct alanlarına } //erişim, örnek: it->length( )

Bunlardan başka iki tane daha eklenme işleci bulunur; operator+( ) veoperator-( ) . Bunlar RandomAccessIterator tarafından resmen istenmezama kullanımını bilmelidir.

iterator& operator+=(int step){//yineleyiciyi n adım arttır d_current+=step ; return *this ; }

iterator& operator-=(int step){//yineleyiciyi n adım azalt d_current-=step ; return *this ; }

Öteki yineleyiciler için gereken arayüz daha basittir. Rastgele erişimliyineleyiciye gereken araryüzün sadece alt kümesi bunlar için yeterlidir.Örnek olarak; öncü yineleyici (ForwardIterator) hiçbir zaman rastgeleadım büyüklüğü ile ne azaltılır ne de arttırılır. Bu yüzden öncüyineleyicilerde bu görevleri yerine getiren işlevler arayüzden çıkarılabilir.Tabii o zaman kullanılacak uzantı std::forward_iterator_tag olur.

reverse_iterator Uygulaması

Bir yineleyici bir kez gerçekleştirildimi, buna uygun reverse_iterator dakaşla göz arasında hemen uygulanabilir. std::iterator a görestd:.reverse_iterator bulunduğundan, bir yineleyici sınıf inşa ettiğimizdeters yineleyici bizim için gayet güzel inşa edilir. İnşa etmek istediğimiz tersyineleyici için, yapıcı işlevi yineleyici tipin nesnesine ihtiyaç duyar.

Page 199: C++ 2 (Kitap - Selami KOÇLU)

StringPtr ye ters yineleyici gerçekleştirmek için, ihtiyaç duyulan tek şeyarayüzde reverse_iterator tipini tanımlamaktır. Bu da iterator sınıfınınarayüzünden sonra bir satır kod eklememizi gerektirir;

typedef std::reverse_iterator<iterator> reverse_iterator ;

Son olarak StringPtr in arayüzüne rbegin( ) ve rend( ) üyeleri eklenir.Bunlar gayet güzel bir şekilde satıriçi olarak uygulanır;

reverse_iterator rbegin( ){ return reverse_iterator(end( )) ;}

reverse_iterator rend( ){ return reverse_iterator(begin( )) ;}

revese_iterator yapıcı işlevin aldığı verilere dikkat edelim; terslenmişyineleyicinin başlangıç noktası reverse_iterator un yapıcısı end( ) üye ileberaberken elde edilir: end( ) yani yineleyici alanının son noktası demektirbu. Terslenmiş yineleyicinin son noktası, reverse_iteratorun yapıcısıbegin( ) üye ile beraberken elde edilir: begin( ) yani yineleyici alanınınbaşlangıç noktası demektir. Aşağıdaki basit program StringPtr deRandomAccessIterator kullanımını göstermektedir;

#include <iostream>#include <algorithm>#include “stringptr”using namespace std ;int main(int argc, char** argv){ StringPtr sp ; while(*argv) sp.push_back(new string(*argv++)) ; sort(sp.begin( ), sp.end( )) ; copy(sp.begin( ), sp.end( ), ostream_iterator<string>(cout, “ “)) ; cout<<”\n======\n” ; sort(sp.rbegin( ), sp.rend( )) ; copy(sp.begin( ), sp.end( ), ostream_iterator<string>(cout, “ “)) ; cout<<endl ;}

Page 200: C++ 2 (Kitap - Selami KOÇLU)

//when called as:z.out izmir tire anadolu ulus selamigenerated output:z.out izmir anadolu tire selami ulus= = = = = =ulus selami tire anadolu izmir z.out//

Page 201: C++ 2 (Kitap - Selami KOÇLU)

burası boş bırakıldı

Page 202: C++ 2 (Kitap - Selami KOÇLU)

5. BÖLÜM

Standart Kalıp Kütüphanesi (SKK) veAlgoritmalar

Standart Kalıp kütüphaneleri (standart template library=STL); kaplar(containers), kalıp algoritmaları (generic algorithms), yineleyiciler(iterators), işlev nesneleri (function objects), uyarlaştırıcılar (adaptors) vebellek yerleştiricilerden (allocators) meydana gelmiştir. SKK, veri yapıları

Page 203: C++ 2 (Kitap - Selami KOÇLU)

ve algoritmalardan oluşan genel amaçlı bir kütüphanedir. Algoritmalardakullanılan veri yapıları anlam bakımından soyut olup, algoritmalaruygulamada her veri tipi ile kullanılabilir.Algoritmalar bu soyut veri tipleri üzerinde çalışabilir, zira bunlar kalıptabanlı algoritmalardır.

SKK Unsurları

Yukarıda belirttiğimiz gibi Standart Kalıp Kütüphanesi gayet iyiyapılandırılmış farklı parçaların birleşiminden oluşmuştur. SKK nınanahtar unsurları kaplar (containers), yineleyiciler (iterators) vealgoritmalardır (algorithms). __ Kaplar (containers) : Belli özellikteki nesneler topluluğunu yönetmekiçin kullanılırlar. Her çeşit kap kendi üstünlük ve sakıncalarına sahiptir.Bundan dolayı farklı kap tipleri, programlardaki toplulukların farklıgereksinimlerini karşılar. Kaplar diziler (sequence) veya ilişkili(associative) listeler olarak hazırlanırlar. Ya da her öğe için özel biranahtara sahiptirler.__ Yineleyiciler (iterators): Nesne topluluklarındaki öğelerin arasındadolaşmak için kullanılırlar. Bu topluluklar, kap veya kapların alt kümeleriolabilir. Yineleyicilerin esas üstünlüğü, herhangi bir kap tipi için küçükama ortak arayüz sağlamasıdır. Örnek olarak; arayüzün bir işlemiyineleyicinin adımını topluluktaki bir sonraki öğeye götürür. Bu işlemtopluluğun iç yapısından bağımsız gerçeklenir. Yani üzerinde çalışılan topluluğun dizi veya ağaç olup olmadığı dikkate alınmaz. Bununnedeni her kap tipi kendi yineleyicisine sahiptir, bunlar da kap sınıfının içyapısını bildiğinden yineleyici hep doğru olanı yapar.Yineleyicilerin arayüzü, bildik göstergeçler için olanlarla hemen hemenaynıdır. Yineleyiciyi bir artırmak için operator++ işleci çağrılır.Yineleyicinin herhangi bir değerine ise erişebilmek için ise, operator*işleci kullanılır. Yani yineleyici “bir sonraki öğeye git” çağrısını “uygunolana git” e çeviren bir çeşit cingöz (smart) göstergeçtir.__ Algoritmalar (algorithms): Topluluk öğelerini işlemlemek içinkullanılırlar. Örneğin; değişik amaçlar için öğeleri araştırabilir, sırayakoyabilir, değiştirebilir veya basit şekilde kullanabilir. Algoritmalaryineleyicileri kullanırlar. Böylece herhangi bir kapla çalışan bir algoritmasadece bir kez yazılır, zira yineleyiciler için yineleyici arayüzü bütün kaptipler için ortaktır. Algoritmalara esneklik kazandırmak amacı ilealgoritmaların çağırabildiği, belli bazı yardımcı işlevleri de siz

Page 204: C++ 2 (Kitap - Selami KOÇLU)

ekleyebilirsiniz. Bu sayede genel bir algoritmayı ihtiyaçlarınıza uygun halegetirip kullanabilirsiniz. Hatta bu ihtiyaç çok karmaşık veya çok özel olsabile. Örneğin; kendinize özgü araştırma kıstaslarınızı veya öğeleri birarayagetiren özel işlem oluşturabilirsiniz.SKK düşüncesi veri ve işlemlerin ayrılması temeline dayanır. Kap sınıflarıverileri yönetir, düzenlenebilir algoritmalar işlemleri tanımlar, veyineleyiciler bu iki unsuru yapıştırır. Yani yineleyiciler herhangi biralgoritma ile herhangi bir kap arasında ilişki sağlar.SKK düşüncesi, nesne yönelimli programlama ile ters düşer: SKK veri ilealgoritmaları birleştirmekten ziyade birbirinden ayırır. Bunu böyleyapmanın nedeni çok önemlidir. İlke olarak her çeşit kabı her çeşitalgoritma ile birleştirebilirsiniz, çerçeve hala küçük olmasına rağmensonuç gayet esnektir. SKK nin bir temel tavrı da bütün unsurlarının rastgele tiplerle çalışmasıdır.Adında anlayacağınız gibi Standart Kalıp Kütüphanesinin (SKK) bütünunsurları, tipler için kalıplardır (verilen tipler gereken işlemleri gerçekler).Bundan dolayı SKK, kalıpla programlamanın (generic programming) iyibir örneğidir. Kaplar ve algoritmalar, tipler ve sınıflar içindoğumhanelerdir.SKK oldukça doğurgan unsurlar sağlar. Özel amaçlar için belliuyarlayıcılar (adapters) ve işlev nesneleri (function objects veya functors)kullanarak, algoritmaları ve arayüzleri destekleyebilir, kısıtlayabilir veyadüzenleyebilirsiniz. Şimdi bunları örneklerle adım adım ilerleyerekanlatacağız.

Kaplar Kap sınıfları veya kısaca kaplar, öğeler topluluğunu yönetir. Farklıihtiyaçları gidermek için SKK farklı kaplar sağlar. Genel olarak iki çeşitkap bulunur; 1- Dizi kapları (sequence containers), her öğenin belli bir yerde bulunduğudüzenlenmiş topluluklardır. Öğe yeri, yerleştirmenin zamanı ve mevkisincesaptanır. Bu düzen, öğe değerinden bağımsızdır. Örneğin; bir topluluğayedi öğeyi herbiri ötekinin ardına gelecek şekilde koyarsanız, öğeler tamolarak sizin yerleştirdiğiniz düzendedir. SKK öntanımlı olarak 3 tane dizikap sınıf bulundurur: vector, deque, ve list.2- İlişkili kaplar (associative containers), öğe yerinin belli bir sıralamakıstasına bağlı olarak, değerine göre belirlendiği sıralanmış topluluklardır.Bir topluluğa yedi öğe koyarsanız, bunların düzeni değerlerine bağlıdır.

Page 205: C++ 2 (Kitap - Selami KOÇLU)

Yani yerleştirme sırası değil yerleştirilenin değeri önemlidir. SKK da 4tane öntanımlı ilişkili kap sınıfı bulunur: set, multiset, map ve multimap. İlişkili kaplar özel bir çeşit dizi kabı olarak düşünülmelidir. Zira onlar bellibir sıralama kıstasına göre düzenlenmiş topluluklardır.

Dizi Kapları

Aşağıda yeralan dizi kapları, SKK da öntanımlı olarak bulunurlar.

** vector ler** deque ler ** list ler

Yukarıdakilere ek olarak string leri ve normal dizileri (array) de dizikapları olarak kullanabilisiniz.

'vector' ler

vector, öğelerini dinamik bir dizide yönetir. Denk gelen dizin numarasıyazıldığında, rastgele erişime izin verilir. Dizinin sonundaki yere öğeyerleştirmek ve oradan öğe silmek çok hızlı gerçeklenir. Bununla beraber,dizinin orta yerine veya başına öğe yerleştirmek zaman alır. Sebebi ise,ilgili yere öğe yerleştirirken gerekli yeri boşaltmak için takip eden bütünöğelerin hareket etmeleridir. Şimdi bir örnekle vector konusunuinceleyelim. Örneğimizde 10 tane int sayısı bulunduran vectorbulunmaktadır ve vector öğeleri daha sonra çıkışa yazdırılmaktadır.

//B05:vector1.cpp//10 tane int öğeli vektör #include <iostream>#include <vector>using namespace std;int main( ){ vector<int> depo ; //int öğelerin konduğu depo vector ü for(int i=1; i<=10; ++i){ depo.push_back(i) ; }//değerleri 1 den 10 a kadar olan int öğeleri depoya yerleştiriyor. for(int i=0; i<depo.size( ); ++i){ cout<<depo[i]<<' ' ;

Page 206: C++ 2 (Kitap - Selami KOÇLU)

}//bütün öğeleri bir boşluk bırakarak yazdır cout<<endl ;}///

#include <vector> başlık dosyası vectorleri oluşturmak için eklenir.vector<int> depo ;bildirimi int tipinde depo adında vector ü oluşturur. Burada herhangi birilk değer ataması yapılmadığı için, yapıcı işlev tarafından boş depo olarakoluşturulur. push_back(i) işlevi kap sonuna bir öğe ekler. Bu üye işlevbütün dizi kaplar (sequence container) için kullanılabilir. depo.push_back(i) ;

size( ) üye işlevi kap içindeki öğelerin toplam sayısını verir.

for(int i=0; i<depo.size( ); ++i){....}

size( ) üye işlevi de herhangi bir kap sınıfı için kullanılabilir. Köşeliayraçlar kullanarak vector ün herhangi bir öğesine erişilebilir.

cout<<depo[i]<<' ' ;

standart çıktıdan elde edilen sonuç aşağıdaki gibi olur;

1 2 3 4 5 6 7 8 9 10

'deque' ler

deque terimi ingilizcede “double ended queue” yani çift uçlu kuyruktanımından gelir. Her iki doğrultuda genişleyebilen dinamik dizianlamındadır. Öğelerin kuyruk baş veya sonuna eklenmesi çok hızlıgerçeklenir. Ama öğenin orta kısıma yerleştirilmesi, takip eden bütünöğelerin yer değiştirmesi yüzünden uzun sürer. Örneğimiz, deque kabınınbaş kısmına yerleştirilen 1.1 den 9.9 e kadar olana kayan noktalı sayılar, veonların çıkışa yazdırılması göstermektedir.

//:B05:deque1.cpp

Page 207: C++ 2 (Kitap - Selami KOÇLU)

//kayan noktalı sayıların bulunduğu deque#include <iostream>#include <deque>using namespace std ;int main( ){ deque<float> depo; //depo isimli deque float öğeleri bulunduruyor. for(int i=1; i<=9; ++i){ depo.push_front(i*1.1) ; //ön kısıma yerleştir } //bütün öğeleri 2 boşlukla yazdır for(int i=0; i<depo.size( );++i){ cout<<depo[i]<<' '; } cout<<endl ;}///

deque ler için aşağıdaki başlık dosyası eklenir

#include <deque>

aşağıdaki bildirim ise float tipleri barındıran depo adlı boş bir dequeoluşturur;

deque<float> depo ;

öğeleri yerleştirmek için ise aşağıdaki işlev kullanılır;

depo.push_front(i*1.1) ;

push_front( ) üye işlevi, yeni öğenin topluluk başına yerleştirilmesinisağlar. Bu çeşit yerleştirme öğelerin ters sıralamada düzenlenmesineyolaçar. Zira her yeni öğe bir önceki öğenin önüne yerleştirilir. Bu yüzdenprogramın çıktısı aşağıdaki gibi olur;

9.9 8.8 7.7 6.6 5.5 4.4 3.3 2.2 1.1

Bir deque kabına push_back( ) üye işlevi ile, öğeleri arka taraftan dayerleştirmek mümkündür. vector lerde push_front( ) üye işlevi yoktur vekullanılmaz eğer vector lerde topluluğun en başına öğe yerleştirmekisterseniz çok verimsiz olur, zira bütün öğelerin yer değiştirmesi

Page 208: C++ 2 (Kitap - Selami KOÇLU)

gerekecektir. Kısaca, bir topluluğun başına öğe yerleştirmek isterseniz,deque kabını kullanmak daha akıllıca olur. SKK kaplarında birbirindenfarklı olan üye işlevler, zamanı verimli kullanmaya yardım ederler.

'list' ler

Bir list çift bağlı öğelerin bulunduğu liste olarak oluşturulur. Bununanlamı; listede bulunan her öğe bellekte kendi dilimine sahiptir ve ayrıca,bu yerin öncesi ve sonrasına bağlıdır. list ler rastgele erişime izin vermez.Rastgele erişim derken bir konuya açıklık getirelim; rastgele erişimkafasına göre takılıp erişmek değildir, burada erişilecek bellek noktasıdizin içinde verilen rakam tarafından belirlenir. Yani rastgele erişimde;verilen bellek noktasına öteki bellek noktalarından geçmeden doğrudanerişilir. İşte list ler buna izin vermez. Örnek olarak; list teki beşinci öğeyeerişmek için, bağlantılar zinciri takip edilerek mutlaka ilk dört öğesindengeçilmelidir. Bununla birlikte, bir önceki veya bir sonraki öğeye erişmeksabit bir süre alır. Böylece herhangi bir öğeye genel erişim zamanla doğruorantılıdır. (ortalama uzaklık öğe sayısı ile orantılıdır). Bu uygulamavector ve deque ler tarafından sağlanan sabit erişim zamanından çokkötüdür.list in üstünlüğü herhangi bir yerdeki öğenin kaldırılmasının veyayerleştirilmesinin çok hızlı gerçekleşmesidir. Sadece bağlantılar mutlakadeğiştirilmelidir. Bunun anlamı; list ortasında yer alan bir öğenin yerdeğiştirmesi, vector veya deque de bulunan öğenin yer değiştirmesinegöre çok daha hızlıdır.Aşağıdaki örnek, karakterler için boş bir list oluşturur, 'a' dan 'z' ye kadarolan bütün karakterleri yerleştirir, bir döngü kullanarak bütün öğeleriyazdırır, yazdırıp daha sonra topluluğun ilk öğesini kaldırır.

//:B05:list1.cpp//list kap sınıf kullanımı örneğidir#include <iostream>#include <list>using namespace std ;int main( ){ list<char> depo ; //karakter öğeleri için depo adlı list nesnesi for(char c='a'; c<='z'; ++c){ depo.push_back(c) ; }//a dan z ye bütün karakterleri depoya koyar.

Page 209: C++ 2 (Kitap - Selami KOÇLU)

while(!depo.empty( )){ cout<<depo.front( )<<' ' ; depo.pop_front( ) ; }//öğeleri yazdır, öğeler varken, ilk öğeyi yazdır ve sil yani kaldır. cout<<endl ;}///:~

Karakter değerleri tutan list sınıf nesnesi oluşturabilmek için #include <list> başlık dosyası sisteme eklenir. depo adlı list nesnesi iseaşağıdaki deyimle oluşturulur;

list<char> depo ;

list sınıfının üye işlevlerinden empty( ) ise kapta öğe olmadığınıbelirlemede kullanılır. Döngü koşul belirleme true olduğu sürece devameder. (yani kapta öğe olduğu sürece)

while(!depo.empty( )){.....}

Döngü içinde yer alan front( ) üye işlevi asıl ilk öğeyi döndürür.

cout<<depo.front( )<<' ' ;

pop_front( ) üye işlevi ise ilk öğeyi kaldırır yani siler.

depo.pop_front( ) ;

Hemen belirtelim pop_front( ) kaldırılan öğeyi döndürmez. Bu yüzdenönceki iki deyim tek deyimde birleştirilmez. Programın çıktısı sisteminkarakter kümesine bağlıdır. ASCII karakter kümesi olduğunda, çıktıaşağıdaki gibi olur;

a b c d e f g h i j k l m n o p q r s t u v w x y z

Herhalde karakterlerin tamamını bir döngü ile yazdırmak ve çıktıdan eldeedip ilk öğeyi sildirmek biraz acayip gelebilir. Bununla birlikte [] işlecinikullanarak list öğelerine doğrudan erişmek mümkün değildir. Zira listöğelerine rastgele erişim olmaz. Hemen belirtelim rastgele erişim ile

Page 210: C++ 2 (Kitap - Selami KOÇLU)

kastedilen kafaya göre takılıp rastgele bir öğeye erişmek demek değildir,sadece, belirtilmiş herhangi bir öğeye doğrudan erişimdir. Bundan başkabir yol daha bulunmaktadır; yineleyicileri (iterator) kullanıp list öğeleriarasında dolaşıp istenenleri yapmak.Ayrıca ilk bölümümüzde incelediğimiz string leri de, SKK kap sınıfıolarak kabul edebiliriz. C++ string sınıf nesneleri (basic_string< >, stringve wstring) ilgi alanımıza girer. string ler vector lere benzerler ama tekfark vardır; sadece karakterleri tutarlar. vector ve deque kullanımı ile ilgilidüşüncelerimizi açıklayalım. Benzer görevleri yerine getiren gerek dequegerekse vector den hangisi, ne zaman, nasıl tercih edilecek?. C++standartları 23.1.1 bölümünde kap sınıf tercihleri ile ilgili bazı önerilerbulunmaktadır. “vector varsayım olarak kullanılması lazım olan dizi(sequence) tipidir, deque ise öğe yerleştirme ve silmelerinin dizi baş vesonlarında olduğu seçimin veri yapısıdır.” Şimdi burada ben gayet içten bir şekilde muhalif görüşümü açıklayacağım;varsayım olarak kullanılması istenen vector yerine, deque yi tavsiyeediyorum. Özellikle uygulamada kullanılan tipler yerleşik olmayan tipleryani, sınıf (class) veya struct tipler olduğunda, kullanılmalıdır. Kapbelleğinin bitişik olması gerekmediği zamanları da buna ekleyelim.vector ve deque hemen hemen aynı arayüze yani üye işlevlere sahiptirler.deque de, vector de olmayan push_front( ) ve pop_front( ) üye işlevleribulunur. (evet vector deki capacity( ) ve reserve( ) üye işlevleri deque debulunmaz, ama bu üstünlük değil bilakis zayıflıktır) vector ve deque arasındaki temel yapı farkı; her iki kabın iç saklamadüzeninden kaynaklanır. Bir deque saklama alanındaki yerleşimlerisayfalar halinde yapar, ve her sayfada sabit sayıda öğe bulunur; işte buyüzden deque ile iskambil destesi karşılaştırılır, gerçi dizide öğelerin heriki uçtan işleme tabi olması yüzünden deque adı, çift uçlu kuyruktankaynaklanır (“double ended queue”) ya neyse. Öte yandan, vector belleğinbitişik parçalarını kullanır, ve öğeleri sadece dizi sonuna etkin biçimdeyerleştirebilir. deque nin sayfa düzenlemesi birçok üstünlüğe sahiptir;** Bir deque, kap başında insert( ) ve erase( ) işlemlerini sabit bir süredetamamlar, ama vector de böyle olmaz. Standartta belirtilen deque kullanımsebebi ise; kuyruğun her iki ucundan işlem yapılabilmesiydi.** Bir deque belleği, özellikle sanal belleği olmayan sistemlerde, işletimsistemine daha uyumlu kullanır. Örnek olarak 10 megabaytlık bir vectortek parça 10 megabaytlık bellek kullanır, 10 megabaytlık deque ise küçükparçalar halinde 10 megabaytlık belleği dilimleyip kullanır bu yüzden

Page 211: C++ 2 (Kitap - Selami KOÇLU)

deque, bellek kullanımında daha verimlidir.** Bir deque nin bir vector e göre kullanımı hem kolay hem degenişlemesi daha verimlidir. vector ün yapıp deque nin yapamadığıişlemler sadece capacity( ) ve reserve( ) dir. Aslında deque nin buişlevlere ihtiyacı bulunmamaktadır. reserve( ) işlevini çok sayıdapush_back( ) işlevinden önce çağırmak, vector de, o an yeterli büyüklüğesahip olmadığı her sefer sonunda, aynı tamponun hep daha geniş sürümbellek yeniden yerleşimlerini iptal eder. deque de böyle bir sorunbulunmaz. Çok sayıda push_back( ) den önce bir deque::reserve( )olması herhangi bir bellek yerleşimi iptal etmez, zira bellekyerleşimlerinden hiçbiri iptal edilmez. deque hepsini bir kere de yapsada,öğeleri yapıştırsada, aynı sayıda ek sayfayı belleğe yerleştirmekzorundadır.İlginç olan bir şey de, standart yığıt (stack) uyarlayıcısı sadece tekdoğrultuda genişler, ve orta yere veya öteki tarafa yerleşim hiçbir zamangerekmez. Varsayılan deque uygulaması ;

template <typename T, typename Container=deque<T> >typename stack{.....};

Aslında herşey mekanizmalar ve politikalarla ilgilidir. Programcılıktamekanizma elde olan bütün olanaklar olarak tanımlanabilir. Olanaklarıniçinde gerek donanım gerekse seçilen programlama dilinin yetenekleri, herikisi de dahildir. Politikalar ise programcının mekanizmaları kullanırkenuyguladığı taktikler bütünü, yani stratejidir. Programcı olarak uygulanacaken temel usul; hedefe en kısa sürede varabilen en az masraflı yolubulabilmektir. Program en alt düzey donanımla çalışabilmeli, en az bellekkullanmalı ve isteneni en kısa sürede gerçekleştirmelidir. Tabii bunuyapabilmek için de sıkı bir maça lazım. Burada uyulacak en basit kural;hedefe kilitlenmektir. Zira birden fazla tavşanı kovalayan hiçbir tavşanıyakalayamaz. Önce konunuzu çok iyi bileceksiniz, sonra çözümü kesinbulacaksınız, ondan sonra artık başardığınızla bir bütün olacaksınız. Sonrabilirsiniz, eski bir deyim vardır, insan aklı nisyan ile malüldür denir. Yaniinsan unutur. Bundan dolayı yapıp geliştirdiklerinizi yazacaksınız. Yarınbir gün bu neydi diye sorduğunuzda, yazılardan hemen hatırlarsınız. Ehartık bu kadar baba nasihatlardan sonra canınızın istediğini yapın. Amatemel kuralı unutmayın. Termodinamiğin temel yasası zaten; minimumenerji harcayıp maksimum iş (aslı karmaşa ama ben yararlı olsun diye iş

Page 212: C++ 2 (Kitap - Selami KOÇLU)

dedim) elde etmektir. Bu kural, yiğitlerin bütün yoğurt yeme çeşitleri içingeçerlidir. Şimdi gelelim kap sınıfların ayrıntılarına.

'vector' Kapları

vector sınıfı genişleyebilir dizi (array) gibi davranır. Bir programda vectorsınıfı kullanılmadan önce başlık dosyaları arasında mutlaka aşağıdakiyönerge yazılmalıdır;

#include <vector>

Yapıcı işlevler, işleçler ve üye işlevleri ise aşağıda gösterilmiştir;

** Yapıcı işlevler;-- Bir vector boş olarak inşa edilebilir;

vector<string> object ;

vector de saklanan veri tipini belirtmek için; 'vector' kap adından hemensonra, açılı ayraçlar arasına (yani < >) veri tip adı yerleştirilir. Buuygulama bütün vector kaplar için geçerlidir.-- Bir vector belli sayıda öğe ile başlatılabilir. Bütün kapların ve özelliklevector lerin en güzel özelliklerinden biri; veri öğelerini veri öğe tipininvarsayılan değerleri ile başlatabilmeleridir. Bu ilk değer atama işlemi için,veri tipinin varsayılan yapıcı işlevi kullanılır. Sınıfsız veri tipleri (yaniyerleşik tipler için) için bu değer sıfırdır (0). Bundan dolayı int vector içinilk değer sıfır (0) olur. Örnekler;

vector<string> object(10, string(“selam”)) ; //10 selam ile başlarvector<string> container(10) ; //10 boş string

-- Bir vector yineleyiciler (iterator) kullanarakta başlatılabilir. Bir vector ü5 öğeden 10 a (son öğede dahil) kadar başlatmak için varolan birvector<string> ile aşağıdaki yapı kullanılır;

extern vector<string> container ;vector<string> object(&container[5], &container[11]) ;

İkinci yineleyici (&container[11]) tarafından işaret edilen son öğe, object

Page 213: C++ 2 (Kitap - Selami KOÇLU)

te saklanmaz. Bu örnek yineleyicilerin kullanıldığı basit bir örnektir. İlkdeğerle başlayan öğeler ikinci yineleyicinin işaret ettiği öğe dışında bütünaradaki değerleri kapsar. Standart gösterim; [begin, end).

-- Bir vector kopya yapıcı işlev kullanarakta başlayabilir:

extern vector<string> container ;vector<string> object(container) ;

-- Kapların standart işleçlerinin yanında vector ler dizin işlecini dedestekler. Dizin (index) işleci vector ün öğelerine yeniden atama yaparkenveya, ilgili dizindeki öğe saptanırken kullanılır. Örnek olarak içi boş birvector ün aşağıdaki deyimi hata üretir;

ivector[0]=20 ; //hata zira vector boştu

Bundan dolayı vector kendiliğinden genişleyemez, ve dizi sınırlarındansorumlu değildir. Bu durumda sorunu çözümlemek için önce vectoryeniden boyutlanır, veya ivector.push_back(20) yazılmalıdır.

** vector üye işlevleri:

-- Type &vector::back( ) :Bu üye işlev vector deki son öğenin dayancını döndürür. vector ancak boşdeğilse, üye işlevin kullanımı programcının sorumluluğundadır.

-- vector::iterator vector::begin( ) :Bu üye vector deki ilk öğeyi işaret eden yineleyici döndürür. Eğer vectorboş ise, vector::end( ) döndürür.

-- vector::clear( ) :Bu üye vector ün bütün öğelerini siler.

-- bool vector::empty( ) :Bu üye vector de hiç öğe yoksa true döndürür.

-- vector::iterator vector::end( ) :Bu üye vector deki son öğeden ilerdeki yeri işaret eden yineleyicidöndürür.

Page 214: C++ 2 (Kitap - Selami KOÇLU)

-- vector::iterator vector::erase( ) :Bu üye vector ün belli bir alanındaki öğeleri silmek için kullanılabilir;*erase(pos), pos yineleyicisince gösterilen öğeyi siler ve ++pos değerigeri döner. *erase(ilk, ötesi), yineleyicinin [ilk, ötesi) alanındaki öğeleri siler. Ötesigeri döner.

-- Type &vector::front( ) :vector deki ilk öğeye dayanç döndürür. Ancak vector boş değilse, bu üyeişlevin kullanımı programcının sorumluluğundadır.

-- ....vector::insert( ) :Belli bir yerden başlayarak öğeler yerleştirilebilir. Dönüş değeri çağrılaninsert( ) e bağlıdır.*- vector::iterator insert(pos) : pos daki Type tipinin varsayılan değeriniyerleştirir, pos döndürülür.*- vector::iterator insert(pos, value) : pos daki değeri yerleştirir, posdöner.*- void insert(pos, ilk, ötesi) : [ilk, ötesi) yineleyici alanındaki öğeleriyerleştirir.*- void insert(pos, n, value) : n tane, pos da value değerli öğe yerleştirir.

-- void vector::pop_back( ) :Bu üye vector deki son öğeyi kaldırır. Boş vector de herhangi bir şeyolmaz.

-- void vector::push_back(value) :Bu üye vector sonuna value ekler.

-- void vector::resize( ) :Bu üye vector de o anki öğe sayısını değiştirmek için kullanılır.*- resize(n, value) : vector boyutunu n adete çıkarmak için kullanılır.value bir seçimdir, olsa da olur olmasa da. Eğer vector genişletilmiş vevalue yoksa, o zaman ilave öğelerin ilk değerleri; kullanılan veri tipininvarsayılan değerleri olur. Aksi durumda ilave öğeler value değeri ilebaşlatılır.

-- vector::reverse_iterator vector::rbegin( ) :

Page 215: C++ 2 (Kitap - Selami KOÇLU)

Bu üye vector deki son öğeyi işaret eden yineleyiciyi döndürür.

-- vector::reverse_iterator vector::rend( ) :Bu üye vector deki ilk öğeden öncesini işaret eden yineleyiciyi döndürür.

-- unsigned vector::size( ) :Bu üye vector deki öğe sayısını döndürür.

-- void vector::swap( ) :Bu üye, aynı veri tipte iki vector ün takas edilmesinde kullanılabilir.Örnek; //B05:swap.cpp#include <iostream>#include <vector>using namespace std ;int main( ){ vector<int> v1(8) ; vecor<int> v2(10) ; v1.swap(v2) ; cout<<v1.size( )<<” “<<v2.size( )<<endl ;}///:~//üretilen çıktı://10 8

'list' Veri yapısı

list kap sınıfı liste veri yapısı oluşturur. Liste veri yapısı kullanılmadanönce program başlıkları arasında mutlaka aşağıdaki yönerge bulunmalıdır;

#include <list>

Bir list aşağıda şekil 5.1 de gösterildiği gibi düzenlenir. list birbirleri ilegöstergeçler aracılığı ile bağlantılı olan ayrık list öğelerinden oluşur. listiki doğrultuda geçilebilir; FRONT da soldan sağa yani en sağdaki NULL (0)-göstergeçe varana kadar olan ile, BACK ten başlayan sağdansola sürüp en soldaki 0-NULL göstergeçe kadar giden geçiştir.Hem vector hem de list saklanacak öğe sayısı tam bilinmediği zamankullanılacak ver yapılarıdır. Bunun yanında, iki veri yapısından birinitercih ederken uyulması gereken bazı kurallar bulunur;

Page 216: C++ 2 (Kitap - Selami KOÇLU)

__Erişimlerin çoğu rastgele olduğu zaman vector tercih edilen veri yapısıolmalıdır. Örnek olarak bir metin yapısında kullanılan karakter sıklıklarınısaptarken

vector<int> frekans(256) ;

ipucu olarak iyi bir veri yapısıdır. Alınan karakterlerin sayısı frekansvector üne dizin (index) değeri olarak konur.

Şekil 5.1 list veri yapısı

__Önceki örnek ikinci kuralı tarif ettiği gibi vector ün tercih nedenini debelirtir. Öğe sayısı önceden bilinirse (ve program süresince değişmezse)vector list e göre her zaman tercih edilmelidir.__ Öğe yerleştirme ve silmelerinin yaygın olduğu durumlarda genel olaraklist tercih edilmelidir. Aslında benim tecrübelerime göre vector birtakımeksiklikler olmasına rağmen uygulamalarda çok daha hızlıdır. list ve vector arasında tercih yaparken gözönüne alınacak kıstaslarhakkında biraz daha düşünmek gerekmektedir. vector ün dinamik olarakbüyüyebildiği bir gerçek olmasına rağmen, dinamik büyüme çok sayıdaveri kopyalamaya yolaçmaktadır. Açıktır ki milyonlarca veri yapısınınkopyalanması, bilgisayarlar çok hızlı olsa bile epey zaman alır. Öte yandançok sayıda öğenin list e yerleştirilmeleri istenmeyen verilerinkopyalanmalarını gerektirmez. Yeni bir öğenin list eyerleştirilmesigöstergeçlerle ilgili cambazlıklar gerektirir. Şekil 5.2 de yenibir öğenin eklenmesi görülüyor. Burada dört öğeli yeni bir list

FRONT

BACK

NULL (0)

NULL (0)

Page 217: C++ 2 (Kitap - Selami KOÇLU)

oluşturabilmek için ikinci ve üçüncü öğeler arasına yeni bir öğeyerleştirilmiştir. Herhangi bir öğeyi list ten kaldırmakta oldukça basittir.Şekil 5.3 te üç öğeli bir list ten ikinci öğe nasıl çıkarılır gösterilmekte.Yine burada da göstergeçlerle cambazlık yapmak gerekmektedir. Ama buişlem yeni bir öğe eklemeye

Şekil 5.2 list e öğe eklemegöre daha kolaydır. Sadece iki göstergecin yolu değiştirilir. list ve vectorler arasında yapılan karşılaştırmayı özetleyecek olursak; herhalde varılacaken iyi sonuç, hangi veri yapısının seçileceği konusunda kesin açık biryanıtın olmadığıdır. Sadece gözönüne alınacak bazı kuralların olduğunukabul etmek en iyisidir. Eğer kötünün kötüsü ile karşılaşmakistemiyorsanız, daha iyisini bulmak için bir işlem süresi ölçücücü(profiler) kullanmak hepsinden iyidir.

Şekil 5.3 list ten öğe çıkartma

yeni veri FRONT NULL

BACKNULL

front

FRONT

NULLBACK

NULL

Page 218: C++ 2 (Kitap - Selami KOÇLU)

Biz yine de bu konudaki değişik düşünceleri dikkate almadan list tebulunan yapıcı işlevleri, işleçleri ve üye işlevleri anlatalım;

** Yapıcı işlevler:

-- Bir list boş olarak inşa edilebilir :

list<string> object ;

vector lerde olduğu gibi boş bir list te öğe sorgulamak hataya yolaçar.

--Bir list belli sayıda öğe ile başlatılabilir. Varsayalım ilk değer atamasıaçık olarak yapılmamış olsun, o zaman kullanılan veri tipinin varsayılandeğeri veya varsayılan yapıcı işlevi bu işi yapar. Örnek;

list<string> object(10, string(“selam”)) ; //10 selam la başlatırlist<string> container(10) ; //10 boş string le başlar

-- Bir list iki yineleyici kullanılarak başlatılabilir. Bir list i vector<string>in 5. öğesinden 10. öğesine (sonuncu da dahil) kadar başlatabilmek içinaşağıdaki yapı kullanılır;

extern vector<string> container ; list<string> object(&container[5], &container[11]) ;

-- Bir list kopya yapıcı işlev kullanarakta başlatılabilir;

extern list<string> container ;list<string> object(container) ;

-- Kap sınıflarda kullanılan standart işleçlerin dışında, list kap sınıfınaözgü başka işleç bulunmamaktadır.

** Aşağıdaki üye işlevler list ile birlikte kullanılmaktadır;

-- Type &list::back( ) :Bu üye işlev list teki son öğenin dayancını döndürür. list boş olmadığında,bunun kullanımı tamamen programcının sorumluluğundadır.

front

Page 219: C++ 2 (Kitap - Selami KOÇLU)

-- list::iterator list::begin( ) :Bu üye işlev list in ilk öğesini işaret eden yineleyiciyi döndürür, list boşise o zaman list::end( ) döner.

-- list::clear( ) :Bu üye işlev list te bulunan bütün öğeleri siler.

-- bool list::empty( ) :Bu üye işlev list te hiç öğe bulunmazsa true (doğru) döndürür.

-- list:.iterator list:.end( ) :Bu üye işlev list te bulunan son öğenin ötesini işaret eden yineleyicidöndürür.

-- list::iterator list::erase( ) :Bu üye işlev list te belli yerlerdeki öğeleri silmekte kullanılabilir.*- erase(pos) : pos tarafından belirtilen noktadaki öğeyi siler. ++posyineleyicisi döner.*- erase(ilk, ötesi) : [ilk, ötesi) yineleyicisinin gösterdiği alandaki öğelerisiler. ötesi döndürülür.

-- Type &list::front( ) :Bu üye işlev list te bulunan ilk öğeye dayanç döndürür. list boş olmadığızaman, üye işlevin kullanımı programcının sorumluluğundadır.

-- ... list::insert( ) :Bu üye işlev list e öğeler eklemek istendiği zaman kullanılır. Dönüş değeriçağrılan insert( ) işlevinin sürümüne bağlıdır:*- list::iterator insert(pos) : pos ta Type tipinde varsayılan değeriyerleştirir. pos döner.*- list::iterator insert(pos, value) : pos ta value değerini yerleştirir, posdöner.*- void insert(pos, ilk, ötesi) : [ilk, ötesi) yineleyici alanındaki öğeleriyerleştirir.*- void insert(pos, n, value) : pos ta, value değerlerine sahip n tane öğeyiyerleştirir.

-- void list<Type>::merge(list<Type> other) :Bu üye işlev o anki ve öbür list lerin sıralı düzende olduğunu kabul eder.

Page 220: C++ 2 (Kitap - Selami KOÇLU)

(aşağıda bulunan sort( ) üyesine bakınız). Bu kabule dayalı olarak otherın öğelerini o anki list e öyle yerleştirirki değiştirilen yeni list hala sıralıdüzende kalır. Eğer her iki list sıralı değilse, sonuç list her iki list inbaşlangıç durumundaki sıralamaya mümkün olduğunca en yakınsıralamada düzenlenir. list<Type>::merge( ), list teki verileri sıralamakiçin Type::operator<( ) kullandığından bu işleç mutlaka bulunmalıdır.Şimdiki örnek merge( ) üye işlevinin kullanımını göstermektedir; list'object' sıralı değildir, bu yüzden elde edilen sonuç list mümkün olan enyakın düzende olur.

//:B05:liste.cpp#include <iostream>#include <string>#include <list>using namespace std ;void showlist(list<string> &hedef){ for(list<string>::iterator from=hedef.begin( ); from!=hedef.end( ); ++from) cout<<*from<<endl ;}

int main( ){ list<string> ilk ; list<string> ikinci ; ilk.push_back(string(“alfa”)) ; ilk.push_back(string(“bravo”)) ; ilk.push_back(string(“golf”)) ; ilk.push_back(string(“ankara”)) ; ikinci.push_back(string(“ahmet”)) ; ikinci.push_back(string(“banu”)) ; ikinci.push_back(string(“abdullah”)) ; ikinci.push_back(string(“bolu”)) ; ilk.merge(ikinci) ; showlist(ilk) ;}///:~

list in kendisi veri olarak kullanılırsa, merge( ) list i değiştirmez;object.merge(object), 'object' list ini değiştirmez.

Page 221: C++ 2 (Kitap - Selami KOÇLU)

-- void list::pop_back( ) :Bu üye işlev list teki son öğeyi kaldırır. Ama list boş ise herhangi bir şeyolmaz.

-- void list::pop_front :Bu üye işlev list teki ilk öğeyi kaldırır. list boş ise herhangi bir şey olmaz.

-- void list::push_back(value) :Bu üye işlev list sonuna value ekler.

-- void list::push_front(value) :Bu üye işlev list in ilk öğesinden önceye value yi ekler.

-- void list::resize( ) :Bu üye işlev list teki öğe sayısını değiştirmek için kullanılır.*- resize(n, value) :Üye işlevin bu kullanımı list i n öğeli hale getirir, value ise olsa da olurolmasa da, yani sadece bir seçenektir. list genişletilip n sayısıbelirtilmezse, eklenen yeni öğeler de, veri tipinin varsayılan başlatmadeğerleri kullanılır. Aksi taktirde yeni öğelerde başlatma value ile olur.

-- list::reverse_iterator list::rbegin( ) :Bu üye işlev list te yeralan son öğeyi işaret eden yineleyiciyi döndürür.

-- void list::remove(value) :Bu üye işlev list teki bütün value tekrarlarını siler. Aşağıdaki örnekprogram bütün 'selam' ları list nesne sinden kaldırır.

//:B05:tekrar_sil.cpp#include <iostream>#include <string>#include <list>using namespace std ;int main( ){ list<string> nesne ; nesne.push_back(string(”selam”)) ; nesne.push_back(string(”alemler”)) ; nesne.push_back(string(”selam”)) ; nesne.push_back(string(“alemler”)) ;

Page 222: C++ 2 (Kitap - Selami KOÇLU)

nesne.remove(string(“selam)) ; while(nesne.size( )){ cout<<nesne.front( )<<endl ; nesne.pop_front( ) ; }}///:~//çıktı :alemleralemler//

-- list::reverse_iterator list:.rend( ) :Bu üye işlev list te yer alan ilk öğeden öncesini işaret eden yineleyiciyidöndürür.

-- unsigned list:.size( ) :Bu üye işlev list teki öğe sayısını döndürür.

-- void list::reverse( ) :Bu üye işlev list teki öğe düzenini ters yapar. back( ) öğesi front( ) olur,veya tersi de doğrudur.

-- void list::sort : Bu üye işlev list i sıraya dizer. Kullanımı ile ilgili örnek, unique( ) üyeişlevin aşağıdaki uygulamasında verilmiştir. list teki öğeleri sıraya dizmekiçin list<Type>::sort( ), Type::operator<( ) u kullanır, burada işleçmutlaka temin edilmiş olmalıdır.

-- void list::splice(pos, object) : Bu üye işlev object içeriklerini o anki list e aktarır. splice( ) üyesikullanılırken object yineleyicisinin pos noktasına yerleştirerek başlanır.splice( ) ardından object boştur. Örnek ;

//:B05:aktar.cpp//splice kullanımı#include <iostream>#include <string>#include <list>using namespace std ;

Page 223: C++ 2 (Kitap - Selami KOÇLU)

int main( ){ list<string> object ; object.push_front(string(“selam”)) ; object.push_back(string(“alemler”)) ; list<string> argument(object) ; object.splice(++object.begin( ), argument) ; cout<<”nesne kapsar”<<object.size( )<<”elements, “<< ”arguments kapsar, “<<argument.size( )<< “elements, “<<endl ; while(object.size( )){ cout<<object.front( )<<endl ; object.pop_front( ) ; }}///:~

Başka bir usul de bölünecek argument in ilk öğesini gösteren, argumentyineleyicisi argument i takip etmesidir. Ya da object e bölünmesi lazımgelen, yineleyici çalışma alanını gösteren [begin, end) i tanımlayan ikiyineleyici; begin ve end tarafından takip edilmesidir.

-- void list::swap( ) :Bu üye işlev aynı veri tiplerini kullanan iki list i takas eder.

-- void list::unique( ) :Bu üye işlev sıraya konmuş bir list te yanyana bulunan birbirinin aynıöğeleri list ten kaldırır. list<Type>::unique( ) üye işlevi Type::operator==( ) işlecini özdeş (aynı) öğeleri saptamada kullanır. Buyüzden bu işleç dizgede tanımlanmış olmalıdır. Bir örnek olarak aynıkelimeleri list ten kaldıralım;

//:B05:Benzeri_sil.cpp//list te unique( ) kullanımını#include <iostream>#include <string>#include <list>using namespace std ;//merge( ) örneğine bak!!void showlist(list<string> &target) ;void showlist(list<string> &target){

Page 224: C++ 2 (Kitap - Selami KOÇLU)

for(list<string>::iterator from=target.begin( ); from!=target.end( ) ; ++from) cout<<*from>>” “ ; cout<<endl ;}

int main( ){ string array[]={“izmirli”, “damla”, “bravo”, “damla”) ; list<string> target(array, array+sizeof(array)/sizeof(string)); cout<<”ilkin sahip olunan: ”<<endl ; showlist(target) ; target.sort( ) ; cout<<”yeni hal: “<<endl ; showlist(target) ; target.unique( ) ; cout<<”unique( ) ten sonra: “<<endl ; showlist(target) ;}///:~

//üretilen çıktı:ilkin sahip olunan:izmirli damla bravo damlayeni hal:bravo damla damla izmirliunique den sonra:bravo damla izmirli//

'queue' Kap sınıfı

Bu sınıf kuyruk denilen veri yapısını oluşturur. Bir programın içinde queuesınıfını kullanabilmek için önişlemleyiciye aşağıdaki yönergeyi vermekgerekir;

#include <queue>

Aşağıdaki şekilde görüldüğü üzere queue ye bir noktadan (arka) yeniöğeler eklenir (yani yazılır) bir noktadan da (ön) öğeler kaldırılabilir (yani

Page 225: C++ 2 (Kitap - Selami KOÇLU)

okunabilir). Yani önce giren önce çıkar (veya ilk giren ilk çıkar).

Şekil 5.4 - Queue (kuyruk)

queue kap sınıfı aşağıdaki işleç, üye işlev ve yapıcı işlevleri kullanımınısağlar;

** Yapıcı işlevler:

-- Bir queue boş olarak inşa edilebilir;

queue<string> object ;

vector kap sınıfında olduğu gibi, eğer boş queue nin öğesi kullanılmayakalkılırsa hata ortaya çıkar.

-- Bir queue kopya yapıcı işlev kullanılarak başlatılabilir;

extern queue<string> container ;queue<string> object(container) ;

** queue kabı sadece basit kap işleçlerini destekler.

** queue nin üye işlevleri aşağıda verilmiştir;

-- Type &queue::back( ) :Bu üye işlev kuyruğun son öğesine dayanç döndürür. Sadece kuyruk boşdeğilse, kullanma sorumluluğu tamamen programcıdadır.

-- bool queue:.empty( ) :Bu üye işlev kuyruk boş ise true (yani bool 1) döndürür.

arkaön

Page 226: C++ 2 (Kitap - Selami KOÇLU)

-- Type &queue::front( ) :Bu üye işlev kuyruktaki ilk öğeye dayanç döndürür. Sadece kuyruk boşdeğilse bu üye işlevin kullanımı programcının sorumluluğundadır.

-- void queue::push(value) :Bu üye işlev kuyruğun arkasına yeni bir value öğesi ekler.

-- void queue::pop( ) :Bu üye işlev kuyruğun önünde yer alan öğeyi kaldırır. Bu üye işlev öğeyidöndürmez belirtelim. Boş bir kuyrukta herhangi bir şey olmaz.

-- unsigned queue::size( ) :Bu üye işlev kuyruktaki öğe sayısını döndürür.

Burada bazı özellikleri not düşelim; queue yineleyicileri ve dizinbelirtecini ([]) desteklemez. queue nin sadece ilk ve son öğelerineerişilebilir. Bir kuyruk aşağıdaki yollarla boşaltılabilir;

*- önde yer alan öğeler sıra ile kaldırılır.*- aynı veri tipinde boş bir kuyruk kendisine atanarak.*- yıkıcı işlevi çağrılarak.

'priority_queue' Kabı

Bu kap öncelikli kuyruk veri yapısını oluşturur. priority_queuekullanılmadan önce aşağıdaki önişlemleyici yönergesi mutlaka yazılmışolmalıdır;

#include <queue>

Bir priority_queue, queue nin aynısıdır, sadece farkı; veri öğelerininyerleştirilmelerine öncelik kurallarına göre izin vermesidir. Önceliklikuyruklara gerçek hayattta, hava alanlarına girerken denetlemelerderastlanır. Geç kalan yolculara sırada öne geçme izni verilir; böylece ötekiyolculara göre daha üst önceliğe sahip olurlar.Öncelikli kuyrukta operator<( ) işleci kullanılır. Bu işleç önceliklikuyrukta saklanan veri öğelerinin, öncelik derecelerini belirlemektekullanılır. Değeri daha küçük olan daha düşük önceliklidir. Bu yüzden

Page 227: C++ 2 (Kitap - Selami KOÇLU)

öncelikli kuyruk, değerleri varma esnalarında sıraya dizer. Aşağıdaki örnekpriority_queue nin basit bir kullanımıdır. cin nesnesinden kelimeleri okurve cout nesnesine kelimeleri dizili bir liste olarak yerleştirir.

//:B05:makam_sahibi.cpp//priority_queue kullanımı#include <iostream>#include <string>#include <queue>using namespace std ;int main( ){ priority_queue<string> s ; string kelime ; while(cin>>kelime) s.push(kelime) ; while(s.size( )){ cout<<s.top( )<<endl ; s.pop( ) ; }}///:~

Maalesef kelimeler ters düzende sıralanır; zira öncelikli kuyrukta öncegörünen <- işleci ASCII dizisinde daha sonra ortaya çıkar. Bu sorunuçözmek için string veri tipi etrafında bir düren (wrapper) sınıf tanımlanır,böylece operator<( ) işleci istediğimiz gibi tanımlanır. Böylece ASCII deerken görünen kuyrukta da ilk görünür. İşte değiştirilmiş program;

//:B05:dogru_makam_sahibi.cpp//değişikliğe uğramış istediğimiz sonucu veren program #include <iostream>#include <string>#include <queue>class Metin{ std::string d_s; public: Metin(std::string const &str) : d_s(str) {} operator std::string const &( ) const{

Page 228: C++ 2 (Kitap - Selami KOÇLU)

return d_s ; } bool operator<(Metin const &right) const{ return d_s>right.d_s ; }};using namespace std ;int main( ){ priority_queue<Metin> s ; string kelime ; while(cin>>kelime){ s.push(kelime) ; } while(s.size( )){ kelime=s.top( ) ; cout<<kelime<< endl ; s.pop( ) ; }}///:~

Yukarıdaki örnek programda düren (wrapper) sınıf operator<( ) işlecinistring sınıfının kendisinden farklı, isteğimize uygun tanımlar.priority_queue de bunlardan başka olasılıklarda saklanabilir, örnekvector, öğeleri ters düzende okunur.priority_queue kabının işleçleri üye işlevleri ve yapıcı işlevleri aşağıdaverilmiştir;

** Yapıcı işlevler :-- Bir priority_queue boş olarak inşa edilebilir;

priority_queue<string> object ;

Boş priority_queue nin bir öğesine erişilmek istenirse vector de olduğugibi hata verir.

-- Bir öncelikli kuyruk (priority_queue) kopya yapıcı işlev kullanılarakbaşlatılabilir.

extern priority_queue<string> container ;priority_queue<string> object(container) ;

Page 229: C++ 2 (Kitap - Selami KOÇLU)

** priority_queue kapların sadece temel işleçlerini destekler.

** Aşağıdaki üye işlevler priority_queue ye aittirler;

-- bool priority_queue::empty( ) :Bu üye işlev öncelikli kuyruk boşsa true döndürür.

-- void priority_queue::push(value) :Bu üye işlev öncelikli kuyrukta uygun yere value yi yerleştirir.

-- void priority_queue::pop( ) :Bu üye işlev öncelikli kuyruğun en üstünde bulunan öğeyi kaldırır. Öğe buüye işlev tarafından döndürülmez. Öncelikli kuyruk boşsa herhangi bir şeyolmaz.

-- unsigned priority_queue::size( ) :Bu üye işlev öncelikli kuyruktaki öğe sayısını döndürür.

-- Type &priority_queue::top( ) :Bu üye işlev öncelikli kuyruktaki ilk öğeye dayanç döndürür. Ancak eğeröncelikli kuyruk boş değilse bu üye işlevin kullanım sorumluluğuprogramcıya aittir.

Burada hemen belirtelim priority_queue yineleyicileri veya dizinişleçlerini desteklememektedir. Bir öncelikli kuyruğu boşaltmak için;

*ardarda en üst öğeler kaldırılır.*aynı veri tipi kullanılarak boş bir öncelikli kuyruk taması yapılır.*yıkıcı işlev çağrılır.

'deque' Kabı

Dek olarak telaffuz edilen deque çift uçlu kuyruk veri yapısıdır. dequekabı kullanılmadan önce mutlaka aşağıdaki önişlemleyici yönergesikullanılmalıdır;

#include <deque>

deque, queue ile karşılaştırılabilir. deque nin queue den tek farkı; her iki

Page 230: C++ 2 (Kitap - Selami KOÇLU)

uçtan da yazılabilir ve okunabilir olmasıdır. Aslında birçok uygulamabakımından deque, queue ye göre daha işleviktir. Aşağıdaki incelemelersırasında göreceğiniz gibi, üye işlevler bunu açıkça göstermektedir. Birdeque, iki queue ile bir vector kabından oluşmuştur. vector un her ikiucunda işlem yapabilir. Öğelerin rastgele yerleştirilmelerinde ve her ikiuçtan öğelerin eklenmelerinde veya kaldırılmalarında vector yerine dequedüşünülmelidir. Bu konuda birinci cildimizde de ayrıntılı bir incelemebulunmaktadır. deque nin yapıcı işlevleri, işleçleri ve üye işlevleri aşağıda verilmiştir;

** Yapıcı işlevler :-- Bir deque boş olarak inşa edilebilir;

deque<string> object ;

vector de olduğu gibi boş bir deque nin öğesine dayanç alındığında hataürer.

-- Bir deque belli sayıda öğe ile başlatılabilir. Varsayım olarak eğerbaşlatma değeri açık olarak beyan edilmemişse, varsayılan değer veya asılveri tipi için varsayılan yapıcı işlev kullanılır. Örnek;

deque<string> object(8, string(“selam”)) ; //8 tane selam ile başlardeque<string> container(19) ; //19 tane boş string

-- Bir deque iki tane yineleyici kullanılarak başlatılabilir. Birvector<string> in 5. öğesinden 10. öğesine (10. öğe de dahil)kadar olandeğerlerle deque yi başlatmak için aşağıdaki yapı kullanılır;

extern vector<string> container ;deque<string> object(&container[5], &container[11]) ;

-- Bir deque kopya yapıcı işlev kullanılarakta başlatılabilir.

extern deque<string> container ;deque<string> object(container) ;

** Kaplarda kullanılan standart işleçlerin dışında deque, dizin işlecini dedestekler. ([ ]) dizin işleci deque ye rastgele öğe atanmasında veya ondan

Page 231: C++ 2 (Kitap - Selami KOÇLU)

öğe elde edilmesinde kullanılır. Yalnız dizinde belirtilen öğe yerindemutlaka bulunmalıdır.

** deque de aşağıdaki üye işlevler bulunur;

-- Type &deque::back( ) : Bu üye işlev deque deki son öğeye dayanç döndürür. Sadece eğer dequeboş değilse bu üye işlevin kullanımı programcının sorumluluğundadır.

-- deque::iterator deque::begin( ) :Bu üye işlev deque deki ilk öğeyi işaret eden yineleyiciyi döndürür.

-- void deque::clear( ) :Bu üye işlev deque deki bütün öğeleri siler.

-- bool deque::empty( ) :Bu üye işlev deque de öğe olmazsa true döndürür.

-- deque::iterator deque::end( ) :Bu üye işlev deque deki son öğeden sonrasını işaret eden yineleyiciyidöndürür.

-- deque::iterator deque::erase( ) :Bu üye işlev deque de belli bir bölgede yer alan öğeleri silmedekullanılabilir.

erase(pos) : pos un işaret ettiği yerdeki öğe silinir. ++ pos döner.erase(ilk, beyond) : [ilk, beyond) yineleyici bölgesindeki öğeleri siler.Beyond geri döner

-- Type &deque::front( ) :Bu üye işlev deque te yer alan ilk öğeye dayanç döndürür. Sadece ancakdeque boş değilse bu üye işlevin kullanımı programcınınsorumluluğundadır.

-- ... deque::insert( ) :Bu üye işlev belli bir yerden başlamak üzere deque ye öğeler yerleştirmekiçin kullanılabilir. Döndürülen değer insert( ) işlevine bağlıdır :

deque::iterator insert(pos) : Type tipinin varsayılan değerini pos ta yerleştirir ve pos geri döner.

Page 232: C++ 2 (Kitap - Selami KOÇLU)

deque::iterator insert(pos, value) : pos ta value yi yerleştirir, pos döner.

void insert(pos, ilk, beyond) : Bu üye işlev [ilk, beyond) yineleyici bölgesindeki öğeleri yerleştirir.

void insert(pos, n, value) :value değerine sahip n tane öğeyi pos tan başlayarak yerleştirir.

-- void deque::pop_back( ) :Bu üye işlev deque deki son öğeyi kaldırır. deque boşsa herhangi bir şeyolmaz.

-- deque::pop_front( ) :Bu üye işlev deque deki ilk öğeyi kaldırır. Eğer deque boşsa bir şeyolmaz.

-- void deque::push_back(value) :Bu üye işlev deque sonuna bir value değerli öğeyi ekler.

-- void deque::push_front(value) :Bu üye işlev deque nin ilk öğesinden önce value değerini yerleştirir.

-- void deque::resize( ) :Bu üye işlev deque de o anki saklı olan öğe sayısını değiştirmek içinkullanılabilir. resize(n, value) : deque büyüklüğünü n değerine boyutlandırır. Value bir seçenektir. Olsada olur olmasada. deque genişletilir ve value de belirtilmezse, eklenenöğeler kullanılan veri tipinin varsayılan değeri ile başlatılır. Aksi taktirdevalue ile başlatılırlar.

-- deque::reverse_iterator deque:.rbegin( ) :Bu üye işlev deque deki son öğeyi işaret eden yineleyiciyi döndürür.

-- deque::reverse_iterator deque::rend( ) :Bu üye işlev deque deki ilk öğeden öncesini işaret eden yineleyiciyidöndürür.

-- unsigned deque::size( ) :Bu üye işlev deque deki öğe sayısını döndürür.

Page 233: C++ 2 (Kitap - Selami KOÇLU)

-- void deque::swap(argument) :Bu üye işlev aynı veri tipini kullanan iki deque yi takas etmektekullanılabilir.

'map' Kabı

map sınıfı ilişkili, sıralı dizi gerçekleştirir. Bir map herhangi bir kapsınıfının kabul edebileceği tipte, anahtar/değer çiftleri ile doldurulur.Tipler hem anahtar hem de değerle ilişkili olduğu için, açılı ayraçiçerisinde her iki tipi de belirtmek zorundayız. İlki anahtarın tipini, ikinciside değerin tipini gösterir. Örnek olarak; anahtarı string değeri ise doubletipinde olan bir map aşağıdaki gibi yazılır:

map<string, double> object;

Anahtar kendisi ile ilişkili olan bilgiye erişmekte kullanılır. Çağrılanbilgiye değer adı verilir. Telefon fihristini buna örnek olarak verebiliriz.Anahtar olarak tanıdıkların isimleri, onların telefon numaraları veya başkabilgileri (işi, adresi, posta kodları vs.. ) ise değer olarak kullanılır. mapsınıfı anahtarları sıraya dizdiği için, anahtarın operator<( ) işleci mutlakatanımlanmalı ve kullanılabilir olmalıdır. Örnek olarak; göstergeçlerinanahtarlar için kullanılması kötü bir düşüncedir, zira göstergeçlerin sırayadizilmesi onların gösterdiği değerlerin sıraya dizilmesinden farklı birşeydir.map sınıfı ile yapılan iki temel işlem; anahtar/değer birlikteliklerininsaklanması ve anahtarları bildirilen değerlerin saptanmasıdır. Dizin değeriolarak anahtar kullanarak her ikisi için dizin işleci kullanılabilir. Dizinişleci solyan olarak kullanılırsa, değer yerleştirme gerçeklenir. Sağyanolarak kullanılırsa, anahtarın ilişkili değeri elde edilir. Her anahtar map tesadece bir kez bulunabilir. Eğer aynı anahtar bir kez daha kullanılırsa, yenideğer önceki değerle yerdeğiştirir, önceki değer kaybolur.Belli bir anahtar/değer birlikteliği map içine açık veya gizliyerleştirilebilir.Açıkça yerleşim tercih edilirse, önce anahtar/değerbirlikteliği inşa edilmelidir. Bu amaçla her map bir value_type ı, map tesaklanacak değerleri oluşturmada kullanmak için tanımlar. Örnek olarakmap<string, int> bir değeri aşağıdaki gibi inşa eder;

Page 234: C++ 2 (Kitap - Selami KOÇLU)

map<string, int>::value_type seValue(“selam”, 1) ;

map<string, int>: ile ilişkili olan value_type tır ve anahtarın tipi stringtir, değerin tipi ise int tir. Anonim value_type nesneler de kullanılabilirÖrnek;

map<string, int>::value_type(“selam”, 1) ;

map<string, int>::value_type(... ) ifadesini tekrar tekrar yazmaktansabunu basitleştirmek ve kolaylaylaştırmak için typedef kullanımı dahauygundur;

typedef map<string, int>::value_type StringIntValue

Bu typedef ı kullanarak map<string, int> için değerler artık aşağıdakigibi inşa edilir;

StringIntValue(“selam”, 1) ;

Son olarak, map tarafından kullanılan anahtar/değer birliktelikleri pairtarafından da gösterilebilir;

pair<string, int>(“selam”, 1) ;

map kap sınıfında bulunan yapıcı işlevler, işleçler ve üye işlevler aşağıdaverilmiştir;

** Yapıcı işlevler :-- Bir map boş olarak inşa edilebilir;

map<string, int> object ; //object nesne demek hatırlatalım

Burada bir şeyi daha hatırlatalım; map lerde saklanan değerlerin kendileride kap olabilir. Aşağıdaki örnek değeri pair olan bir map tanımlar; buradabir kap başka bir kabın içinde yuvalanmıştır;

map<string, pair<string, string> > object ;Burada yeri gelmişken hemen bir konuyu da bildirelim; kapatan açılıayraçlar arasında her zaman bir boşluk bırakın (yani > > yapın). Bu birzorunluluktur. Zira eğer boşluk bırakılmazsa (yani >> yapılırsa), derleyici

Page 235: C++ 2 (Kitap - Selami KOÇLU)

tarafından sağa kaydırma olarak algılanır. Tabii bu burada bizimistemediğimiz bir işlemdir.

-- Bir map iki yineleyici kullanılarak başlatılabilir. Yineleyiciler ya map invalue_type değerlerini işaret ederler ya da basit pair nesnelerini. pair lerkullanılırsa, ilk öğeler anahtarları ikinci öğelerde değerleri temsil ederler.Örnek;

pair<string, int> pa[] ={ pair<string, int>(“bir”, 1), pair<string, int>(“iki”, 2), pair<string, int>(“uc”, 3),};map<string, int> object(&pa[0], &pa[3]) ;

Yukarıdaki örnekte pair<string, int> yerine map<string, int>::type_value te yazılabilir de. begin ilk yineleyici endde ikinci yineleyici olduğunda, [begin, end) map i başlatmakta kullanılır.Sezgilerin zıttına belki map yapıcı işlevi sadece new anahtarlarını girer.pa nın son öğesi “bir” 3 olsaydı, sadece iki öğe map e girebilirdi : “bir” 1,“iki” 2 . “bir” 3 için değerlendirme sessizce gözardı edilirdi.map, yineleyicilerin işaret ettiği verilerin kendi kopyalarını alır. Bunuanlamak için aşağıdaki örneğe bakabilirsiniz;

//:B05:map_ornek.cpp//yineleyicilerin işaret ettikleri#include <iostream>#include <map>using namespace std ;class MyClass{public: MyClass( ){ cout<<”MyClass yapici islevi\n” ; } MyClass(const MyClass &other){ cout<<”MyClass kopya yapici islevi\n” ; } ~MyClass( ){ cout<<”MyClass yikici islevi\n” ; }

Page 236: C++ 2 (Kitap - Selami KOÇLU)

};int main( ){ pair<string, MyClass> pairs[] ={ pair<string, MyClass>(“bir”, MyClass( )), }; cout<<”ciftler insa edildi\n” ; map<string, MyClass> mapsm(&pairs[0], &pairs[1]) ; cout<<”mapsm insa edildi \n” ;}///:~

//üretilen çıktı:MyClass yapici isleviMyClass kopya yapici isleviMyClass yikici isleviciftler insa edildiMyClass kopya yapici isleviMyClass kopya yapici isleviMyClass yikici islevimapsm insa edildiMyClass yikici islevi//

Bu programın çıktısını izlemeye aldığımız zaman, ilk önce MyClassnesnesinin yapıcı işlevi, pairs dizisinin anonim öğelerini başlatmak içinkullanıldığı görülür. Bu nesne kopya yapıcı işlev tarafından pairs dizisininilk öğesine kopyalanır. Daha sonra artık esas öğeye gerek duyulmaz, vesilinir. pairs dizisinin inşa edildiği nokta burasıdır. Artık map, mapöğesini inşa etmek için geçici pair nesnesini inşa eder. map öğesi inşaedildiğinde geçici pair nesneleri silinir. Neticede, program sonlandığızaman map teki pair öğesi de silinir.

-- Bir map, kopya yapıcı işlev kullanarakta başlatılabilir;

extern map<string, int> container ;map<string, int> object(container) ;

-- Kaplar için kullanılan standart işleçlerden başka, map kap sınıfı dizinişlecini de destekler. Böylece map in tek tek öğeleri öğrenilebilir veyayeniden atanabilir. Burada dizin işlecinin atanan verisi bir anahtardır.Atanan veri yani anahtar map te bulunmazsa, yeni bir veri öğesi otomatik

Page 237: C++ 2 (Kitap - Selami KOÇLU)

olarak map e eklenir ve yeni öğenin değer kısmını başlatmak için, yavarsayılan değer ya da varsayılan yapıcı işlev kullanılır. Dizin işlecisağyan olarak kullanılmışsa, bu varsayılan değer döndürülür.map in yeni bir öğesi veya başka bir öğenin yeniden ataması yapıldığızaman, atama işlecinin sağyan tipi map in değer kısmının tipi ile mutlakaaynı olmalıdır. Bu bir zorunluluktur. Örnek; map te “iki” öğesinin değerinideğiştirmek veya yenisini eklemek için aşağıdaki deyim kullanılır;

mapsm[“iki”]=MyClass( ) ;

** map kap sınıfında aşağıdaki üye işlevler bulur:

-- map::iterator map::begin( ) :Bu üye işlev map in ilk öğesini işaret eden yineleyiciyi döndürür.

-- map::clear( ) :Bu üye işlev map öğelerinin tümünü siler.

-- unsigned map::count(key) :Bu üye işlev eldeki anahtar map te bulunuyorsa 1 döndürür. Aksi taktirde0 döner.

-- bool map::empty( ) :Bu üye işlev map te hiç üye olmazsa true döndürür.

-- map::iterator map::end( ) :Bu üye işlev, map teki son öğeden ötesini gösteren yineleyiciyi döndürür.

-- pair<map::iterator map::iterator> map::equal_range(key) :Bu üye işlev, lower_bound( ) ve upper_bound( ) üye işlevlerinin dönüşdeğerlerine bağlı olarak, bir çift yineleyici döndürür. Bunun örneğiupper_bound( ) üye işlevi incelenirken verildi.

-- .... map::erase( ) :Bu üye işlev map ten belli bir tane veya belli bir grup öğeyi silmek içinsilmek için kullanılabilir; bool erase(key): map ten, anahtarı verilen öğeyi siler. Değer kaldırılmışsa true döner, yokeğer anahtarı verilen öğe bulunmuyorsa o zaman false döner.

Page 238: C++ 2 (Kitap - Selami KOÇLU)

void erase(pos):pos yineleyicisi tarafından işaret edilen öğeyi siler. void erase(first, beyond) : [first, beyond) yineleyici bölgesinde yer alan öğeleri siler.

-- map::iterator map::find(key) :Bu üye işlev anahtarı verilen öğenin yineleyicisini döndürür. Eğer öğebulunmuyorsa o zaman end( ) döner. Aşağaıdaki örnek find( ) üyeişlevinin kullanımını göstermektedir;

//:B05:bul.cpp//find( ) işlev kullanım örneği#include <iostream>#include <map>using namespace std ;int main( ){ map<string, int> object ; object[“bir”]=1 ; map<string, int>::iterator it=object.find(“bir”) ; cout<<” 'bir' “<<(it==object.end( ) ? “not” : “ ”)<<”bulundu\n” ; it=object.find(“uc”) ; cout<<” 'uc' “<<(it==object.end( ) ? “not” : “ “)<<”bulundu\n” ;}///:~//üretilen çıktı:'bir' bulundu'uc' not bulundu//

-- ... map::insert( ) :Bu üye işlev map e öğe yerleştirmekte kullanılır. Bununla birlikte varolananahtarlarla ilişkili olan değerler, yenileri ile yer değiştirmez. Dönüş değeriçağrılan insert( ) ün çeşidine bağlıdır;

pair<map::iterator, bool> insert(keyvalue)Yeni bir map::value_type map e yerleştirilir. Dönüş değeri isepair<map::iterator, bool> olur. Keyvalue de belirtilen anahtar eğerönceden map te varsa, o zaman false döner, bundan dolayı keyvalue mape yerleştirilmez. Her iki durumda da keyvalue de bildirilmiş olan anahtarınveri öğesi map::iterator alanınca işaret edilir. insert( ) ün bu çeşidinin

Page 239: C++ 2 (Kitap - Selami KOÇLU)

kullanım örneği aşağıda verilmiştir;

//:B05:yerles.cpp//insert( ) kullanımı#include <iostream>#include <map>#include <string>using namespace std ;int main( ){ pair<string, int> pa[] ={ pair<string, int>(“bir”, 10), pair<string, int>(“iki”, 20), pair<string, int>(“uc”, 30), }; map<string, int> object(&pa[0], &pa[3]) ; //{dort, 40} ve 'true' döner pair<map<string, int>::iterator, bool> ret=object.insert(map<string, int>::value_type (“dort”, 40)); cout<<boolalpha ; cout<<ret.first->first<<” “<<ret.first->second<<” “<<ret.second<<”“<< object[“dort”]<<endl ;//{dort, 40} ve false döner ret=object.insert(map<string, int>::value_type (“dort”, 0)); cout<<ret.first->first<<” “<< ret.first->second<<” “<< ret.second<<” “<<object[“dort”]<<endl ;}///:~//üretilen çıktı:dort 40 true 40dort 40 false 40//

Aşağıdakine benzer kendine özgü yapılara dikkat edilmelidir;

cout<<ret.first->first<<” “<<ret.first->second<<... ..

ret, insert( ) üye işlevince döndürülen pair e özdeştir. first alanımap<string, int> e bir yineleyicidir, yani map<string, int>::value_type egöstergeç olarak kabul edilebilir. Bu değer tiplerinin kendileri pair dir. Ve

Page 240: C++ 2 (Kitap - Selami KOÇLU)

bunlar 'first' ile 'second' alanlarına aittirler. Sonuç olarak; 'ret.first->first'bir map değerinin (string) anahtarıdır, ve 'ret.first->second' ise değerdir(int).

map::iterator insert(pos, keyvalue) : Bu yolla bir map::value_type, pos gözardı edilerek map e yerleştirilir,eklenen öğenin yineleyicisi döner.

void insert(first, beyond) : Bu üye işlev [first, beyond) alanındaki yineleyicilerin gösterdiği yerleremap::value_type öğeleri yerleştirir.

-- map::iterator map::lower_bound(key) :Bu üye işlev en azından önceden belirlenmiş anahtara eşit olan key in ilkanahtar değerini işaret eden yineleyiciyi döndürür. Böyle bir öğe yoksa ozaman map::end( ) döner.

-- map::reverse_iterator map::rbegin( ) :Bu üye işlev map teki son öğeyi işaret eden yineleyiciyi döndürür.

-- map:.reverse_iterator map:.rend( ) :Bu üye işlev map teki ilk öğeden öncesini işaret eden yineleyiciyidöndürür.

-- unsigned map::size( ) :Bu üye işlev map teki öğe sayısını döndürür.

-- void map::swap(argumant) :Bu üye işlev özdeş anahtar/değer tiplerine sahip iki map i birbiri ile takaseder.

-- map::iterator map::upper_bound(key) :Bu üye işlev önceden belirlenmiş key değerini aşan ilk key e ait olananahtar değer (keyvalue) öğesini gösteren yineleyiciyi döndürür. Böyle biröğe yoksa o zaman map:.end( ) döner. Aşağıda verdiğimiz örnekte equal_range( ), lower_bound( ) ve upper_bound( ) üye işlevleri kullanılmıştır:

//B05:map_uye.cpp

Page 241: C++ 2 (Kitap - Selami KOÇLU)

//equal_range, lower_bound, upper_bound#include <iostream>#include <map>using namespace std ;int main( ){ pair<string, int> pa[]={ pair<string, int>(“bir”, 10), pair<string, int>(“iki”, 20), pair<string, int>(“uc”, 30), }; map<string, int> object(&pa[0], &pa[3]) ; map<string, int>::iterator it ; if((it=object.lower_bound(“ik”)) !=object.end( )) cout<<”lower_bound 'ik' var o: ”<< it->first<<endl ; if(object.lower_bound(“ikii”) == object.end( )) cout<<”lower_bound 'ikii' yok”<<endl ; cout<<”lower_bound iki : “<<object.lower_bound(“iki”)->first<< “var\n” ; if((it=object.upper_bound(“ik”)) != object.end( )) cout<<”upper_bound 'ik' var o: “<<it->first<<endl ; if(object.upper_bound(“ikii”) == object.end( )) cout<<”upper_bound 'ikii' yok<<endl ; if(object.upper_bound(“iki”) == object.end( )) cout<<”upper_bound 'iki' yok “<<endl ; pair<map<string, int>::iterator, map<string, int>::iterator> p=object.equal_range(“iki”) ; cout<<”equal_range : 'first' gösterir: “<<p.first->first<<”, 'second'“<< (p.second == object.end( ) ? “yok” : p.second->first)<<endl ;}///:~//üretilen çıktı:lower_bound 'ik' var o:ikilower_bound 'ikii' yoklower_bound iki: varupper_bound 'ik' var o: ikiupper_bound 'ikii' yokupper_bound 'iki' yokequal_range : 'first' gösterir: iki, 'second' yok//

Page 242: C++ 2 (Kitap - Selami KOÇLU)

Bu bölümün ilk kısmında belirtildiği gibi map bir sıralı, ilişkili diziyitemsil eder. map içinde anahtarlar (key) sıraya dizilir. Bir uygulama mapte bulunan bütün öğeleri (ya sadece anahtarları ya da değerleri) ziyaretetmek zorunda kalırsa, begin( ) ve end( ) yineleyicilerini kullanmakmecburiyetindedir. Aşağıdaki örnek, bir map teki bütün anahtar vedeğerleri listeleyen basit bir tablo oluşumunu göstermektedir;

//:B05:tablo.cpp//basit tablolama#include <iostream>#include <iomanip>#include <map>using namespace std ;int main( ){ pair<string, int> pa[] ={ pair<string, int>(“one”, 10), pair<string, int>(“two”, 20), pair<string, int>(“three”, 30), }; map<string, int> object(&pa[0], &pa[3]) ; for(map<string, int>::iterator it=object.begin( ); it!=object.end( );++it) cout<<setw(5)<<it->first.c_str( )<<setw(5)<<it->second<<endl ;}///:~

//üretilen çıktı:one 10two 20three 30//

'multimap' Kap Sınıfı

multimap sınıfı aynı map sınıfı gibi (sıralı) ilişkili dizi oluşturur.multimap kapları kullanılmadan önce programda aşağıdaki önişlemleyiciyönergesi mutlaka belirtilmelidir.

#include <map>

Page 243: C++ 2 (Kitap - Selami KOÇLU)

map ile multimap arasındaki temel fark; multimap aynı anahtarla ilişkilibirden fazla değeri destekler. map ise sadece tek değerli anahtarlarıdestekler. Bir şeyi daha ekleyelim; multimap ayrıca aynı anahtarla ilişkiliçok sayıda aynı değeri de kabul eder.multimap, operator []( ) dizin işleci haricindeki bütün map üye işlevlerinidestekler. Nedenini kolaylıkla anlayabilirsiniz. Aynı anahtara çok sayıdadeğer girişine izin verilirse, sizce object[key] için hangi değerlerdöndürülür?.(!).. Bir önceki map üye işlevlerine istinaden multimap kap sınıfı içinbunlardan bazılarına daha fazla dikkat göstererek anlatacağız.

Üye işlevler:

-- unsigned map::count(key) :Bu üye işlev verilen anahtarla (key) ilişkili multimap e girenlerin sayısınıgeri döndürür.

-- ... multimap::erase( ) :Bu üye işlev map ten öğeleri silmekte kullanılır.

unsigned erase(key) :Verilen anahtara sahip bütün öğeleri siler. Silinen öğe sayısı döner.

void erase(pos) :pos un gösterdiği tek öğeyi siler. Aynı anahtara sahip olabilen başka öğelersilinmez. void erase(first, beyond) :[first, beyond) yineleyici alanının gösterdiği bütün öğeleri siler.

-- pair<multimap::iterator, multimap::iterator> multimap::equal_range(key) :Bu üye işlev multimap::lower_bound( ) ile multimap::upper_bound( )ın dönüş değerlerine bağlı olan bir çift yineleyici döndürür. İşlevmultimap te aynı anahtarlara sahip bütün öğeleri belirlemede kullanılanbasit bir araç sağlar. Bu üye işlevlerin kullanımını gösteren örnek,bölümün sonunda verildi.

-- multimap::iterator multimap::find(key) :

Page 244: C++ 2 (Kitap - Selami KOÇLU)

Bu üye işlev anahtarı key olan ilk değeri işaret eden yineleyiciyi döndürür.Eğer öğe yoksa, o zaman multimap::end( ) döner. Yineleyici birartırılarak aynı key e sahip bütün öğeleri ziyaret eder. Bu işlem yamultimap::end( ) ya da yineleyicinin first üyesi başka key olmayanakadar devam eder.

-- multimap::iterator multimap::insert( ) :Bu üye işlev normal biçimde uygulanır, map kabı ile dönenpair<multimap::iterator, bool> yerine multimap::iterator döndürür.Dönen yineleyici eklenen yeni öğeyi işaret eder.

lower_bound( ) ve upper_bound( ) işlevleri map ve multimapkaplarında aynı davranışı göstermelerine rağmen, multimap teki işlemleridaha fazla ilgiyi hak eder. Aşağıdaki örnek bu işlevlerin multimap tekikullanımlarını göstermektedir;

//:B05:cokmap:cpp//multimap teki işlev kullanımlarını#include <iostream>#include <map>using namespace std ;int main( ){ pair<string, int> pa[]={ pair<string, int>(“alpha”, 1), pair<string, int>(“bravo”, 2), pair<string, int>(“charley”, 3), pair<string, int>(“bravo”, 6), //düzensiz 'bravo' değerleri

pair<string, int>(“delta”, 5), pair<string, int>(“bravo”, 4), }; multimap<string, int> object(&pa[0], &pa[6]) ; typedef multimap<string, int>::iterator msiIterator; msiIterator it=object.lower_bound(“brava”) ; cout<<” 'brava' için lower_bound: “<<it->first<<”, “<< it->second<<endl; it=object.upper_bound(“bravu”) ; cout<<” 'bravu' için upper_bound: “<<it->first<<”, “<< it->second<<endl ; pair<msiIterator, msiIterator> itPair=object.equal_range(“bravo”) ;

Page 245: C++ 2 (Kitap - Selami KOÇLU)

cout<<” 'bravo' için equal_range: \n”; for(it=itPair.first; it!=itPair.second; ++it) cout<<it->first<<”, “<<it->second<<endl ; cout<<”upper_bound: “<<it->first<<”, “<<it->second<<endl ; cout<<” 'brav' için equal_range: \n”; itPair=object.equal_range(“brav”) ; for(it=itPair.first; it!=itPair.second; ++it) cout<<”it->first<<”, “<<it->second<<endl ; cout<<”upper_bound: “<<it->first<<”, “<<it->second<<endl ;}///:~

//üretilen çıktı:'brava' için lower_bound: bravo, 2'bravu' için upper_bound: charley, 3'bravo' için equal_rangebravo, 2bravo, 6bravo, 4upper_bound: charley, 3'brav' için equal_range:upper_bound: bravo, 2//

Aşağıdaki özelliklere her zaman dikkat edilmelidir;

** Olmayan anahtarlar için upper_bound( ) ve lower_bound( ) aynısonuçları üretirler; her ikisi de verilen anahtardan daha büyük bir anahtarınilk öğesini döndürür.

** Her ne kadar multimap te anahtarlar düzenlenmiş olsalarda, aynı(=özdeş) anahtarların değerleri düzenli değillerdir; ve bunlar girildiğidüzende geriye alınırlar.

'set' Kabı set sınıfı değerlerin sıralı olarak toplanmasını sağlar. set kaplarıkullanılmadan önce programın başında aşağıdaki önişlemleyici yönergesikullanılmalıdır;

Page 246: C++ 2 (Kitap - Selami KOÇLU)

#include <set>

'set' kabı herhangi bir kap sınıfı tarafından saklanabilen tipteki değerlerledoldurulur. Her değer set te sadece bir kez bulunabilir. set e yerleştirilecek belli bir değer net olarak oluşturulur; her set, kendindesaklanabilecek olan değerleri oluşturmak için bir value_type tanımlar.Örnek olarak set<string> için bir değer aşağıdaki gibi oluştururlur;

set<string>::value_type setValue(“selam”) ;

valu_type, set<string> ile ilişkilidir. set<string>::value_type satırını tekrar tekrar yazmak yerine hem yazımıkısaltmak hem de okunabilirliliği arttırmak için typedef kullanılır;

typedef set<string>::value_type StringSetValue

Bu typedef kullanıldığı zaman, set<string> için değerler aşağıdaki gibioluşturulur;

StringSetValue(“selam”) ;

Başka bir seçenek olarakta, set tipinin değerleri hemen kullanılabilir. Budurumda Type tipinin değeri set<Type>::value_type a farkettirilmedençevrilir.

Aşağıdaki yapıcı işlevler, işleçler ve üye işlevler set kabında bulunurlar;

** Yapıcı işlevler:

-- Bir set boş olarak inşa edilebilir :

set<int> object ;

-- Bir set iki yineleyici kullanılarak başlatılabilir :

int intarra[] ={1, 2, 3, 4, 5, 6} ;set<int> object(&intarr[0], &intarr[6]) ;

Burada dikkat edilecek nokta; set te yer alan bütün öğelerin birbirinden

Page 247: C++ 2 (Kitap - Selami KOÇLU)

farklı olma zorunluluğudur. Yani set inşa edilirken aynı değer defalarcadepo edilemez. Sadece değer ilk girildiği anda depolanır, daha sonra tekrargirilirse çaktırmadan gözardı edilir.Aynı map te olduğu gibi set içerdiği verinin kendine ait kopyasını alır.

-- set kopya yapıcı işlev kullanılarak başlatılabilir.

extern set<string> container ;set<string> object(container) ;

-- set kabı kaplar için geçerli bütün standart işleçleri destekler.

** set kabının üye işlevleri :

-- set::iterator set::begin( ) :Bu üye işlev set in ilk öğesini işaret eden yineleyiciyi döndürür. Eğer setboş ise o zaman set::end( ) döner.

-- set::clear( ) :Bu üye işlev set te yer alan bütün öğeleri siler.

-- unsigned set::count(key) :Bu üye işlev verilen anahtar set te yer alıyorsa o zaman 1 döndürür, aksitaktirde 0 döner.

-- bool set::empty( ) :Bu üye işlev set te öğe yoksa, true döndürür.

-- set::iterator set::end( ) :Bu üye işlev set in son öğesinden öteyi işaret eden yineleyiciyi döndürür.

-- pair<set::iterator set::iterator> set:.equal_range(key) :Bu üye işlev lower_bound( ) ve upper_bound( ) üye işlevlerine bağlıolan bir çift yineleyici döndürür. Aşağıda ayrıntılar açıklanacak.

-- ... set::erase( ) :Bu üye işlev set ten belli bir öğeyi veya bir bölüm öğeyi silmek içinkullanılır. bool erase(value) :

Page 248: C++ 2 (Kitap - Selami KOÇLU)

Değeri 'value' olarak belirtilen öğeyi set ten siler. 'value' silinirse, truedöner, aksi taktirde yani belirtilen 'value' yoksa false döner. void erase(pos) : pos yineleyicisinin işaret ettiği öğeyi siler. void erase(first, beyond) : [first, beyond) yineleyici aralığındaki bütün öğeleri siler.

-- set:.iterator set::find(value) :Bu üye işlev verilen değere sahip öğenin yineleyicisini döndürür. Eğer öğeyoksa end( ) döner.

-- ... set::insert( ) :Bu üye işlev set e öğeler yerleştirmek için kullanılır. Eğer öğe önceden sette bulunuyorsa, varolan öğe dokunulmadan bırakılır ve yerleştirilecek öğegözardı edilir. Dönüş değeri çağrılan insert( ) işlevinin çeşidine bağlıdır. pair<set::iterator, bool> insert(keyvalue) : Yeni bir set::value_type ı set e yerleştirir. pair<set::iterator, bool> ise dönüş değeridir. Dönen bool alanı true gösterirse, istenen değer set e yerleşmiş demektir. Eğer dönen bool alanı false gösterirse, o zaman verilen değer daha önceden set te bulunuyor demektir. Bu yüzden anılan yeni değer set e yerleştirilmez. Her iki durumda da set::iterator alanı set te belirtilen değere sahip veri öğesini işaret eder. set::iterator insert(pos, keyvalue) : Bu yolla bir set::value_type set e yerleştirilir. pos gözardı edilir. Yerleşen öğenin yineleyicisi döner. void insert(first, beyond) : [first, beyond) yineleyici alanınca işaret edilen set::value_type öğelerini set e yerleştirir.

-- set::iterator set::lower_bound(key) :Bu üye işlev en azından belirtilen key e eşit anahtarın ilk anahtar değeröğesini işaret eden yineleyiciyi döndürür. Böyle bir öğe bulunmuyorsa ozaman set::end( ) döner.

-- set::reverse_iterator set::rbegin( ) :Bu üye işlev set in son öğesini işaret eden yineleyiciyi döndürür.

-- set::reverse_iterator set::rend( ) :

Page 249: C++ 2 (Kitap - Selami KOÇLU)

Bu üye işlev set in ilk öğesinden öncesini işaret eden yineleyiciyi döner.

-- unsigned set::size( ) : Bu üye işlev set teki öğe sayısını döner.

-- void set::swap(argument) :Bu üye işlev aynı tiplerdeki iki set i (set lerden öteki argument tir) takaseder.

-- set::iterator set::upper_bound(key) :Bu üye işlev belirtilen key i aşan anahtara sahip, ilk anahtar değer öğesiniişaret eden yineleyiciyi döndürür. Böyle bir öğe yoksa, o zaman set::end( )döner.

'multiset' Kap sınıfı

multiset kabı da aynı set sınıfında olduğu gibi sıralı değerler topluluğuoluşturur. mutiset kabı kullanılmadan önce programda aşağıdakiönişlemleyici yönergesi mutlaka yazılmalıdır;

#include <set>

set ile multiset arasındaki temel fark; multiset aynı değerin birden fazlagirişine izin verir. set ise aynı değer için sadece tek girişe izin verir.set ile multiset aynı üye işlevler kümesini kullanırlar. Bu yüzden birönceki bölümde incelenen set üye işlevleri aynen multiset için degeçerlidir. Bununla beraber bazı üye işlevler multiset te kullanılırken dahafazla dikkat gerektirir. İşte bu üye işlevler aşağıda ayrıntıları ile verilmiştir;

-- unsigned set::count(value) :Bu üye işlev verilen değerin (value) multiset e kaç kez girildiğini gösterensayıyı döndürür.

-- ... multiset::erase( ) :Bu üye işlev set ten öğeleri silmekte kullanılır;

Page 250: C++ 2 (Kitap - Selami KOÇLU)

unsigned erase(value) : Verilen değere (value) sahip bütün öğeleri siler. Silinen öğe sayısı döner. void erase(pos) : pos yineleyicisi tarafından işaret edilen öğeyi siler. Aynı değere sahip öteki öğeler silinmez. void erase(first, beyond) : [first, beyond) yineleyici alanınca işaret edilen bütün öğeleri siler.

-- pair<multiset::iterator multiset::iterator> multiset::equal_range(value) :Bu üye işlev multiset::lower_bound( ) ile multiset::upper_bound( ) üyeişlevlerine bağlı olan bir çift yineleyici döndürür. İşlev, multiset te aynıdeğere sahip bütün öğeleri belirlemede basit bir usul sağlar.

-- multiset::iterator multiset::find(value) :Bu üye işlev belli bir değere sahip ilk öğeyi işaret eden yineleyiciyidöndürür. Eğer ilgili öğe yoksa o zaman multiset::end( ) döner. Yineleyicibelirlenen değere sahip bütün öğeleri saptamak için, değerini her seferindebir arttırarak, ya multiset:.end( ) ya da artık aynı değer rastlamayana kadarmultiset içinde gezinir.

-- ... multiset::insert( ) :Bu üye işlev normal olarak işlemlenir ve set kabında dönenpair<multiset::iterator, bool> değil, multiset::iterator döndürür. Dönenyineleyici yeni eklenen öğeyi işaret eder.

lower_bound( ) ve upper_bound( ) üye işlevleri hem set hem de multisette aynı şekilde davranmalarına rağmen, multiset teki işlemi özel dikkatgerektirir. Özellikle belirtmemiz gereken; multiset kabı ile, olmayananahtarlar için hem lower_bound( ) hem de upper_bound( ) her ikisi de,aynı sonucu üretir, her ikisi de verilen anahtarı aşan anahtara sahip ilköğeyi döndürür.Şimdi multiset ile birlikte kullanılan birçok üye işlevi barındıran bir örnekverelim;

//:B05:multst.cpp//uye kullanımları#include <iostream>

Page 251: C++ 2 (Kitap - Selami KOÇLU)

#include <set>using namespace std ;int main( ){ string sa[]={“alpha”, “echo”, “hotel”, “mike”, “romeo”}; multiset<string> object(&sa[0], &sa[5]) ; object.insert(“echo”) ; object.insert(“echo”) ; multiset<string>::iterator it=object.find(“echo”) ; for(; it!=object.end( ); ++it) cout<<*it<<” “ ; cout<<endl ; cout<<”Multiset::equal_range(\”echo\”)\n” ; pair<multiset<string>::iterator multiset<string>::iterator> itpair=object.equal_range(“ech”) ; if(itpair.first!=object.end( )) cout<<”lower_bound( ) points at”<<*itpair.first<<endl ; for(; itpair.first!=itpair.second; ++itpair.first) cout<<*itpair.first<<” “; cout<<endl<<object.count(“ech”)<<”occurences of 'ech' ”<<endl ; cout<<”Multiset::equal_range(\”echo\”)\n”; itpair=object.equal_range(“echo”) ; for(; itpair.first!=itpair.second; ++itpair.first) cout<<*itpair.first<<” “ ; cout<<endl<<object.count(“echo”)<<”occurences of 'echo' ”<<endl ; cout<<”Multiset::equal_range(\”echoo\”)\n” ; itpair=object.equal_range(“echoo”) ; for(; itpair.first!=itpair.second; ++itpair.first) cout<<*itpair.first<<” “; cout<<endl<<object.count(“echoo”)<<”occurences of 'echoo'“<<endl ;}///:~

//üretilen çıktı:echo echo echo hotel mike romeoMultiset::equal_range(“ech”)lower_bound( ) points at echo

0 occurences of 'ech'Multiset::equal_range(“echo”) echo echo echo

Page 252: C++ 2 (Kitap - Selami KOÇLU)

3 occurences of 'echo'Multiset::equal_range(“echoo”)

0 occurences of 'echoo'//

'stack' Kap sınıfı

stack sınıfı yığıt veri yapısını inşa eder. stack kapları kullanılmadan önceaşağıdaki önişlemleyici yönergesi programa yazılmış olmalıdır;

#include <stack>

Yığıt veri yapıları, ilk giren son çıkar (İGSÇ veya ingilizcesi FİLO veyaLİFO) yapılardır. Yığıtlarda veriler üst üste yığılır. İlk yerleştirilen veri enaltta kalır. En son yerleştirilen veri en üstte bulunur. Bu yüzden bir veriyiyığıttan almak için önce en üstteki (son giren veri) veri alınır, taki istenenveriye gelene kadar işlem sürer. Yani son giren ilk çıkar, veya ilk girenson çıkar. Yığıtlar verilerin geçici olarak saklandığı durumlar için çokyararlıdır. Örnek olarak, programlar işlevlerin yörel değişkenlerini yığıttatutarlar; değişken ömürleri işlevlerin etkinlik süresi tarafından belirlenir.Bu süre, program ömrü boyunca etkin olan global (veya static yörel)değişkenlere göre çok kısadır. Yığıttakiler geçici, global olanlar programboyunca yaşar. Başka bir örnek ise hesaplayıcılarda kullanılan tersdüzenleme uygulamasıdır. Burada işleçlerin işlenenleri yığıta girer, işleçlerişlenenlerini yığıt dışına alır, daha sonra işlem sonuçlarını tekrar yığıtayerleştirirler. Bu işlemler hesaplama bitene kadar sürer gider.Şimdi size yığıt kullanarak (1+2)*3 işleminin yapılışını görelim. Tersdüzenlemeye göre açıklama; 1 2 + 3 * olur.

1 2 + 3* 1 2 + 3 * 1 2 + 3 * 1 2 + 3 * 1 2 + 3 *

1 2 3 3 9 1 3 (1) (2) (3) (4) (5)Şekil 5.5 (1+2)*3 işleminin yığıtta işlemlenmesi

Ayraç içindeki sayıların üst kısımlarında yer alan sayılar hesaplamanın her

Page 253: C++ 2 (Kitap - Selami KOÇLU)

aşamasında yığıtın içini göstermektedir. Burada işlemin ters düzenlemesiolan 1 2 + 3 * deki her simge yığıtta sıra ile işlemlenir. Önce yığıta ilksimge 1 alınır, daha sonra onun üstüne yığıta ikinci simge 2 yerleştirilir.Üçüncü simge olan + yığıta önceden yerleştirilmiş olan 1 ve 2 arasındatoplamayı gerçekler. Burada 1 ve 2 yığıttan çekip alınır yani yığıtboşaltılır, toplama işlemi yapılır sonucu boşalmış olan yığıta konur. Yığıtabu aşamada 3 (1+2=3) yerleşmiş olur. Daha sonra toplam 3 aşağıda, yenideğer 3 üstte olmak üzere, yığıt yeniden düzenlenir. Bu aşama 4. aşamadır.Son aşamada (5. aşama) yığıttaki 3 ler arasında * işlemi gerçeklenir. Buaşamada yine yığıttan 3 ler alınır yani yığıt boşaltılır çarpma işlemigerçeklenir ve çarpım sonucu yığıta yerleştirilir. Görüldüğü gibi işleçleryığıt içindeki işlenenleri değiştirmektedir. Yığıta yerleştirilmekte, yığıttanalınıp işleme tabi tutulmakta, sonuç yığıta yazılmakta ve bunlar simgelerbitene kadar sürmektedir. Anlayacağınız gibi, bir yığıtın tek noktasından(en üst noktadan) unsurlar girer ve çıkar. Buradaki üst öğe, yığıtın hemengörülebilen öğesidir. Ona doğrudan erişilebilir ve değiştirilebilir.Şimdi yığıtın bu çalışma mekanizmasını aklınızdan çıkarmadan, stack kapsınıfı ile neler yapılabileceğini görelim. Aşağıdaki yapıcı işlevler, işleçler,ve üye işlevler stack sınıfı ile kullanılırlar;

** Yapıcı işlevler :

-- Bir stack boş olarak inşa edilebilir;

stack<string> object ;

Buradaki string yerine herhangi bir yerleşik (float, int, double v.s.) veyakullanıcı tanımlı tip kullanılabilir. Object ise sizin adını koyacağınızherhangi bir nesne olabilir.

-- Bir stack kopya yapıcı işlev kullanılarak başlatılabilir;

extern stack<string> container ;stack<string> object(container) ;

** İşleçlerin kaplar için sadece temel olanları stack tarafından desteklenir.

** Aşağıdaki üye işlevler stack sınıfında bulunurlar;

-- bool stack::empty( ) :

Page 254: C++ 2 (Kitap - Selami KOÇLU)

Bu üye işlev stack te öğe yoksa true döndürür.

-- void stack::push(value) :Bu üye işlev value değerini yığıtın en üstüne yerleştirir, öbür öğeleri görüşalanından çıkarır.

-- void stack::pop( ) :Bu üye işlev yığıtın en üstünde yer alan öğeyi oradan kaldırır. Alınan öğebu üye işlevce geri döndürülmez. Boş bir yığıtta pop( ) kullanıldığı zamanbir şey olmaz.

-- unsigned stack::size( ) :Bu üye işlev stack teki öğe sayısını döndürür.

-- Type &stack::top( ) :Bu üye işlev stack te en üst öğeye (görüş alanındaki öğe) dayanç döndürür.Bunun kullanımı ancak eğer stack boş değilse programcınınsorumluluğundadır.Burada bir konuyu belirtelim; yığıt ve stack aynı şeydir. Yığıt türkçekarşılığıdır. Program yazarken stack kullanılır.stack, yineleyicileri ve dizin işlecini ([]) desteklemez, yani bunlar stack ilekullanılamaz. Erişilebilecek olan tek öğe stack in en üstünde yer alanöğedir. Bir yığıtı tamamen boşaltmak için : *- Artarda en üstte yer alan öğeleri kaldırmak *- Aynı veri tipinde boş bir stack i kendisine atamak *- Yıkıcı işlevini çağırmakyolları kullanılabilir.

'hash_map' ve benzeri başka kaplar map daha önce anlatıldığı üzere sıralı veri yapısıdır. map te anahtarlarınsıralaması anahtar veri tipinin operator <( ) işleci kullanılarak sağlanır.Genel olarak bu bir öğenin saklanması veya oradan alınması için en hızlıyol değildir. Aslında map te yapılan sıralama, sıralı anahtar listelemesinindüzensiz bir listelemeden daha çekici olmasındandır. Sadece kazanç odur.Verileri daha hızlı depolayıp oradan daha hızlı almanın yöntemi çorba(hashing) kullanmaktır. Şimdi hashing yerine neden çorba kelimesini

Page 255: C++ 2 (Kitap - Selami KOÇLU)

kullandığımı açıklayayım; çorba reçetesi belli fakat içindekilerin dışardanbir bakışta saptanamadığı bir yemek türüdür. İçindekileri tam olarakçorbayı yapan bilir. Yani burada da çorba işlevini oluşturan. Ama böreğebakarsanız hamuru nerede ıspanağı nerede bir bakışta görülür yani herşeybelli bir sıralamadadır.Çorba, anahtardan bir sayı elde etmek için çorba işlevini kullanır; bu sayıanahtarların depolandığı tabloda derhal dizin olarak kullanılır. Biranahtarın geri alınması ise, verilen anahtarın çorba değerinin hesaplanmasıve bunun tabloda dizin yerindeki değerine bakılması ile olur; anahtarvarsa, tabloda depolanır, değeri dönebilir. Anahtar yoksa, anahtardepolanmaz.Hesaplanmış olan dizin yeri başka bir öğe tarafından kaplanmışsa,çatışmalar ortaya çıkar. Böyle durumlar için soyut kapların bir çok çözümübulunmaktadır. Ama konu bu bölümün sınırlarını aşmaktadır.Gnu g++ derleyicisi hash_(multi)map ve hash_(multi)set kaplarınıdesteklemektedir. Google a girip 'C++ hash function' yazarsanız konu ileilgili çok sayıda kaynak bulabilirsiniz. Şimdi çorba kaplarını bir sorunu çözmede kullanarak konuyu açıklamayaçalışalım. Anahtar ve değerleri, öğelere hep aynı sürede erişilecek şekildedepolayalım ve depolanan öğeler belli bir sıralamada olmasın.Çözüm: ilişkili çorba kaplarından biri kullanılır: bu ya hash_map ya dahash_set olur. Tekrar belirtelim bu kaplar standart olmayıp kullanımlarımaalesef derleyiciden derleyiciye değişebilir. stlport sitesini kaynak siteolarak kullanabilirsiniz.

//:B05:corba.cpp//hash_set kullanımı#include <iostream>#include <string>#include <hash_set>using namespace std ;int main( ){ hash_set<std::string> hsString ; string s=”bravo” ; hsString.insert(s) ; s=”selami” ; hsString.insert(s) ; s=”koclu” ; hsString.insert(s) ; for(hash_set<string>::const_iterator p=hsString.begin( ) ;

Page 256: C++ 2 (Kitap - Selami KOÇLU)

p!=hsString.end( ) ; ++p) cout<<*p<<endl ; //sıralı olması garantili değildir.}///:~

Programlama dillerinde çorba kapları oldukça bilinen veri yapılarıdır. Amamaalesef C++ dili standartları içinde henüz bulunmamaktadır. Çorba kaplarının (C++ yazılı metinelerinde çorba ilişkili kaplar olarakgeçer) ortalama temel özelliği; öğelerin belli bir zaman için yerleşimi, bellibir sürede silme, belli bir sürede yerleştirilebilmesidir. En hoşa gitmeyenyanı ise doğrusal karmaşadır. Bütün bu sabit süreli işlemlere rağmen çorbakaplarındaki öğeler map te olduğu gibi belli bir sıralama da değillerdir.Ondan karışık gibi görünürler. (neden çorba dedik ))):)Burada anlattığımız çorba kapları (hash containers) stlport internetsitesinde ele alınan, yaygın olarak kullanılan kütüphanelerdir. Sizinkullanmayı düşündüğünüz başka kütüphaneler varsa onlarda büyükihtimalle benzer özellikler taşıyacaktır. Yukarıdaki örnekten görüleceği üzere, çorba kaplarının kullanımı, öbürkapların bildirimi ve öğe yerleştirilmesi gibi oldukça kolaydır.

hash_set<string> hsString ; //string lerin hash_set istring s=”bravo” ;hsString.insert(s) ; //s nin kopyasını yerleştir.

hash_map kullanımı da yukarıdaki hash_set kullanımına benzer, ancak ekolarak kullanılacak olan hem anahtar hem de veri tipleri belirtilmelidir.

hash_map<string, string> hmString ; //string in string e map istring key=”key” ;string val=”val” ;hmString[key]=val ;

Yukarıda anlatılanlar çorba kaplarının temel kullanımlarıdır. Çorba işlevikalıp değişkenleri kullanılarak belirlenir, bu sayede anahtar aynılığı içinsınama ve nesnenin belleğe yerleşimi gerçeklenir. Birazdan ayrıntılarıvereceğiz.Kütüphanelerin çoğunda 4 çeşit çorba kabı bulunur; (ve bunlar standartkütüphanedeki öteki ilişkili kapları temsil ederler) hash_map,hash_multimap, hash_set ve hash_multiset. Bütün çorba kapları çorbatablosu kullanılarak gerçeklenirler. Bir veri yapısı olan çorba tablosu

Page 257: C++ 2 (Kitap - Selami KOÇLU)

öğelere sabit sürede erişmeye izin verir. Çorba işlevi kullanılarak depodaki istenen nesneye sıçrayarak erişilir, yani bütün sokakları dolaşmadandoğrudan adrese paraşütle inilir. Paraşüt çorba işlevidir. Benzetmeleranlatımı kolaylaştırmak içindir. Sizin ferasetinize güveniyoruz. hash_mapile hash_set arasındaki temel fark; verilerin çorba tablosunda nasıldepolandığı ile ilgilidir. Stlport ta gösterilen çorba tablosu tabanlı kapların bildirimleri aşağıdaverilmiştir;

hash_map<Key, //Key (anahtar) in tipi Value, // Key ile ilişkili Value (değer) nin tipi HashFun=hash<Key>, //kullanılan çorba (hash) işlevi EqualKey=equal_to<Key>, //anahtar aynılığı için sınama //işlevi Alloc=alloc> //bellek yerleştirici

hash_set<Key, //Key (anahtar) tipi HashFun=hash<Key>, //kullanılan çorba (hash) işlevi EqualKey=equal_to<Key>, //anahtar aynılığı için sınama //işlevi Alloc=alloc> //bellek yerleştirici

Bir hash_map değerleri pair<const Key, ,Data> nesneleri olarakdepolayan çorba tablosudur. Bunun anlamı; aşağıda çorba tablolarınıbelirtirken tablo daki öğeler, anahtar/değer (key/value) pair lerdir (yaniçiftlerdir) demektir. hash_map hiçbir zaman anahtar ve değerleri ayrı ayrıdepolamaz. (aslında map lerin hiçbiri ayrı ayrı depolama yapmaz). Birhash_set ise anahtarı (key) değer (value) tipinde depolar.HashFun kalıp değişkeni bir işlevdir, Key tipindeki nesneleri size_tolarak saklanan değere çevirir. EqualKey kalıp değişkeni de bir işlevdir veiki değişken barındırır bunlar aynı ise true üretir. Gerek hash_map tegerekse hash_set te hiç bir zaman iki anahtar aynı olamaz. Amahash_multiset ve hash_multimap te aynı (özdeş=eşit) çok sayıda anahtarbulunabilir. Ve Alloc bütün kaplarda olduğu gibi bir bellek yerleştiricisidir(allocator).Bir çorba tablosunda iki bölüm bulunur. Bir tane göreceli olarak büyükvector bulunur. Buradaki her dizin bir bukettir. Her buket ise kısa tekliveya çiftli ilişimli listede ki ilk düğümün göstergecidir. (stlport ta tekli) Bu

Page 258: C++ 2 (Kitap - Selami KOÇLU)

listeler asıl verilerin depolandığı yerdir. Çorba kabında bulunan buketsayısını bucket_count( ) üye işlevi ile elde edebilirsiniz.

Şekil 5.5 Çorba tablosu (hash table)

Bir öğe yerleştirdiğiniz zaman kap önce öğenin ait olduğu buketibelirlemek zorundadır. Kap için, üzerinde bulunulan anahtarın çorba işleviçağrılır ve buket sayısı ile modülü hesaplanır. Bu buket vector undaki birdizini verir. Çorba ile ilgili hiç bilginiz yoksa, hemen belirtelim anlaşılması kolay birmevzudur. Verilen bir değer (char dizisi diyelim) ile, tek değişkeni olan birçorba işlevi size_t tipinde çorba değeri döndürür (yani bir sayı). İdeal olanıgenellikle tek olan (zorunluluk değil) çorba değerlerini üreten çorbaişlevine sahip olmaktır. Bu işlev matematik anlamda bire bir işlev değildir.Birden fazla string aynı çorba değerine karşılık (map) olabilir.Bir çorba değerinin nasıl göründüğünü aşağıdaki örnekten görebiliriz;

std::hash<const char*> hasFun ;std::cout<<”\”Hashomatic\” hashes to” <<hasFun(“Hashomatic”)<<'\n' ;

Görünen aşağıdaki gibi olur;

“Hashomatic” hashes to 189555649

bucket_count( ) q

Page 259: C++ 2 (Kitap - Selami KOÇLU)

Şimdi kısım nesnelerine giriş adlarını karşılık getiren (yani map eden)örnek verelim;

//:B05:harita.cpp//session yöneticisi#include <iostream>#include <string>#include <hash_map>using namespace std ;class Session{/..../ };//okumayı kolaylaştırmak için typedef kullantypedef hash_map<string, Session*> SessionHashMap ;class SessionManager{public: SessionManager( ) :sessionMap_(500){ } //çorba tablosunu başlat ~SessionManager( ){ for(SessionHashMap::iterator p=sessionMap_.begin( ); p!=sessionMap_.end( ); ++p) delete (*p).second ; //Session nesnlerini sil } Session* addSession(const string &login){ Session* p=NULL ; if(!(p=getSession(login))){ p=new Session( ) ; sessionMap_[login]=p ; //operator[] ile yeni session ata } return (p) ; } Session* getSession(const string& login){ return (sessionMap_[login]) ; } ///...private : SessionHashMap sessionMap_;}; ///:~

Her anahtar (key) tek buket karşılık gelir (map eder). Bukette birden fazlaanahtar olabilir. Bir buket daha önce söylediğimiz gibi tek veya çift ilişimlilistedir. Daha fazla bilgi almak isterseniz, google girip 'c++ hash function'

Page 260: C++ 2 (Kitap - Selami KOÇLU)

yazın.

Yineleyiciler (Iterators)

Yineleyiciler göstergeç gibi davranan nesnelerdir, ve özellikleri;

1- Bir iter yineleyicisinde *iter, yineleyicinin gösterdiği nesnedir. (başkadeyişle iter-> yineleyicinin işaret ettiği nesne üyelerine erişmektekullanılabilir)

2- ++iter ve iter++ yineleyiciyi bir sonraki öğeye ilerletir. Yineleyiciyi birsonraki öğeye ilerletmek, yerine göre uygulanır; çok sayıda kap sınıfındareverse_iterator tipi bulunur, iter++ işlemi ise aslında dizideki bir öncekiöğeye erişimi sağlar.

3- Göstergeç aritmetiği, öğeleri bellekte ardarda bulunan kaplardakullanılır. Bunlar vector ve deque dir. Anılan kaplarda iter+2, iter inişaret ettiği yerin ikincisidir.

4- Bir yineleyici karşılaştırmalı olarak sadece 0-göstergeçe e göretanımlanabilir. Aşağıdaki basit örnekte bu durum görülebilir;

//:B05:iter.cpp//tanımlama#include <iostream>#include <vector>using namespace std ;int main( ){ vector<int>::iterator vi; cout>&*vi<<endl ; //0 yazar}///:~

SKK kap sınıfları genellikle begin( ) ve end( ) üye işlevlerini kullanarakyineleyicileri (örnek; iterator tipi) üreten üyeleri tanımlar. Tabii tersyineleyicilerde (reverse_iterator tipi) kullanılan işlevler rbegin( ) ve

Page 261: C++ 2 (Kitap - Selami KOÇLU)

rend( ) dir. Standart uygulamada yineleyici alanına sol tarafı dahil edilirsağ tarafı ise bir sonraki noktayı gösterir. Yani [solyan, sağyan) biçimindegösterilir. Buradaki solyan yineleyicinin ilk değeridir, sağyan iseyineleyicinin işaret ettiği son değerin bir sonrasıdır. Yineleyici için solyanilk değer, sağyan-1 ise son değerdir. sağyan==solyan ise, yineleyici alanıboş demektir. Boş kaplarda begin ve end yineleyicileri birbirine eşittir. Aşağıdaki örnek; [begin( ), end( )) ve [rbegin( ), rend( )) yineleyici alanlarıkullanılarak string vector ünün bütün öğelerinin cout nesnesine yazdırıldığıdurumu göstermektedir; Her iki yineleyici alanı için for döngüleri özdeştir.

//:B05: strvect.cpp//string vector ü#include <iostream>#include <vector>#include <string>using namespace std ;int main(int argc, char** argv){ vector<string> args(argv, argv+argc); for(vector<string>::iterator iter=args.begin( ); iter!=args.end( ); ++iter) cout<<*iter<<” “ ; cout<<endl ; for(vector<string>::reverse_iterator iter=args.rbegin( ); iter!=args.rend( ); ++iter) cout<<*iter<<” “ ; cout<<endl ; return 0 ;}///:~

Bunlardan başka SKK (standart kalıp kütüphanesi) sabit kaplardaki bir diziöğeyi ziyaret etmesi için const_iterator tipler de tanımlar. Önceki örnekteyer alan vector öğeleri değiştirilebilirken, bir sonraki örnekte vectoröğelerinin değiştirilemez olması için const_iterator lar gerekir.

//:B05:sabit_yineleyici.cpp //const_iterator kullanımı#include <iostream>

Page 262: C++ 2 (Kitap - Selami KOÇLU)

#include <string>#include<vector>using namespace std ;int main(int argc, char** argv){ vector<string> const args(argv, argv+argc); for(vector<string>::const_iterator iter=args.begin( ); iter!=args.end( ); ++iter) cout<<*iter<<” “ ; cout<<endl ; for(vector<string>::const_reverse_iterator iter=args.rbegin( ); iter!=args.rend( ); ++iter) cout<<*iter<<” “; cout<<endl ; return 0;}///:~

Örneklerden, yineleyiciler yerine bildiğimiz basit göstergeçlerin dekullanılabileceğini anlayabilirsiniz. vector<string> args(argv, argv+argc) başlatması bir çift, göstergeçtabanlı yineleyicisi olan args vector sağlar; argv işaret ettiği ilk öğeyi sargile başlatır, argv+argc kullanılacak olan son öğeden ötesini işaret eder,argv++ bir sonraki string e gider. Bu göstergeçlerin genel özelliğidir.Bundan dolayı yineleyicilerin beklendiği yerlerde kullanılabilmektedirler.SKK 5 tip yineleyici tanımlamaktadır. Bunlar kalıp algoritmalarındakullanılmaktadırlar ve kendinize uygun belli bir tipte yineleyicioluşturabilmek için özellikleri iyi bilmek çok önemlidir. Yineleyiciler aşağıdakileri mutlaka tanımlamalıdır; operator==( ) ; iki yineleyiciyi özdeşlik için sınar.operator++( ) ; yineleyiciyi bir arttırma, önartım işleci.operator*( ) ; yineleyicinin işaret ettiği öğeye erişim.

Aşağıdaki yineleyiciler ilerleyen bölümde anlatılan algoritmalarıtanımlarken kullanılır; ** InputIterators (girdi yineleyicileri)

Girdi yineleyicileri bir kaptan okuyabilir. Dayançtan kurtarma işlecininaçıklamalarda sağyan (rvalue) olarak çalışması güvenceye alınmıştır.

Page 263: C++ 2 (Kitap - Selami KOÇLU)

Burada anlatılacak olan kalıp algoritmalarla InputIterator yerine,Forward- , Bidirectional- , veya RandomAccessIterator- yineleyicileri dekullanılabilir.

** OutputIterators (çıktı yineleyicileri)

Çıktı yineleyicileri kaba yazarken kullanılabilir. Dayançtan kurtarmaişlecinin açıklamalarda solyan (lvalue) olarak çalışması güvenceyealınmıştır. Sağyan (rvalue) olarak çalışması gerekli değildir. OutputIteratoryerine Forward-, Bidirectional-, veya RandomAccessIterator kullanılabilir.

**ForwardIterators (öndeki yineleyiciler)

ForwardIterartor ler, InputIterator ler ile OutputIterator leri biraraya getirir.Bunlar okumak/ veya yazmak için kaplarda bir yönde dolaşmayı sağlar.ForwardIterator yerine Bidirectional-, veya RandomAccessIterator lerkullanılabilir.

** BidirectionalIterators (çift yönlü yineleyiciler)

BidirectionalIterator ler kapları okumak veya yazmak için iki doğrultudadolaşmayı sağlar. BidirectionalIterator ler yerine RandomAccessIterator lerkullanılabilir. Bir list veya deque yi dolaşmak için BidirectionalIterator lerçok yararlı olabilir.

** RandomAccessIterators (rastgele erişimli yineleyiciler)

Kap öğelerine rastgele erişimi sağlar. sort( ) gibi bir algoritmaRandomAccessIterator a gerek duyar. Bundan dolayıRandomAccessIterator ler map ve list lerle kullanılmazlar. Örneklere bakıldığında RandomAccessIterator, yineleyiciler konusununnasıl işlediğini gösterir. Kalıp algoritma tarafından kullanılacak olanyineleyici aranır, daha sonra veri yapısının bu yineleyiciyi destekleyipdesteklemediği görülür. Eğer desteklemiyorsa algoritma veri yapısı ilekullanılamaz.

Yerleşim Yineleyicileri Kalıp algoritması çoğu kez sonuçlarını içinde depoladığı bir hedef kap

Page 264: C++ 2 (Kitap - Selami KOÇLU)

kullanır. Örneğin; copy( ) algoritması 3 değişkene sahiptir, ilk iki değişkenziyaret edilen öğe alanını, üçüncü değişkende kopya işlem sonuçlarınındepolanacağı ilk yeri tanımlar. copy( ) algoritması ile kopyalanacak olanöğe sayısını daha önceden belirlemek mümkündür. Bu sayı genelliklegöstergeç aritmetiği kullanılarak saptanabilir.Bununla birlikte göstergeçaritmetiğinin kullanılamayacağı bazı durumlar da vardır. Benzer şekildenetice öğelerin sayısı başlangıç alanındakilerden de farklı olabilir.unique_copy( ) algoritması dikkate değer bir duruma sahiptir; burada varışkabına kopyalanacak olan öğe sayısı normalde daha önceden bilinemez.Bunlara benzer durumlarda gerektiğinde varış kaplarında öğelerioluşturmak için bir inserter uyarlayıcı işlevi (adaptor function) kullanılır.Üç çeşit inserter( ) uyarlayıcı işlevi bulunur;

back_inserter( ) :Kap sonunan yeni öğe eklemek için kabın push_back( ) üyesini çağırır.Örneğin; source, yani kaynağın bütün öğelerini ters sıralamada varışınarkasına kopyalamak için :

copy(source.rbegin( ), source.rend( ), back_inserter(destination));

front_inserter( ) :Kabın başlangıcına yeni öğe eklemek için kabın push_front( ) üyesiniçağırır. Örneğin; varış kabının ön tarafına source yani kaynağın bütünöğelerini kopyalamak için (o suretle öğelerin düzenini terslemek için) :

copy(source.begin( ), source.end( ), front_inserter(destination));

inserter( ) :Kabın belirlenmiş bir noktasından başlamak üzere yeni öğe eklemek içinkabın insert( ) işlevi çağrılır. Örneğin; varışın baş kısmından başlayarak,ayrıca varolan önceki öğeleri yeni eklenenlerin ötesine kaydırarak, sourceyani kaynağın bütün öğelerini kopyalamak için;

copy(source.begin( ), source.end( ), inserter(destination, destination.begin( )));

back_inserter( ) yineleyicisine bakacak olursak yineleyicinin push_back( ) üyesi bulunan kabın adını bekeldiğini görürürüz. Bu üyeinserter ün operator( )( ) üyesi tarafından çağrılır. Soyut kapların dışındaki

Page 265: C++ 2 (Kitap - Selami KOÇLU)

sınıf push_back( ) kabını desteklediği zaman, eğer sınıf aşağıdakinitanımlarsa, nesneleri back_inserter( ) in değişkenleri olarak kullanılabilir;

typedef DataType const& const_reference ;

DataType const&, sınıfın üye işlevi push_back( ) değişkeninin tipidir.Örneğin; IntStore bir sınıf tanımlamaktadır, nesneleri back_inserter( )yineleyicisinin değişkenleri olarak kullanılabilir;

//:B05:Depolama.cpp//IntStore kullanmak#include <iterator>#include <algorithm> using namespace std ;class Y{ public: typedef int const& const_reference; void push_back(int const&){ }};int main( ){ int arr[ ]={1} ; Y y ; copy(arr, arr+1, back_inserter(y));}///:~

'istream' nesnelerinin yineleyicileri 'istream' nesneleri için istream_iterator<Type>( ), yineleyici (pair)tanımlamak için kullanılabilir. istream_iterator<Type>( )yineleyicilerinin genel biçimi;

istream_iterator<Type> identifier(istream& inStream) ;

Burada Type, istream akışından okunan verilerin tipidir. Type, istreamnesneleri için tanımlanan operator<<( ) e uygun herhangi bir tip olabilir.Varsayılan yapıcı işlev akış sonuna denk gelen yineleyici çiftinin bitişyerini tanımlar. Örnek;

Page 266: C++ 2 (Kitap - Selami KOÇLU)

istream_iterator<string> endOfStream ;

Bir back_inserter( ) ve bir grup istream_iterator< >( ) uyarlayıcıkullanarak bütün string leri cin nesnesinden okuyabiliriz;

//:B05:oku_string.cpp//string leri cin den okumak#include <iostream>#include <algorithm>#include <string>#include <iterator>#include <vector>using namespace std ;int main( ){ vector<string> vs ; copy(istream_iterator<string>(cin), istream_iterator<string>( ), back_inserter(vs)); for(vector<string>::iterator from=vs.begin( ); from!=vs.end() ); ++from) cout<<*from<<” “ ; cout<<endl ; return 0;}///:~

Yukarıdaki örnekte istream_iterator uyarlayıcılarının, genel sürümününkullanımı gösterildi. Burada özellikle genel yapıcı işlev kullanımına dikkatedilmelidir. Ayrıca istream_iterator<string>( ) yerine aşağıdaki yapı dakullanılabilmektedir;

istream_iterator<string> eos ;copy(istream_iterator<string>(cin), eos, back_inverter(vs)) ;

istream_iterator kullanılmadan önce, aşağıdaki önişlemleyici yönergesimutlaka programa yazılmalıdır;

#include <iterator>

iostream yazıldığında bu da ima edilmiş olur.

Page 267: C++ 2 (Kitap - Selami KOÇLU)

'istreambuf' nesnelerinin yineleyicileri Girdi yineleyicileri (Input Iterators) streambuf nesneleri için de kullanılır.istreambuf yineleyicileri kullanılmadan önce, mutlaka aşağıdakiönişlemleyici yönergesi programa yazılmış olmalıdır;

#include <iterator>

istreambuf_iterator ları girdi işlemlerini desteklemek için streambufnesnelerini okumaya yardımcı olur. istream_iterator de kullanılanstandart işlemler istreambuf_iterator içinde geçerlidir. Üç tane yapıcıişlev bulunur;

** istreambuf_iterator<Type>( ) :Bu yapıcı işlev streambuf tan Type tipinde değerler alde edilirken akışyineleyicisinin sonunu temsil eder.

** istreambuf_iterator<Type>(istream) : Bu yapıcı işlev istream nesnesinin streambuf erişen ve yapıcı işlevindeğişkeni olarak kullanılan istreambuf_iterator unu inşa eder.

** istreambuf_iterator<Type>(streambuf*) :Bu yapıcı işlev adresi yapıcı işlevin değişkeni olarak kullanılan streambufa erişen istreambuf_iterator unu inşa eder.

Bir sonraki bölümde hem istreambuf_iterator hem de ostreambuf_iteratorün birlikte kullanıldığı örnek verilecektir.

'ostream' nesnelerinin yineleyicileri ostream_iterator<Type>( ), ostream nesnesi için varış yineleyicisitanımlamakta kullanılır. ostream_iterator<Type>( ) ın genel biçimleriaşağıdadır;

ostream_iterator<Type> identifier(ostream& outStream), //ve:ostream_iterator<Type> identifier(ostream& outStream,

Page 268: C++ 2 (Kitap - Selami KOÇLU)

char const* delim) ;

Type, ostream akışına yazılan verilerin tipidir. Type ostream nesneleri ilebirlikte operator<<( ) ün tanımlandığı herhangi bir tip olabilir.Aşağıdaki örnekte istream_iterator ve ostream_iterator bir dosyadanbilgilerin başka bir dosyaya kopyalanmasında kullanıldı. Burada kideyimde bir incelik bulunmaktadır; in.unsetf(ios::skipws); buios::skipws bayrağını siler. Bunun sonucunda operator>>( ) ın varsayılandavranışı boşluğa atlamak için değiştirilir. Boşluk karakterleri işleçtarafından döndürülür, ve dosya sınırsız bir biçimde kopyalanır, işteprogram;

//:B05:Dosya_kopya.cpp//Dosya kopyalama #include <algorithm>#include <fstream>#include <iomanip>using namespace std ;int main(int argc, char** argv){ ifstream in(argv[1]) ; ofstream out(argv[2]) ; in.unsetf(ios::skipws) ; copy(istream_iterator<char>(in), istream_iterator<char>( ), ostream_iterator<char>(out)) ; return 0;}///:~

ostream_iterator lar kullanılmadan önce aşağıdaki önişlemleyiciyönergesi mutlaka programa yazılmış olmalıdır;

#include <iterator>

'ostreambuf' nesnelerinin yineleyicileri Bir ostreambuf_iterator ünden önce aşağıdaki önişlemleyici yönergesimutlaka programa yazılmalıdır;

#include <iterator>

Page 269: C++ 2 (Kitap - Selami KOÇLU)

ostreambuf_iterator, çıktı işlemlerini destekler ve streambuf nesnelerineyazar. ostream_iterator nesneleri için geçerli olan bütün işlemlerostreambuf_iterator nesneleri için de geçerlidir. İki tane yapıcı işlevivardır;

** ostreambuf_iterator<Type>(ostream) :Bu yapıcı işlev, yapıcı işlevin değişkeni olarak kullanılan ostreamnesnesinin streambuf una erişen, ostreambuf_iterator ünü inşa eder veType tipinde değerler ekler.

** ostreambuf_iterator<Type>(streambuf*) :Bu yapıcı işlev adresi yapıcı işlevin değişkeni olarak kullanılan streambufa erişen ostreambuf_iterator ü inşa eder.

Şimdi akışın kopyalandığı başka bir örnek verelim;

//:B05:yeni_kopya.cpp//kopyalama#include <iostream>#include <algorithm>#include <iterator>using namespace std ;int main( ){ istreambuf_iterator<char> in(cin.rdbuf( )) ; istreambuf_iterator<char> eof ; ostreambuf_iterator<char> out(cout.rdbuf( )) ; copy(in, eof, out) ; return 0;}///:~

Algoritmalar

Page 270: C++ 2 (Kitap - Selami KOÇLU)

Bu kısımda alfabetik sıralama ile SKK da yer alan algoritmaları göreceğiz.Her algoritma için sırası ile şu bilgiler verilecektir;

1- Gereken başlık dosyası.2- İşlev öntipi.3- Kısa tanım.4- Basit bir örnek.

Algoritma öntiplerinde temel veri tipi için Type kullanılmıştır. Ayrıca bellibir yineleyici tipi gerektiğinde temel tipler gibi, bu da kullanılmıştır.Heralgoritma çalışma alanını tanımlamak için [first, last) yineleyici alanınıbilmelidir. Yineleyiciler nesneleri veya değerleri işaret eder. Bir yineleyicibir Type nesne veya değerini işaret ettiği zaman, algoritmalar tarafındankullanılan işlev nesneleri genellikle Type const& nesneleri veya değerlerialır: işlev nesneleri bundan dolayı değişken olarak aldıkları nesnelerideğiştiremezler. Tabii algoritmaların üzerinde işlem yaptığı nesnelerdedeğişiklik yaparak algoritmaları da değiştirmenin doğru olduğunusöyleyemeyiz. Algoritmaları kullanıldıklara yerlere göre sınıflandırarak,açıklamalarımızı sürdüreceğiz.

** Karşılaştırıcılar: öğeleri (alanları ile beraber) karşılaştırır;Gereken: #include <algorithm>equal( ), includes( ), lexicographical_compare( ), max( ), min( ), mismatch( ).

** Kopyalayıcılar: kopya işlemlerini gerçekleştirirler;Gereken: #include <algorithm>copy( ), copy_backward( ), partial_sort_copy( ), remove_copy( ),remove_copy_of( ), replace_copy( ), replace_copy_if( ), unique_copy( ).

** Sayıcılar: sayma işlemlerini gerçekleştirirler;Gereken: #include <algorithm>count( ), count_if( ).

** Küme işleçleri: max-küme değişimleri;Gereken: #include <algorithm>make_heap( ), pop_heap( ), push_heap( ), sort_heap( ).

Page 271: C++ 2 (Kitap - Selami KOÇLU)

** Başlatıcılar: veriyi başlatma;Gereken: #include <algorithm>fill( ), fill_n( ), generate( ), generate_n( ).

** İşleçler: belli bir düzende aritmetik işlemleri gerçekleştirme;Gereken: #include <numeric> adjacent_difference( ), accumulate( ), inner_product( ), partial_sum( ).

** Arayıcılar: arama (ve bulma) işlemlerini gerçekleştirme;Gereken: #include <algorithm>adjacent_find( ), binary_search( ), equal_range( ), find( ), find_if( ),find_end( ), find_first_of( ), lower_bound( ), max_element( ), min_element( ), search( ), search_n( ), set_difference( ),set_intersection( ), set_symmetric_difference( ), set_union( ),upper_bound( ).

** Karmacılar: yeniden düzenleme işlemlerini gerçekleştirme;Gereken: #include <algorithm> inplace_merge( ), iter_swap( ), merge( ), next_permutation( ), nth_element( ), partial_sort( ), partial_sort_copy( ), partition( ),prev_permutation( ), random_shuffle( ), remove( ), remove_copy( ),remove_if( ), remove_copy_if( ), reverse( ), reverse_copy( ), rotate( ),rotate_copy( ), sort( ), stable_partition( ), stable_sort( ), swap( ),unique( ).

** Ziyaretçiler: bir alandaki öğeleri ziyaret edenler:Gereken: #include <algorithm>for_each( ), replace( ), replace_copy( ), replace_if( ), replace_copy_if( ), transform( ), unique_copy( ).

accumulate( )

Başlık Dosyası: #include <numeric>

İşlev öntipleri:-- Type accumulate(InputIterator first, InputIterator last, Typeinit);

Page 272: C++ 2 (Kitap - Selami KOÇLU)

-- Type accumulate(InputIterator first, InputIterator last, Type init, BinaryOperation op);

Açıklama:İlk öntipte: yineleyici alanınca belirtilen bütün öğelere ve başlangıç değeriolan init e operator+( ) uygulanır. Netice değer döner.İkinci öntipte: yineleyici alanınca belirtilen bütün öğelere ve başlangıçdeğeri olan init e binary işleç olan op( ) uygulanır. Netice değer döner.Örnek:

//:B05:accu.cpp//accumulate kullanımı#include <numeric>#include <iostream>#include <vector>using namespace std ;int main( ){ int ia[]={1, 2, 3, 4}; vector<int> iv(ia, ia+4); cout<<”degerlerin toplami”<<accumulate(iv.begin( ), iv.end( ), int( ))<< <<endl<<”degerlerin carpimi”<<accumulate(iv.begin( ), iv.end( ), int(1), multiplies<int>( ))<<endl; return 0;}///:~

//üretilen çıktı:degerlerin toplami: 10degerlerin carpimi: 24//

adjacent_difference( )

Başlık dosyası:#include <numeric>

İşlev öntipleri:-- OutputIterator adjacent_difference(InputIterator first,

Page 273: C++ 2 (Kitap - Selami KOÇLU)

InputIterator last, OutputIterator result);-- OutputIterator adjacent_difference(InputIterator first,InputIterator last, OutputIterator result, BinaryOperation op);

Açıklama:Bütün işlemler esas değerler üzerinde yapılır. Hesaplanan değerlerin hepsidönen değerlerdir.-- İlk öntip: İlk dönen öğe girdi alanındaki ilk öğeye eşittir. Dönenöğelerin geri kalanları, girdi alanında denk gelen öğe ile bir önceki öğeninfarkına eşittir.-- İkinci öntip: İlk dönen öğe girdi alanındaki ilk öğeye eşittir. Dönenöğelerin geri kalanları, girdi alanında denk gelen öğe (sol işlenen) ile birönceki öğeye (sağ işlenen) binary operator op uygulanması neticesineeşittir.

Örnek:

//:B05:adj_dif.cpp//adjacent_difference örneği#include <iostream>#include <numeric>#include <vector>using namespace std;int main( ){ int ia[]={1, 2, 5, 10}; vector<int> iv(ia, ia+4); vector<int> ov(iv.size( )); adjacent_difference(iv.begin( ), iv.end( ), ov.begin( )); copy(ov.begin( ), ov.end( ), ostream_iterator<int>(cout, “ “)); cout<<endl; adjacent_difference(iv.begin( ), iv.end( ), ov.begin( ), minus<int>( )); copy(ov.begin( ), ov.end( ), ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~

//üretilen çıktı:1 1 3 51 1 3 5//

Page 274: C++ 2 (Kitap - Selami KOÇLU)

adjacent_find( )

Başlıkdosyası:#include <algorithm>

İşlev öntipleri:-- ForwardIterator adjacent_find(ForwardIterator first,ForwardIterator last);-- OutputIterator adjacent_find(ForwardIterator first,ForwardIterator last, Predicate pred);

Açıklama:-- İlk öntip: ilk çift bitişik iki eşit öğenin ilk öğesini işaret edenyineleyici döner. Böyle bir öğe yoksa o zaman son öğe (last) döner.-- İkinci öntip: iki bitişik öğenin ilk çiftinin ilk öğesini işaret eden vebunun için de binary yüklem pred i true döndüren yineleyiciyi döndürür.Böyle bir öğe yoksa o zaman son öğe (last) döner.

Örnek://:B05:adj_fin.cpp//adjacent_find( ) örneği#include <iostream>#include <string>#include <algorithm>class SquaresDiff{ unsigned d_minimum; public: SquaresDiff(unsigned minimum) : d_minimum(minimum) { } bool operator( ) (unsigned first, unsigned second){ return second*second-first*first >=d_minimum; }};using namespace std;int main( ){

Page 275: C++ 2 (Kitap - Selami KOÇLU)

string sarr[]={“alpha”, “bravo”, “charley”, “delta”, “echo”, “echo”, “foxtrot”,”golf”}; string* last=sarr+sizeof(sarr)/sizeof(string) ; string* result=adjacent_find(sarr, last) ; cout<<*result<<endl; result=adjacent_find(++result, last) ; cout<<”ikinci sefer, bir sonraki yerden baslar:\n”<< (result==last ? “***daha fazla bitisik ayni oge yok***” : “*result”)<<endl; unsigned iv[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; unsigned* ilast=iv+sizeof(iv)/sizeof(unsigned) ; unsigned* ires=adjacent_find(iv, ilast, SquaresDiff(10)); cout<<”kareleri farki en az 10 olan ilk sayilar: “ <<*ires<<” ve “<<*(ires+1)<<endl; return 0;}///:~

//üretilen çıktı:echoikinci sefer, bir sonraki yerden baslar:***daha fazla bitisik ayni oge yok***kareleri farki en az 10 olan ilk sayilar: 5 ve 6 //

binary_search( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- bool binary_search(ForwardIterator first, ForwardIterator last, Type const& value);-- bool binary_search(ForwardIterator first, ForwardIterator last, Type const& value, Comparator comp);

Açıklama:

Page 276: C++ 2 (Kitap - Selami KOÇLU)

-- Birinci öntip: [first, last) yineleyici alan öğelerinde binary aramayaparak value değerini arayıp bulmak. Type::operator<( ) işlevi, alandakiöğeleri sıraya koymuş olmalıdır. Aranan öğe bulunursa true döner yok eğeraranan öğe bulunamazsa false döner.-- İkinci öntip: [first, last) yineleyici alan öğelerinde binary aramayaparak value değerini arayıp bulmak. Comparator işlev nesnesi alandakiöğeleri mutlaka sıraya koymuş olmalıdır. Aranan öğe bulunduğunda truedöner, aranan öğe bulunamazsa false döner.

Örnek:

//:B05:ikici_arama.cpp//binary_search kullanımı#include <algorithm>#include <string>#include <iostream>#include <functional>using namespace std;int main( ){ string sarr[]={“alpha”, “bravo”, “charley””delta”, “echo”, “foxtrot”, “golf”, “hotel”}; string* last=sarr+sizeof(sarr)/sizeof(string); bool result=binary_search(sarr, last, “foxtrot”); cout<<(result ? “bulundu” : “bulunmadi”)<<”foxtrot”<<endl; reverse(sarr, last) ; //öğe sıralamasını ters düzenle //şimdi binary arayıp bulma çalışmaz result=binary_search(sarr, last, “foxtrot”); cout<<(result ? “bulundu” : “bulunmadi”)<<”foxtrot”<<endl; //uygun comparator kullanıldığında tamam: result=binary_search(sarr, last, “foxtrot”, greater<string>( )); cout<<(result ? “bulundu” : “bulunmadi”)<<”foxtrot”<<endl; return 0;}///:~//üretilen çıktı:bulundu foxtrotbulunmadi foxtrot bulundu foxtrot//

Page 277: C++ 2 (Kitap - Selami KOÇLU)

copy( ) Başlık dosyası:#include <algorithm>

İşlev öntipi:-- OutputIterator copy(InputIterator first, InputIterator last, OutputIterator destination);

Açıklama:[first, last) yineleyici alanındaki öğeler, belirlenmiş veri tipine ait atamaişleci kullanılıp destination dan başlayarak çıktı alanına kopyalanır. Dönüşdeğeri, varış alanına kopyalanan son öğenin hemen ötesini işaret edenOutputIterator dür. (böylece varış alanındaki 'last' döner).

Örnek:call( ) işlevine ikinci çağrıyı bildirilecek. string nesneleri içinostream_iterator kullanılacak. Bu yineleyici string değerlerini ayırmastring i (“ “) kullanarak belirlenmiş ostream e (yani cout) yazılır.

//:B05:kopyala.cpp//copy( ) örneği#include <iostream>#include <algorithm>#include <string>#include <iterator>using namespace std;int main( ){ string sarr[]={“alpha”, “bravo”, “charley”, “delta”, “echo”,“foxtrot”, “golf”, “hotel”}; string* last=sarr+sizeof(sarr)/sizeof(string); copy(sarr+2, last, sarr); //bütün öğeleri iki adım sola kaydır //string ler için ostream_iterator kullanarak cout a kopyala copy(sarr, last, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~ //üretilen çıktı:

Page 278: C++ 2 (Kitap - Selami KOÇLU)

charley delta echo foxtrot golf hotel golf hotel//

Ayrıca unique_copy e bakınız.

copy_backward( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- BidirectionalIterator copy_backward(InputIterator first,InputIterator last, BidirectionalIterator last2);

Açıklama:-- [first, last) yineleyici alanında bulunan öğeler, belirlenmiş veri tipineuygun atama işlecini kullanıp, last-1 yerindeki öğeden başlayarak firstyerindeki öğeye kadar (first dahil), last2-1 de sonlanma ile, kopyalanır.Bundan dolayı varış alanı [last2-(last-first), last2) dir.Dönen değer, varış alanına kopyalanan son öğeyi işaret eden,BidirectionalIterator dür. (böylece varış alanındaki 'first', last2-(last-first)işaret edilen, döner). Yani son öğeden başlayarak bir alanı kopyalar.

Örnek://:B05:kopya_geri.cpp//copy_backward( ) örneği#include <iostream>#include <string>#include <algorithm>#include <iterator>using namespace std;int main( ){ string sarr[]={“alpha”, “bravo”, “charley”, “delta”, “echo”,“foxtrot”, “golf”, “hotel”}; string* last=sarr+sizeof(sarr)/sizeof(string); copy(copy_backward(sarr+3, last, last-3), last, ostream_iterator<string>(cout, “ “)); cout<<endl;

Page 279: C++ 2 (Kitap - Selami KOÇLU)

return 0;}///:~//üretilen çıktı:golf hotel foxtrot golf hotel foxtrot golf hotel//

count( ) Başlık dosyası:#include <algorithm>

İşlev öntipi:-- size_t count(InputIterator first, InputIterator last, Type const& value);

Açıklama:[first, last) yineleyici alanında value değerinin kaç defa bulunduğudöndürülür. Yineleyici alanında value değerine eşit kaç tane öğebulunduğu Type::operator==( ) işleci ile saptanır.

Örnek://B05:say.cpp//count( ) işlev kullanımı#include <algorithm>#include <iostream>using namespace std;int main( ){ int ia[]={1, 2, 3, 4, 3, 4, 2, 1, 3}; cout<<”3 degerinin kullanim sayisi: “<< count(ia, ia+sizeof(ia)/sizeof(int), 3)<<endl; return 0;}///:~//üretilen çıktı:3 degerinin kullanim sayisi: 3//

count_if( )

Page 280: C++ 2 (Kitap - Selami KOÇLU)

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- size_t count_if(InputIterator first, InputIterator last, Predicate predicate);

Açıklama:first, last yineleyici alanındaki öğelere uygulandığında tekli yüklem'predicate' in true döndürdüğü sayı döner. Yani koşula uyan öğe sayısınıdöner.

Örnek://:B05:say_eger.cpp//count_if( ) işlev kullanımı #include <iostream>#include <algorithm>class Tek{ public: bool operator( )(int value){ return value& 1; }};using namespace std;int main( ){ int ia[ ]={1, 2, 3, 4, 3, 4, 2, 1, 3}; cout<<”dizideki tek sayi adedi: “ <<count_if(ia, ia+sizeof(ia)/sizeof(int), Tek( ))<<endl; return 0;}///:~//üretilen çıktı:dizideki tek sayi adedi: 5//

equal( )

Başlık dosyası:

Page 281: C++ 2 (Kitap - Selami KOÇLU)

#include <algorithm>

İşlev öntipleri:-- bool equal(InputIterator first, InputIterator last, InputIterator otherFirst);-- bool equal(InputIterator first, InputIterator last, InputIterator otherFirst,BinaryPredicate pred);

Açıklama:-- Birinci öntip: [first, last) alanındaki öğeler aynı uzunluktaki otherFirstten başlayan alandaki öğelerle karşılaştırılır. İşlev aynı uzunlukta ki her ikialanda eşit öğe çiftlerine rastladığında true üretir. Aslında alanlarıntamamının eşit olması gerekmeyebilir, sadece belirtilen karşılaştırmaalanları aynı olsa yeterlidir. (tabii ki belirtilen alan mutlaka bulunmalıdır).-- İkinci öntip: [first, last) alanındaki öğeler aynı uzunlukta ki otherFirstten başlayan alandaki öğelerle karşılaştırılır. Her iki alanda karşılıklı gelenbütün öğelerin true ürettiği her karşılıklı çifte, Binary yüklem (pred)uygulandığında işlev true üretir. Her iki alanın tamamının eşit olmasıgerekmez, sadece dikkate alınan öğelerin sayısının aynı olması yeterlidir.(tabii ki belirtilen alan mutlaka bulunmalıdır).

Örnek://:B05:esit.cpp//equal( ) işlev örneği#include <algorithm>#include <string>#include <iostream>class CaseString{ public: bool operator( )(std::string const& first, std::string const& second) const{ return !strcasecmp(first.c_str( ), second.c_str( )) ; }};using namespace std;int main( ){ string first[]={“Alpha”, “bravo”, “Charley”, “delta”, “Echo”,“foxtrot”, “Golf”, “hotel”}; string second[]={“alpha”, “bravo”, “charley”, “delta”, “echo”,“foxtrot”, “golf”, “hotel”}; string* last=first+sizeof(first)/sizeof(string);

Page 282: C++ 2 (Kitap - Selami KOÇLU)

cout<<”The elements of 'first' and 'second' are pairwise”<< (equal(first, last, second) ? “equal” : “not equal”)<< endl<<”compared case-insensitively, they are”<< (equal(first, last, second, CaseString( )) ? “equal” : “not equal”) <<endl; return 0;}///:~//üretilen çıktı:The elements of 'first' and 'second' are pairwise not equalcompared case-insensitively, they are equal.//

equal_range( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- pair<ForwardIterator, ForwardIterator> equal_range(ForwardIteratorfirst, ForwardIterator last, Type const& value);-- pair<ForwardIterator, ForwardIterator> equal_range(ForwardIteratorfirst, ForwardIterator last, Type const& value, Compare comp);

Açıklama:(map ve multimap teki aynı isimli üye işlevlere de bakınız)-- İlk öntip: Sıraya konmuş olan bir diziden başlayarak (belirtilenalandaki veri tipinin işaret ettiği yineleyici ile operator<( ) işlecikullanılarak öğeler sıralanır), göreceli olarak lower_bound( ) (belirtilendayanç değerinden daha küçük olmayan ilk öğeyi döner) ve upper_bound( ) (belirtilen dayanç değerinin ötesindeki ilk öğeyi döner)dönüş değerini temsil eden bir çift yineleyici döner.-- İkinci öntip: Sıraya konmuş olan bir diziden başlayarak (comp işlevnesnesi belirtilen alanda sıraya koyma işleminde kullanılır), göreceli olaraklower_bound( ) ve upper_bound( )dönüş değerini temsil eden bir çiftyineleyici döner.

Örnek://:B05:esit_alan.cpp

Page 283: C++ 2 (Kitap - Selami KOÇLU)

//equal_range( ) algoritma kullanımı #include <iostream>#include <algorithm>#include <iterator>#include <functional>using namespace std;int main( ){ int range[]={1, 3, 5, 7, 7, 9, 9, 9}; unsigned const size=sizeof(range)/sizeof(int); pair<int*, int*> pi ; pi=equal_range(range, range+size, 6); cout<<”lower_bound for 6: “<<*pi.first<<endl ; cout<<”upper_bound for 6: “<<*pi.second<<endl; pi=equal_range(range, range+size, 7); cout<<”lower_bound for 7: “; copy(pi.first, range+size, ostream_iterator<int>(cout, “ “)); cout<<endl; cout<<”upper_bound for 7: “; copy(pi.second, range+size, ostream_iterator<int>(cout, “ “)); cout<<endl; sort(range, range+size, greater<int>( )); cout<<”sorted in descending order\n”; copy(range, range+size, ostream_iterator<int>(cout, “ “)); cout<<endl; pi=equal_range(range, range+size, 7, greater<int>( )); cout<<”lower_bound for 7: “; copy(pi.first, range+size, ostream_iterator<int>(cout, “ “)); cout<<endl; cout<<”upper_bound for 7: “; copy(pi.second, range+size, ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:lower_bound for 6: 7upper_bound for 6: 7lower_bound for 7: 7 7 9 9 9 upper_bound for 7: 9 9 9 sorted in descending order9 9 9 7 7 5 3 1

Page 284: C++ 2 (Kitap - Selami KOÇLU)

lower_bound for 7: 7 7 5 3 1 upper_bound for 7: 5 3 1//

fill( ) Başlık dosyası:#include <algorithm>

İşlev öntipi:-- void fill(ForwardIterator first, ForwardIterator last, Type const& value);

Açıklama:-- [firdt, last) yineleyici alanının belirttiği bütün öğeler value değeri ile(önceden saklı değerlerin üzerine de yazılarak) başlatılır.

Örnek://:B05:doldur.cpp//fill( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <vector>#include <iterator>using namespace std;int main( ){ vector<int> iv(8) ; fill(iv.begin( ), iv.end( ), 8); copy(iv.begin( ), iv.end( ), ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~

//üretilen çıktı:8 8 8 8 8 8 8 8//

Page 285: C++ 2 (Kitap - Selami KOÇLU)

fill_n( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- void fill_n(ForwardIterator first, Size n, Type const& value);

Açıklama:-- first ten başlayarak n öğe value değeri ile ilk değer atamaları yapılır, Buöğelerin önceki değerleri üzerine de başlatma değeri yazılabilir, belirtelim.

Örnek://:B05:n_doldur.cpp//fill_n( ) algoritma örneği#include <iostream>#include <algorithm>#include <vector>#include <iterator>using namespace std;int main( ){ vector<int> iv(8); fill_n(iv.begin( )+2, 4, 8); copy(iv.begin( ), iv.end( ), ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:0 0 8 8 8 8 0 0//

find( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- InputIterator find(InputIterator first, InputIterator last,

Page 286: C++ 2 (Kitap - Selami KOÇLU)

Type const& value);

Açıklama:-- [first, last) yineleyici alanındaki öğelerde value öğesi araştırılır.Yineleyicinin saptadığı ilk öğe döner. Yok eğer öğe bulunmazsa o zamanlast döner. Öğeleri karşılaştırmak için belirtilen veri tipinini operator==( )işleci kullanılır.

Örnek://:B05:bul.cpp//find( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <iterator>using namespace std;int main( ){ string sarr[]={“alpha”, “bravo”, “charley”, “delta”, “echo”}; string* last=sarr+sizeof(sarr)/sizeof(string); copy(find(sarr, last, “delta”), last, ostream_iterator<string>(cout, ““)); cout<<endl; if(find(sarr, last, “india”)==last){ cout<<”india bulunamadi\n”; copy(sarr, last, ostream_iterator<string>(cout, “ “)); cout<<endl; } return 0 ;}///:~//üretilen çıktı:delta echo'india' bulunamadialpha bravo charley delta echo//

find_end( )

Başlık dosyası:

Page 287: C++ 2 (Kitap - Selami KOÇLU)

#include <algorithm>

İşlev öntipleri:-- ForwardIterator1 find_end(ForwardIterator1 first1, forwardIterator1last1, ForwardIterator2 first2, ForwardIterator2 last2)-- ForwardIterator1 find_end(ForwardIterator1 first1, forwardIterator1last1, ForwardIterator2 first2, ForwardIterator2 last2, BinaryPredicatepred)

Açıklama:-- İlk öntip: [first1, last1) te ki öğe dizilerinde [first2, last2) te ki öğedizisinin son tekrarını arayıp bulur. Eğer [first2, last2) dizisi bulunmazsa, ozaman last1 döner, yok eğer bulunursa uyan dizinin ilk öğesini işaret edenyineleyici döner. Her iki dizinin karşılaştırmasında ilgili veri tipininoperator==( ) ü kullanılır.-- İkinci öntip: [first1, last1) te ki öğe dizilerinde [first2, last2) te ki öğedizisinin son tekrarını arayıp bulur. Eğer [first2, last2) dizisi bulunmazsa ozaman last1 döner, bulunursa uyan dizinin ilk öğesini işaret eden yineleyicidöner. Her iki dizinin karşılaştırmasında ilgili veri tipinin operator==( ) ükullanılır. BinaryPredicate ise her iki dizideki öğelerin karşılaştırmasındakullanılır.

Örnek://:B05:en_sonu_bul.cpp//find_end( ) agoritma kullanımı #include <iostream>#include <algorithm>#include <iterator>#include <string>class Twice{ public: bool operator( )(unsigned first, unsigned second) const{ return first==(second<<1); }};using namespace std;int main( ){ string sarr[]={“alpha”, “bravo”, “charley”, delta”, “echo”, “foxtrot”, “golf”, “hotel”, foxtrot”, “golf”, “hotel”, “india”,“juliet”,

Page 288: C++ 2 (Kitap - Selami KOÇLU)

“”kilo”}; string search[]={“foxtrot”, “golf”, “hotel”}; string* last= sarr+sizeof(sarr)/sizeof(string); copy(find_end(sarr, last, search, search+3), //dizi başlıyor last, ostream_iterator<string>(cout, “ “)); //2. foxtrot ta cout<<endl; unsigned range[]={2, 4, 6, 8, 10, 4, 6, 8, 10}; unsigned nrs[]={2, 3, 4}; copy //son dizide başlayan değerlerin dizisi (find_end(range, range+9, nrs, nrs+3, Twice( )), range+9, ostream_iterator<unsigned>(cout, “ “)); cout<<endl; return 0;}///:~//ütretilen çıktı:foxtrot golf hotel india juliet kilo4 6 8 10 //

find_first_of( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- ForwardIterator1 find_first_of(ForwardIterator1 first1,ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2);-- ForwardIterator1 find_first_of(ForwardIterator1 first1,ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2, BinaryPredicate pred);

Açıklama:-- Birinci öntip: [first1, last1) daki öğeler dizisi, [first2, last2) öğelerdizisinde bulunan bir öğenin ilk kez ortaya çıkışı için araştırılır. [first2,last2) dizisinde öğe bulunmazsa o zaman last1 döner. Aksi durumda[first2, last2) deki bir öğeye eşit olan [first1, last1) daki ilk öğeyi işareteden yineleyici döner. Her iki dizideki öğeleri karşılaştırmak için

Page 289: C++ 2 (Kitap - Selami KOÇLU)

kullanılan veri tipinin operator==( ) ü kullanılır.-- İkinci öntip: [first1, last1) daki öğeler dizisi, [first2, last2) öğelerdizisinde bulunan bir öğenin ilk kez ortaya çıkışı için araştırılır. [first1,last1) da ki her öğe, [first2, last2) da ki her öğe ile karşılaştırılır, BinaryPredicate pred ve [first2, last2) da true döndüren, [first1, last1) da ki ilköğenin yineleyicisi döner. Aksi durumda last1 döner.

Örnek://:B05:ilkini_bul.cpp//find_first_of( ) kullanımı#include <algorithm>#include <string>#include <iostream>#include <iterator>class Twice{ public: bool operator( )(unsigned first, unsigned second) const{ return first==(second<<1); }};using namespace std;int main( ){ string sarr[]={“alpha”, “bravo”, “charley”, “delta”, “echo”, “foxtrot”, “golf”, “hotel”, “foxtrot”, “golf”, “hotel” “india”, “juliet”, “kilo”}; string search[]={“foxtrot”, “golf”, “hotel”}; string* last=sarr+sizeof(sarr)/sizeof(string); copy(find_first_of(range, range+9, nrs, nrs+3, Twice( )), range+9, ostream_iterator<unsigned>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:foxtrot golf hotel foxtrot golf hotel india juliet kilo4 6 8 10 4 6 8 10//

Page 290: C++ 2 (Kitap - Selami KOÇLU)

find_if( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- InputIterator find_if(InputIterator first, InputIterator last, Predicate pred);

Açıklama:-- [first, last) yineleyici alanının gösterdiği öğelerden, predicate pred (tekdeğerli=unary) in true döndüğü ilk öğeyi işaret eden yineleyiciyi döner.Öğe bulunmazsa o zaman last döner.

Örnek://:B05: bul_eger.cpp//find_if( ) kullanımı#include <algorithm>#include <iostream>#include <string>#include <iterator>class CaseName{ std::string d_string; public: CaseName(char const* str) : d_string(str){ } bool operator( )(std::string const& element){ return !strcasecmp(element.c_str( ), d_string.c_str( )); }};using namespace std;int main( ){ string sarr[]={“Alpha”, “Bravo”, “Charley”, “Delta”, “Echo”}; string* last=sarr+sizeof(sarr)/sizeof(string); copy(find_if(sarr, last, CaseName(“charley”)), last, ostream_iterator<string>(cout, “ “)); cout<<endl; if(find_if(sarr, last, CaseName(“india”)==last){ cout<<” 'india' alanda bulunamadi\n”;

Page 291: C++ 2 (Kitap - Selami KOÇLU)

copy(sarr, last, ostream_iterator<string>(cout, “ “)); cout<<endl; } return 0;}///:~//üretilen çıktı:Charley Delta Echo'india' alanda bulunamadiAlpha Bravo Charley Delta Echo//

for_each( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- Function for_each(ForwardIterator first, ForwardIterator last, Function func);

Açıklama:-- [first, last) yineleyici alanındaki her öğe sırası ile func işlevine (veyaişlev nesnesine=function object) aktarılır. İşlev bünyesine aldığı öğelerideğiştirebilir (kullanılan yineleyici forward (öncü) yineleyici olduğunda).Değişimin başka bir yolu da transform( ) un kullanılmasıdır. Eldeki işlevinkendisi veya kopyası döner. Aşağıdaki örneğe bakarsanız, her for_each( )çağrısına ek değişken listesi konmuştur, değişken for_each( ) e verilenişleve aktarılır. for_each( ) e aktarılan işlev dönüş değeri gözardı edilir.

Örnek://:B05:herbiri.cpp//for_each( ) kullanımı#include <algorithm>#include <string>#include <cctype>#include <iostream>void lowerCase(char& c){ c=static_cast<char>(tolower(c));

Page 292: C++ 2 (Kitap - Selami KOÇLU)

}void capitalizedOutput(std::string const& str){ char* tmp=strcpy(new char[str.size( )+1], str.c_str( )); std::for_each(tmp+1, tmp+str.size( ), lowerCase); tmp[0]=toupper(*tmp); std::cout<<tmp<<” “; delete tmp;};using namespace std;int main( ){ string sarr[]={“alpha”, “BRAVO”, “charley”, “DELTA”, “echo”, “”FOXTROT”, “golf”, “HOTEL”}; string* last=sarr+sizeof(sarr)/sizeof(string); for_each(sarr, last, capitalizedOutput)(“hepsi bu, millet”); cout<<endl; return 0;}///:~//üretilen çıktı:Alpha Bravo Charley Delta Echo Foxtrot Golf Hotel hepsi bu millet//

İşlev nesnesi kullanılan başka bir örnek:

//:B05:func_object.cpp//işlev nesnesi kullanılan örnek#include <algorithm>#include <iostream>#include <cctype>#include <string>void lowerCase(char& c){ c=tolower(c);}class Show{ int d_count; public: Show( ) : d_count(0);{} void operator( )(std::string& str){ std::for_each(str.begin( ), str.end( ), lowerCase); str[0]=toupper(str[0]); std::cout<<++d_count<<” “<<str<<” ; “;

Page 293: C++ 2 (Kitap - Selami KOÇLU)

} int count( ) const{ return d_count; }};using namespace std;int main( ){ string sarr[]={“alpha”, “BRAVO”, “charley”, “DELTA”, “echo”, “FOXTROT”, “golf”, “HOTEL”}; string* last=sarr+sizeof(sarr)/sizeof(string); cout<<for_each(sarr, last, show( )).count( )<<endl; return 0;}///:~//üretilen çıktı:hepsi tek satırda:1 Alpha; 2 Bravo; 3 Charley; 4 Delta; 5 Echo; 6 Foxtrot; 7 Golf; 8 Hotel; 8//

Örnek bize ayrıca, for_each( ) algoritmasının const ve const olmayanunsurlarla kullanımını da göstermiştir. İlerde yer alan transform( ) kalıpalgoritmasına bakarak, buradaki for_each( ) algoritması ile arasında kifarkları inceleyebilirsiniz. for_each( ) algoritması üye işlev içerisinde, for_each( ) algoritması önceaktarılan işlev nesnesinin kendi kopyasını oluştururken, kendi nesnesinideğiştirmek için doğrudan kullanılamaz. O an ki nesnesine göstergeç veyadayanç kabul eden yapıcı işlevi olan bir örtücü (wrapper) sınıf, ve büyükolasılıkla onun üye işlevlerinden biri bu sorunu çözer.

generate( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- void generate(ForwardIterator first, ForwardIterator last, Generator generator);

Açıklama:

Page 294: C++ 2 (Kitap - Selami KOÇLU)

-- [first, last) yineleyici alanında ki bütün öğeler bir işlev veya işlevnesnesi olan generator ün dönüş değeri ile başlatılır. Dikkat edilmesigereken husus; Generator::operator( )( ) da herhangi bir değişkeninverilmemiş olduğudur. Aşağıdaki örnek cebir de iyi bilinen bir örnektir;n+1 in karesi. n*n ene 1+2*n eklemek gibi yani.

//:B05:uret.cpp//generator( ) kullanımı#include <iostream>#include <algorithm>#include <vector>#include <iterator>class NaturalSquares{ unsigned d_newsqr; unsigned d_last; public: NaturalSquares( ) : d_newsqr(0), d_last(0){ } unsigned operator( )( ){ return d_newsqr+=(d_last++<<1)+1; //(a+1)**2==a**2+2*a+1 }};using namespace std;int main( ){ vector<unsigned> uv(10); generate(uv.begin( ), uv.end( ), NaturalSquares( )); copy(uv.begin( ), uv.end( ), ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~

//üretilen çıktı: 1 4 9 16 25 36 49 64 81 100//

generate_n( )

Başlık dosyası:

Page 295: C++ 2 (Kitap - Selami KOÇLU)

#include <algorithm>

İşlev öntipi:-- void generate_n(ForwardIterator first, Size n, Generator generator);

Açıklama:-- first yineleyicisinin işaret ettiği öğeden başlayan n öğe, işlev veya işlevnesnesi olan generator ün dönüş değeri ile başlarlar.

Örnek://:B05:uret_n.cpp//generate_n( ) kıullanımı#include <iostream>#include <algorithm>#include <iterator>#include <vector>class NaturalSquares{ unsigned d_newsqr; unsigned d_last; public: NaturalSquares( ) : d_newsqr(0), d_last(0){ } unsigned operator( )( ){ //(a+1)**2==a**2+2*a+1 kullan return d_newsqr+=(d_last++<<1)+1; }};using namespace std;int main( ){ vector<unsigned> uv(10); generate_n(uv.begin( ), 5, NaturalSquares( )); copy(uv.begin( ), uv.end( ), ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~

//üretilen çıktı:1 4 9 16 25 0 0 0 0 0//

Page 296: C++ 2 (Kitap - Selami KOÇLU)

includes( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- bool includes(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2) ;-- bool includes(InputIterator1 first1, InputIterator1 last1, InputIterator2first1, InputIterator2 last2, Compare comp);

Açıklama:-- İlk öntip: Yineleyicilerin işaret ettiği veri tipinin operator<( ) ükullanılarak hem [first1, last1) hem de [first2, last2) öğelerini sıraya dizer.Eğer [first2, last2) daki her öğe [first1, last1) da bulunursa işlev truedöndürür. (ikinci alan birincinin alt kümesidir).-- İkinci öntip: comp işlev nesnesi kullanılarak hem [first1, last1) da kihem de 8first2, last2) da ki öğeler sıraya dizilir. Eğer [first2, last2) da kiher öğe [first1, last1) da bulunursa işlev true döndürür. (ikinci alanbirincinin alt kümesidir.)

Örnek://:B05:icerir.cpp//includes( ) kullanımını#include <iostream>#include <algorithm>#include < string>class CaseString{ public: bool operator( )(std::string const& first, std::string const& second)const { return (!strcasecmp(first.c_str( ), second.c_str( ))); } };using namespace std;int main( ){ string first1[]={“alpha”, “bravo”, “charley”, “delta”, “echo”,“foxtrot”,

Page 297: C++ 2 (Kitap - Selami KOÇLU)

“golf”, “hotel”}; string first2[]={“Alpha”, “bravo”, “Charley”, “delta”, “Echo”, “foxtrot”, “Golf”, “hotel”}; string second[]={“charley”, “foxtrot”, “hotel”}; unsigned n=sizeof(first1)/sizeof(string); cout<<” 'second' ogeleri “<<(includes(first1, first1+n, second,second+3) ? “ “ : “degil”)<<”ilk dizide bulunur:\n” “ikincisi first1 ninalt grubu\n”; cout<<” 'first1' nin ogeleri:”<<(includes(second, second+3, first1,first1+n) ? “ “ : “degil”)<<”ikinci dizide \n”; cout<<”'second' ogeleri: “<<(includes(first2, first2+n, second,second+3) ? “ “ : “degil”)<<”first2 dizisinde \n”; cout<<”harf boyutuna bakmadan karsilastirma,\n” “'second' ogeleri: \n”<<(includes(first2, first2+n, second,second+3, CaseString( )) ? “ “ : “degil”)<<”first2 dizisinde \n” return 0;}///:~

//üretilen çıktı:'second' ogeleri ilk dizide bulunur ikincisi first1 nin alt grubudur'first1' nin ogeleri ikinci dizide degil'second' ogeleri first2 dizisinde degilharf boyutuna bakmadan karsilastirma'second' ogeleri frist2 dizisinde degil//

inner_product( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- Type inner_product(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, Type init);-- Type inner_product(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, Type init, BinaryOperator1 op1, BinaryOperator2op2);

Page 298: C++ 2 (Kitap - Selami KOÇLU)

Açıklama:-- Birinci öntip: [first1, last1) öğelerinin bütün ikili çarpımlarının ve first2nin işaret ettiği öğeden başlayarak aynı sayıda öğelerin toplamı init eeklenir, ve bu toplam döner. İşlev yineleyicilerin işaret ettiği veri tipininoperator+( ) ve operator*( ) leri kullanılır.-- İkinci öntip: Varsayılan toplama işleci yerine binary operator op2 vebinary operator op1 varsayılan çarpım işleci yerine kullanılırarakyukarıdaki birinci öntipteki işlem gerçeklenir ve toplam sonuç döner.

Örnek://:B05:ic_carp.cpp//inner_product( ) kullanımı#include <numeric>#include <iostream>#include <string>#include <iterator>#include <algorithm>calss Kedi{ std::string d_sep; public: Kedi(std::string const& sep) : d_sep(sep){ } std:.string operator( ) (std::string const& s1, std::string const& s2) const{ return s1+d_sep+s2; }};using namespace std;int main( ){ unsigned ia1[]={1, 2, 3, 4, 5, 6, 7}; unsigned ia2[]={7, 6, 5, 4, 3, 2, 1}; unsigned init=0; cout<<”kareler toplam: “; copy(ia1, ia1+7, ostream_iterator<unsigned>(cout, “ “)); cout<<”dir”<<<<inner_product(ia1, ia1+7, ia1, init)<<endl; cout<<”capraz carpimlarin tamaminin toplami”; copy(ia1, ia1+7, ostream_iterator<unsigned>(cout, “ “)); cout<<” ve”; copy(ia2, ia2+7, ostream_iterator<unsigned>(cout, “ “)); cout<<” dir”<<inner_product(ia1, ia1+7, ia2, init)<<endl;

Page 299: C++ 2 (Kitap - Selami KOÇLU)

string names1[]={“Faruk”, “Kerim”, “Pervin”}; string names2[]={“Belgin”, “Koclu”, “Pelin”}; cout<<”isimlerin birarada listesi: “; copy(names1, names1+3, ostream_iterator<string>(cout, “ “)); cout<<”ve\n”; copy(names2, names2+3, ostream_iterator<string>(cout, “ “)); cout<<”dir:”<<inner_product(names1, names1+3, names2, string(“\t”, Kedi(“\n\t”), Kedi(“ “))<<endl; return 0;}///:~//üretilen çıktı:kareler toplami 1 2 3 4 5 6 7 140 dircapraz carpimlarin tamaminin toplami 1 2 3 4 5 6 7 ve 7 6 5 4 3 2 1 84 dirisimlerin birarada listesi: Faruk Kerim Pervin veBelgin Koclu Pelin dir:Faruk BelginKerim KocluPervin Pelin//

inplace_merge( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void inplace_mergeCHAR(40)BidirectionalIterator first,BidirectionalIterator middle, BidirectionalIterator lastCHAR(41);-- void inplace_mergeCHAR(40)BidirectionalIterator first,BidirectionalIterator middle, BidirectionalIterator last, ComparecompCHAR(41);

Açıklamalar:-- İlk öntip: [first, middle) ve [middle, last) iki sıralı alan birbiri ilebirleştirilir, yineleyicilerin işaret ettiği veri tiplerinin operator<( ) ikullanılarak listenin sıralı olması sürdürülür. Son dizi [first, last) alanındasaklanır.-- İkinci öntip: [first, middle) ve [middle, last) iki sıralı alan birbiri ile

Page 300: C++ 2 (Kitap - Selami KOÇLU)

birleştirilir, ikici (binary) karşılaştırma işleci operator comp un doğru veyanlış bool sonuçlarını kullanılarak listenin sıralı olması sürdürülür. Sondiziler [first, last) alanında saklanır.

Örnek:

//:B05:yerinde_birles.cpp//inplace_merge( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <iterator>class CaseString{ public: bool operator( )(std::string const &first, std::string const &second) const{ return strcasecmp(first.c_str( ), second.c_str( ))<0 ; }};using namespace std;int main( ){ string range[]={“alpha”, “charley”, “echo”, “golf”, “bravo”, “delta”, “foxtrot”}; inplace_merge(range, range+4, range+7); copy(range, range+7, ostream_iterator<string>(cout, “ “); cout<<endl; string range2[]={“ALFA”, “CHARLEY”, “DELTA”, “foxtrot”, “hotel”, “bravo”, “ECHO”, “GOLF”}; inplace_merge(range2, range2+5, range2+8, CaseString( )); copy(range2, range2+8, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:alpha bravo charley delta echo foxtrot golfALFA bravo CHARLEY DELTA ECHO foxtrot GOLF hotel//

Page 301: C++ 2 (Kitap - Selami KOÇLU)

iter_swap( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri: void iter_swap(ForwardIterator1 iter1, ForwardIterator2 iter2);

Açıklama: iter1 ve iter2 yineleyicileri tarafından işaret edilen öğeler takas edilir.

Örnek:

//:B05:takas.cpp//iter_swap( ) algoritma kullanımı#include <iostream>#include <string>#include <iterator>#include <algorithm>using namespace std;int main( ){ string first[]={“alpha”, “bravo”, “charley”}; string second[]={“echo”, “foxtrot”, “golf”}; unsigned const n=sizeof(first)/sizeof(string); cout<<”Once:\n”; copy(first, first+n, ostream_iterator<string>(cout, “ “)); cout<<endl; copy(second, second+n, ostream_iterator<string>(cout, “ “)); cout<<endl; for(unsigned idx=0; idx<n; ++idx){ iter_swap(first+idx, second+idx); cout<<”Sonra:\n; copy(first, first+n, ostream_iterator<string>(cout, “ “)); cout<<endl; copy(second, second+n, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:

Page 302: C++ 2 (Kitap - Selami KOÇLU)

Once:alpha bravo charleyecho foxtrot golfSonra:echo foxtrot golfalpha bravo charley//

lexicographical_compare( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- bool lexicographical_compare(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2);-- bool lexicographical_compare(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, Compare comp);

Açıklama:-- Birinci öntip: [first1, last1) ile [first2, last2) alanlarının birbirlerine denkgelen öğeleri karşılaştırılır. İşlevden doğru (true) dönmesi için;** ilk alandaki ilk öğe ikinci alandaki ilk öğeden küçük olmalıdır. (bununiçin aynı veri tipinin operator<( ) ü kullanılır)** last1 a varılmış olup last2 ya henüz varılmamış olmalıdır.Bunların dışındaki her durumda işlevden yanlış (yani false) döner. Bununanlamı ilk dizi yazım olarak ikinci diziden küçük değil demektir. İşlevdenyanlış (false) dönmesi için:** ilk alandaki ilk öğe ikinci alandaki ilk öğeden büyüktür. (karşılaştırmakiçin aynı veri tipinin operator<( ) ü kullanılır).** last2 ya varılmış olup eğer last1 a henüz varılmamışsa.** last1 ve last2 ya varılmışsa.-- İkinci öntip: Bu işlevde yineleyicilerin işaret ettiği öğelere operator<( )yerine ikici karşılaştırma (binary comparison) işlemi gerçeklenir.

Örnek:

//:B05:yaz_karsila.cpp

Page 303: C++ 2 (Kitap - Selami KOÇLU)

//lexicographical_compare( ) kullanımı#include <algorithm>#include <iterator>#include <iostream>#include <string>class CaseString{ public: bool operator( )(std::string const &first, std::string cost &second) const{ return strcasecmp(first.c_str( ), second.c_str( ))<0; }};using namespace std;int main ( ){ string word1=”hello”; string word2=”help”; cout<<word1<<” is “<< (lexicographical_compare(word1.begin( ), word1.end( ), word2.begin( ), word2.end( )) ? “before” : “beyond or at”)<<word2<<”in the alphabet\n”; cout<<word1<<” is “<< (lexicographical_compare(word1.begin( ), word1.end( ), word1.begin( ), word1.end( )) ? “before” : “beyond or at”)<<word1<<”in the alphabet\n”; cout<<word2<<” is “<< (lexicographical_compare(word2.begin( ), word2.end( ), word1.begin( ), word1.end( )) ? “before” : “beyond or at”)<<word1<<” in the alphabet\n”; string bir[]={“alpha”, “bravo”, “charley”}; string iki[]={“ALPHA”, “BRAVO”, “DELTA”}; copy(bir, bir+3, ostream_iterator<string>(cout, “ “)); cout<<” is ordered”<<(lexicographical_compare(bir, bir+3, iki, iki+3, CaseString( )) ? “before”

Page 304: C++ 2 (Kitap - Selami KOÇLU)

: “beyond or at”); copy(iki, iki+3, ostream_iterator<string>(cout, “ “)); cout<<endl<<harf boyutuna bakmadan karsilastirma kullanildi.\n”; return 0;}///:~//üretilen çıktı:hello is before help in the alphabethello is beyond or at hello in the alphabethelp is beyond or at hello in the alphabetalpha bravo charley is ordered before ALPHA BRAVO DELTAharf boyutuna bakmadan karsilastirma kullanildi//

lower_bound( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const Type& value);-- ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const Type& value, Compare comp);

Açıklamalar:-- Birinci öntip: [first, last) yineleyici alanındaki sıralı öğeler de, value denküçük olmayan (yani büyük veya eşit) ilk öğe aranıp bulunur. Dönenyineleyici sıralamayı bozmadan diziye value yi yerleştirecek yeri işareteder. Yineleyicilerle birlikte aynı veri tipinin operator<( ) işleci kullanılır.Böyle bir öğe bulunmazsa o zaman last döner. -- İkinci öntip: [first, last) yineleyici alanınca işaret edilen öğeler mutlakacomp işlevince (-object) sıraya dizilmiş olmalıdır. Alandaki her öğe compişlevi kullanılarak value ile karşılaştırılır. İkici yüklem (binary predicate)comp için ilk öğenin yineleyicisi value ve alandaki öğelere uygulanır,yanlış (false) döndüren döner. Böyle bir öğe bulunmazsa last döner.

Örnek:

Page 305: C++ 2 (Kitap - Selami KOÇLU)

//:B05:alt_sinir.cpp//lower_bound( ) örneği#include <algorithm>#include <iostream>#include <iterator>#include <functional>using namespace std;int main( ){ int ia[]={10, 20, 30}; cout<<”Sequence: “; copy(ia, ia+3, ostream_iterator<int>(cout, “ “)); cout<<endl; cout<<”15 can be inserted before “<<*lower_bound(ia, ia+3, 15) <<endl; cout<<”35 can be inserted after “<< lower_bound(ia, ia+3, 35)==ia+3 ? “the last element” : “????”)<<endl; iter_swap(ia, ia+2); cout<<”Sequence: “; copy(ia, ia+3, ostream_iterator<int>(cout, “ “)); cout<<endl; cout<<”15 can be inserted before “<< *lower_bound(ia, ia+3, 15, greater<int>( ))<<endl; cout<<”35 can be inserted before “<< (lower_bound(ia, ia+3, 35, greater<int>( ))==ia ? “the first element” : “????”)<<endl; return 0;}///:~~//üretilen çıktı:Sequence: 10 20 3015 can be inserted before 2035 can be inserted after the last element Sequence: 30 20 1015 can be inserted before 1035 can be inserted before the first element//

Page 306: C++ 2 (Kitap - Selami KOÇLU)

max( ) Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- Type const& max(Type const& one, Type const& two);-- Type const& max(Type const& one, Type const& two, Comparator comp);

Açıklamalar:-- Birinci öntip: one ve two öğelerinden daha büyük olanı döner. Buradaaynı veri tiplerinin operator>( ) işleci kullanılır.-- İkinci öntip: Eğer ikici yüklem (binary predicate) comp(one, two) doğru(true) üretirse one döner, aksi durumda two döner.

Örnek://:B05:kocaman.cpp//max( ) kullanımı #include <iostream>#include <algorithm>#include <string>class CaseString{ public: bool operator( )(std::string const& first, std::string const& second) const{ return (strcasecmp(second.c_str( ), first.c_str( ))>0); }};using namespace std;int main( ){ cout<<”Word' “<<max(string(“first”), string(“second”))<< “' is lexicographically last\n”; cout<<”Word' “<<max(string(“first”), string(“SECOND”))<< “' is lexicographically last\n”; cout<<”Word' “<<max(string(“first”), string(“SECOND”), CaseString( ))<<”' is lexicographically last\n”; return 0;}///~~

Page 307: C++ 2 (Kitap - Selami KOÇLU)

//üretilen çıktı:Word 'second' is lexicographically last Word 'first' is lexicographically lastWord 'SECOND' is lexicographically last //

max_element( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- ForwardIterator max_element(ForwardIterator first, ForwardIterator last);-- ForwardIterator max_element(ForwardIterator first, ForwardIterator last, Comparator comp);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki en büyük öğeyi işaret edenyineleyici döner. Yineleyiciyi belirlemek için aynı veri tipinin operator<( )işleci kullanılır.-- İkinci öntip: Bu işlevde operator<( ) yerine ikici yüklem (binarypredicate) comp kullanılarak öğeler karşılaştırılır. Öteki öğelerlekarşılaştırılırken comp için dönen öğe çoğunlukla doğru (true) olup öyledöner.

Örnek:

//:B05:koca_oge.cpp//max_element( ) kullanımı#include <iostream>#include <algorithm>class AbsValue{ public: bool operator( )(int first, int second) const{ return abs(first)<abs(second); } };

Page 308: C++ 2 (Kitap - Selami KOÇLU)

using namespace std;int main( ){ int ia[]={-4, 7, -2, 10, -12}; cout<<”The max int value is “<<*max_element(ia, ia+5)<<endl; cout<<”The max absolut int value is “<< *max_element(ia, ia+5, AbsValue( ))<<endl; return 0;}///:~~//üretilen çıktı:The max int value is 10The max absolute int value is -12//

merge( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- OutputIterator merge(InputIterator1 first1, InputIterator last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);-- OutputIterator merge(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);Açıklamalar:-- Birinci öntip: [first1, last1) ve [first2, last2) iki (sıralı) alan yine sıralıbiçimde biraraya getirilir. (sıralamayı sağlamak için aynı verinin operator<( ) işleci kullanılır). Son dizi result tan başlar ve işlevindöndürdüğü OutputIterator den hemen önce biter. -- İkinci öntip: Burada da aynı birleştirme işlemi gerçeklenir, ama bu işlemesnasında ikici karşılaştırıcı (binary comparison) comp sıralama amacı ilekullanılır.

Örnek://:B05:biles.cpp//merge( ) kullanımı#include <algorithm>#include <iostream>

Page 309: C++ 2 (Kitap - Selami KOÇLU)

#include <string>#include <iterator>class CaseString{ public: bool operator( )(std::string const& first, std::string const&second) const{ return (strcasecmp(first.c_str( ), second.c_str( ))<0); }};using namespace std;int main( ){ string range1[]={“alpha”, “bravo”, “foxtrot”, “hotel”, “zulu”}; string range2[]={“echo”, “delta”, “golf”, “romeo”}; string result[5+4]; copy(result, merge(range1, range1+5, range2, range2+4, result), ostream_iterator<string>(cout, “ “)); cout<<endl; string range3[]={“ALPHA”, “bravo”, “foxtrot”, “HOTEL”, “ZULU”}; string range4[]={“delta”, “ECHO”, “GOLF”, “romeo”}; copy(result, merge(range3, range3+5, range4, range4+4, result, CaseString( )), ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~~//üretilen çıktı:alpha bravo echo delta foxtrot golf hotel romeo zuluALPHA bravo delta ECHO foxtrot GOLF HOTEL romeo ZULU//

min( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- Type const& min(Type const& one, Type const& two);-- Type const& min(Type const& one, Type const& two,

Page 310: C++ 2 (Kitap - Selami KOÇLU)

Comparator comp);

Açıklamalar:-- Birinci öntip: Yineleyicilerin işaret ettiği ve aynı veri tipinin operator<( ) işleci kullanılarak one ve two dan dahak küçük olanı döner.-- İkinci öntip: comp(one, two) false döndürürse, ikinci öntipten onedöner, aksi taktirde two döner.

Örnek://:B05:kucuk.cpp//min( ) kullanımı #include <algorithm>#include <string>#include <iostream>class CaseString{ public: bool operator( )(std::string const& first, std::string const& second) const{ return (strcasecmp(second.c_str( ), first.c_str( ))>0); }};using namespace std;int main( ){ cout<<”Kelime ' “<<min(string(“first”), string(“second”))<< “ ' lexicographical olarak ilktir\n” ; cout<<”Kelime ' “<<min(string(“first”), string(“SECOND”))<< “ ' lexicographical olarak ilktir\n” ; cout<<”Kelime ' ”<<min(string(“first”), string(SECOND”), CaseString( ))<<” 'lexicographical olarak ilktir\n”; return 0;}///:~//Üretilen çıktı:Kelime 'first' lexicographical olarak ilktir.Kelime 'SECOND' lexicographical olarak ilktir.Kelime 'first' lexicographical olarak ilktir.//

min_element( )

Page 311: C++ 2 (Kitap - Selami KOÇLU)

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- ForwardIterator min_element(ForwardIterator first, ForwardIterator last);-- ForwardIterator min_element(ForwardIterator first, ForwardIterator last, Comparator comp);

Açıklamalar-- Birinci öntip: [first, last) alanındaki en küçük öğeyi işaret edenyineleyici döner. Veri tipinin operator<( ) işlevi yineleyici tarafından işaretedilir.-- İkinci öntip: Burada operator<( ) işlevinden ziyade, comp karşılaştırıcısıkullanılarak [first, last) yineleyici alanındaki öğeler karşılaştırılır. Comp iledönen öğe çoğu kez false (yanlış) döndürür.

Örnek://:B05:mineleman.cpp//küçük elemanı bulmak#include <iostream>#include <algorithm>class AbsValue{ public: bool operator( )(int first, int second) const{ return abs(first)<abs(second) ; }};using namespace std;int main( ){ int ia[]={-4, 7, -2, 10, -12}; cout<<”En kucuk int deger: “<<*min_element(ia, ia+5)<<endl; cout<<”Mutlak degeri en kucuk int : “<< *min_element(ia, ia+5, AbsValue( ))<<endl; return 0;}///:~//üretilen çıktı:En kucuk int deger: -12 Mutlak degeri en kucuk int:-2

Page 312: C++ 2 (Kitap - Selami KOÇLU)

//

mismatch( ) Başlık dosyası:#include <algorithm>

İşlev öntipleri: -- pair<InputIterator1, InputIterator2> mismatch(InputIterator first1, InputIterator1 last1, InputIterator2 first2) ;-- pair<InputIterator1, InputIterator2> mismatch(InputIterator first1, InputIterator1 last1, InputIterator2 first2, Compare comp);

Açıklamalar:-Birinci öntip: Yineleyicinin işaret ettiği veri tipinin özdeşlik işlecikullanılarak, iki dizinin öğeleri first1 ve first2 dan başlayarak karşılaştırılır.Karşılaştırılan öğeler farklı olduklarında veya, last1 öğesine ulaşıldığındakarşılaştırma işlemi durur. İkinci dizi birinciden daha fazla öğeye sahipolabilir. Eğer ikinci dizi birinciden daha az öğeye sahip olursa, o zamanalgoritmanın davranışı belirlenemez. -İkinci öntip: Burada operator==( ) yerine comp tarafından tanımlananikici (binary) karşılaştırma kullanılarak iki dizinin öğeleri first1 ve first2den başlayarak karşılaştırılır. Karşılaştırma işlemi, comp yanlış (false)döndürdüğünde veya last1 öğesine ulaşıldığında durur. Son yerleri işareteden yineleyicileri içeren bir pair döner. İkinci dizi birinciye göre dahafazla öğeye sahip olabilir. Eğer ikinci dizi birinciden daha az öğeye sahipolursa, o zaman algoritmanın davranışı belirlenemez.

Örnek:

//:B05:uymaz.cpp//mismatch algoritma kullanımı #include <algorithm>#include <string>#include <utility>#include <iostream>class CaseString{ public:

Page 313: C++ 2 (Kitap - Selami KOÇLU)

bool operator( )(std::string const& first, std::string const& second) const{ return strcasecmp(first.c_str( ), second.c_str( ))==0; }};using namespace std;int main( ){ string range1[]={“alpha”, “bravo”, “foxtrot”, “hotel”, “zulu”}; string range2[]={“alpha”, “bravo”, “foxtrot”, “Hotel”, “zulu”}; pair<string*, string*> pss=mismatch(range1, range1+5, range2); cout<<”The elements”<<*pss.first<<” and “<<*pss.second<< “at offset”<<(pss.first-range1)<<” differ\n”; if(mismatch(range1, range1+5, range2, CaseString( )).first== range+5) cout<<”When compared case insensitively they match\n” ; return 0;}///:~

//üretilen çıktı:The elements hotel and Hotel at offset 3 differWhen compared case insensitively they match//

next_permutation( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- bool next_permutation(BidirectionalIterator first, BidirectionalIterator last);-- bool next_permutation(BidirectionalIterator first, BidirectionalIterator last, Comp comp);

Açıklama:--Birinci öntip: [first, last) alanında yer alan öğeler dizisinin bir sonrakipermütasyonu (next_permutation( )) hesaplanır. Örnek olarak olaraköğeleri 1, 2, 3 olan dizide next_permutation( ) çağrıldığında;

Page 314: C++ 2 (Kitap - Selami KOÇLU)

123132213231312321

Örnekten gördüğünüz gibi, bir sonraki değer öncekine göre mutlak değerolarak daha büyüktür. (132 123 ten, 213 132 den vd.. büyüktür) Buradayineleyicinin işaret ettiği veri tipinin operator<( ) işleci kullanılmıştır.Öğeler sıralamasında yeniden bir düzenleme yapılmışsa o zaman doğru(true) değer döner. Yeniden bir düzenleme yapılmadığında ise yanlış(false) döner. Bu durum en son öğenin en büyük olduğunu gösterir. Bunabenzer düzenlemeler operator<( ) işleci de kullanılarak yapılabilir.

İkinci öntip: [first, last) alanında yer alan öğeler dizisinin bir sonrakipermütasyonu (next_permutation( )) hesaplanır. Alanda yer alan öğeleryeniden düzenlenir. Yeniden düzenleme yapılmışsa doğru (true) değerdöner, yok eğer yeniden düzenleme yapılmamışsa yanlış (false) döner.Bunun başka bir anlamı da dizi düzeninin doğru olarak tamamlandığıdır.Burada öğeleri karşılaştırmak için ikici (binary) kıstas comp kullanılır.

Örnek:

//:B05:birsonraki.cpp//next_permutation algoritmasının kullanımı #include <algorithm>#include <iostream>#include <string>#include <iterator>class CaseString{ public: bool operator( )(std::string const& first, std::string const& last)const{ return strcasecompare(first.c_str( ), second.c_str( ))<0 ; }};using namespace std;int main( ){

Page 315: C++ 2 (Kitap - Selami KOÇLU)

string saints[]={“Oh”, “when”, “the”, “saints”}; cout<<””oh when the saints” in bütün permutasyonlari: \n “; cout<<”Diziler:\n”; do{ copy(saints, saints+4, ostream_iterator<string>(cout, “ “)); cout<<endl; } while(next_permutation(saints, saints+4, CaseString( ))); cout<<”ilk siralamadan sonraki dizi:\n”; sort(saints, saints+4, CaseString( )); cout<<”Diziler:\n ”; do{ copy(saints, saints+4, ostream_iterator<string>(cout, “ “)); cout<<endl; } while(next_permutation(saints, saints+4, CaseString( ))); return 0;}///:~//üretilen çıktı: (bir kısmı verildi)“oh when the saints” in bütün permütasyonları:Diziler:Oh when the saintssaints Oh the when saints Oh when thesaints the Oh when... ilk siralamadan sonraki dizi:Diziler:Oh saints the whenOh saints when theOh the saints whenOh the when saints...

//

nth_element( )

Page 316: C++ 2 (Kitap - Selami KOÇLU)

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void n_th elementCHAR(40)RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator lastCHAR(41);-- void n_th elementCHAR(40)RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last, Compare compCHAR(41);

Açıklamalar:--Birinci öntip: [ilk(first), son(last)) alanındaki bütün öğeler n in işaretettiği öğeye göre sıralamaya konur. [sol(left), nth) alanındaki bütün öğelern nin işaret ettiği öğeden daha küçüktür. [nth+1, son(last)) alanındakibütün öğeler n nin işaret ettiği öğeden daha büyüktür. İki alt kümeninkendileri sıralamaya konmaz. Yineleyicinin işaret ettiği veri tipininoperator<( ) işleci kullanılır.-- İkinci öntip: [ilk(first), son(last)) alanındaki bütün öğeler n in işaretettiği öğeye göre sıralamaya konur. [sol(left), nth) alanındaki bütün öğelern nin işaret ettiği öğeden daha küçüktür. [nth+1, son(last)) alanındakibütün öğeler n nin işaret ettiği öğeden daha büyüktür. İki alt kümeninkendileri sıralamaya konmaz. Öğeleri karşılaştırmak için comp işlevnesnesi kullanılır.

Örnek:

//:B05:ninci.cpp//nth_element algoritmasının kullanımı #include <iostream>#include <algorithm>#include <functional>#include <iterator>using namespace std;int main( ){ int ia[]={1, 3, 5, 7, 9, 2, 4, 6, 8, 10}; cout<<ia[3]<<”e gore dizilme”<<endl; copy(ia, ia+10, ostream_iterator<int>(cout, “ “)); cout<<endl; nt_element(ia, ia+5, ia+10, greater<int>( )); cout<<ia[5]<<”e gore dizilme”<<endl;

Page 317: C++ 2 (Kitap - Selami KOÇLU)

copy(ia, ia+10, ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///://üretilen çıktı:4 e gore dizilme1 2 3 4 9 7 5 6 8 10 5 e gore dizilme10 8 7 9 6 5 3 4 2 1//

partial_sort( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void partial_sortCHAR(40)RandomAccessIterator first,RandomAccessIterator middle, RandomAccessIterator lastCHAR(41);-- void partial_sortCHAR(40)RandomAccessIterator first, RandomAccessIterator middle, RandomAccessIterator last, Compare compCHAR(41);

Açıklamalar--Birinci öntip: Yineleyicinin işaret ettiği veri tipinin operator<( ) işlecikullanılarak, orta (middle)- ilk (first) en küçük öğeleri sıralanarak [ilk(first), orta (middle)) alanında saklanır. Serilerin geri kalan öğelerisıralanmadan kalır ve [orta (middle), son (last)) alanında saklanır.-- İkinci öntip: Sağlanan ikici (binary) kıstas comp kullanılarak orta(middle) -ilk (first) en küçük öğeleri sıralanarak [ilk (first), orta (middle))alanında saklanır. Serilerin geri kalan öğeleri sırlanmadan kalır.

Örnek:

//:B05:parsıra.cpp//partial_sort( ) algoritmasının kullanımı#include <iostream>#include <algorithm>

Page 318: C++ 2 (Kitap - Selami KOÇLU)

#include <functional>#include <iterator>using namespace std;int main( ){ int ia[]={1, 3, 5, 7, 9, 2, 4, 6, 8, 10}; partial_sort(ia, ia+3, ia+10); cout<<”3 en kucuk ogeyi bul: \n”; copy(ia, ia+10, ostream_iterator<int>(cout, “ “)); cout<<endl; cout<<”5 en buyuk ogeyi bul: \n”; partial_sort(ia, ia+5, ia+10, greater<int>( )); cout<<endl; return 0;}///:~//üretilen çıktı:3 en kucuk ogeyi bul:1 2 3 7 9 5 4 6 8 105 en buyuk ogeyi bul:10 9 8 7 6 1 2 3 4 5//

partial_sort_copy( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void partial_sort_copyCHAR(40)InputIterator first, InputIterator last, RandomAccessIterator dest_first, RandomAccessIteratordest_lastCHAR(41) ;-- void partial_sort_copyCHAR(40)InputIterator first, InputIterator last, RandomAccessIterator dest_first, RandomAccessIterator dest_last, Compare compCHAR(41);

Açıklamalar:-- Birinci öntip: Yineleyicinin işaret ettiği veri tipinin operator<( ) işlecikullanılarak [ilk (first), son (last)) alanındaki en küçük öğeler [dest_first,dest_last) alanına kopyalanır. Sadece daha küçük değerler ikinci alana

Page 319: C++ 2 (Kitap - Selami KOÇLU)

kopyalanır.-- İkinci öntip: İkici (binary) kıstas comp kullanılarak [ilk (first), son (last))alanındaki öğeler sıraya dizilirler. Kıstas comp un doğru (true) değerdöndürdüğü öğeler, [dest_first, dest_last) alanına kopyalanır. Sadece dahaküçük değerli öğeler ikinci alana kopyalanır.

Örnek:

//:B05:kdizilikpy.cpp //partial_sort_copy( ) algoritma örneği#include <iostream>#include <algorithm>#include <functional>#include <iterator>using namespace std;int main( ){ int ia[]={1, 10, 3, 8, 5, 6, 7, 4, 9, 2}; int ia2[6] ; partial_sort_copy(ia, ia+10, ia2, ia2+6); copy(ia, ia+10, ostream_iterator<int>(cout, “ “ )); cout<<endl; cout<<”En kucuk 6 oge: ” ; copy(ia2, ia2+6, ostream_iterator<int>(cout, “ “)); cout<<endl; cout<<”Daha buyuk alana en kucuk 4 oge: \n“ ; partial_sort_copy(ia, ia+4, ia2, ia2+6) ; copy(ia2, ia2+6, ostream_iterator<int>(cout, “ “)); cout<<endl; cout<<”Daha buyuk alana 4 en buyuk oge: \n”; partial_sort_copy(ia, ia+4, ia2, ia2+6, greater<int>( )); copy(ia2, ia2+6, ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:1 10 3 8 5 6 7 4 9 2En kucuk 6 oge: 1 2 3 4 5 6Daha buyuk alana en kucuk 4 oge: 1 3 8 10 5 6Daha buyuk alana 4 en buyuk oge:

Page 320: C++ 2 (Kitap - Selami KOÇLU)

10 8 3 1 5 6//

partial_sum( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result) ;-- OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation op);

Açıklamalar:-- Birinci öntip: [result, <dönen OutputIterator>) alanındaki her öğenindeğeri [first, last) alanında denk gelen alandaki öğeleri ekleyerek eldeedilir. Netice alanındaki ilk öğe first tarafından işaret edilen öğeye eşittir.-- İkinci öntip: İkici (binary) işleç op, [result, <dönen OutputIterator>)alanındaki her öğenin değerini elde etmek için, netice alanındaki öncekiöğe ile, [first, last) alanındaki denk gelen öğeye uygulanır. Neticealanındaki ilk öğe first tarafından işaret edilen öğeye eşittir.

Örnek:

//:B05:ktoplama.cpp//partial_sum( ) algoritma örneği#include <iostream>#include <algorithm>#include <functional>#include <numeric>#include <iterator>using namespace std;int main( ){ int ia[]={1, 2, 3, 4, 5}; int ia2[5]; copy(ia2, partial_sum(ia, ia+5, ia2), ostream_iterator<int>(cout, “ “));

Page 321: C++ 2 (Kitap - Selami KOÇLU)

cout<<endl; copy(ia2, partial_sum(ia, ia+5, ia2, multiplies<int>( )), ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:1 3 6 10 151 2 6 24 120//

partition( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- BidirectionalIterator partition(BidirectionalIterator first, BidirectionalIterator last, UnaryPredicat pred);

Açıklama:-- pred tarafından doğru (true) olarak saptanmış olan [first, last) alanındakibütün öğeler yanlış (false) olarak saptanmış öğelerden önce yerleştirilir.Dönüş değeri, pred tarafından doğru (true) olarak bölmelenmiş olanalandaki son öğeden hemen sonrasını işaret eder.

Örnek:

//:B05:parcala.cpp//partition( ) algoritma örneği#include <iostream>#include <string>#include <algorithm>#include <iterator>class LessThan{ int d_x; public: LessThan(int x) : d_x( ){}

Page 322: C++ 2 (Kitap - Selami KOÇLU)

bool operator( )(int value){ return value<=d_x; }};using namespace std;int main( ){ int ia[ ]={1, 3, 5, 7, 9, 10, 2, 8, 6, 4}; int* split; split=partition(ia, ia+10, LessThan(ia[9])); cout<<”son oge <=4 ia[“<<split-ia-1<<”] dir. \n”; copy(ia, ia+10, ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:son oge <= 4 ia[3] dir.1, 3, 4, 2, 9, 10, 7, 8, 6, 5//

prev_permutation( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- bool prev_permutation(BidirectionalIterator first, BidirectionalIterator last);-- bool prev_permutation(BidirectionalIterator first, BidirectionalIterator last, Comp comp);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki öğeler dizisinin verilen öncekipermütasyonunu belirler. Alandaki öğeler, daha önce yapılan düzenlemedeelde edilen daha küçük öğeler dizilişinin tersine (bakınız next_permutation( )) sıralanır. Yeniden düzenlenirse, doğru (true) değeridöner. Yok eğer yeniden düzenleme olmazsa, o zaman yanlış (false) değeridöner. Son durum, yineleyicilerin işaret ettiği veri tipinin operator<( )işlecine göre, dizi zaten istenen sıralamada verilmiş demektir.

Page 323: C++ 2 (Kitap - Selami KOÇLU)

-- İkinci öntip: [first, last) alanındaki öğeler dizisinin verilen öncekipermütasyonunu belirler. Alandaki öğeler yeniden düzenlenir. Yenidendüzenleme olursa, o zaman doğru (true) değer döner. Yok eğer yenidendüzenleme olmazsa, o zaman yanlış (false) değeri döner. Son durumda,ikici (binary) kıstas comp iki öğeyi karşılaştırmak için kullanılır, ve bunagöre dizi zaten istenen sıralamada verilmiş demektir.

Örnek:

//:B05:onperm.cpp//prev_permutation( ) algoritma kullanımı#include <iostream>#include <string>#include <iterator>#include <algorithm>class CaseString{ public: bool operator( )(std::string const& first, std::string const& second)const { return strcasecmp(first.c_str( ), second.c_str( ))<0; }};using namespace std;int main( ){ string izmir[]={“Off”, “bulunmaz”, “bir” “daha”}; cout<<” 'Off bulunmaz bir daha' nin evvelki permutasyonlari:\n”; cout<<”Diziler:\n”; do{ copy(izmir, izmir+4, ostream_iterator<string>(cout, “ “)); cout<<endl; } while(prev_permutation(izmir, izmir+4, CaseString( ))); cout<<”Dizinin ilk siralama sonrasi:\n; sort(izmir, izmir+4, CaseString( )); cout<<”Diziler:\n”; while(prev_permutation(izmir, izmir+4, CaseString( ))){ copy(izmir, izmir+4, ostream_iterator<string>(cout, “ “)); cout<<endl; }

Page 324: C++ 2 (Kitap - Selami KOÇLU)

cout<<”daha prev_permutation yok\n”; return 0;}///:~//üretilen çıktı:'Off bulunmaz bir daha' nin evvelki permutasyonlari:Diziler:Off bulunmaz bir dahaOff bulunmaz daha bir Off bir bulunmaz dahaOff bir daha bulunmaz Off daha bulunmaz birOff daha bir bulunmazDizinin ilk siralama sonrasi:Diziler:daha prev_permutation yok//

random_shuffle( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void random_shuffle(RandomAccessIterator first, RandomAccessIterator last);-- void random_shuffleCHAR(40)RandomAccessIterator first, RandomAccessIterator last, RandomNumberGenerator randCHAR(41);

Açıklamalar:Birinci öntip: [first, last) alanındaki öğeler yeniden rastgele sıralanırlar.İkinci öntip: [first, last) alanındaki öğeler rand rastgele sayı üretecikullanılarak yeniden sıralanır. Bu [0, kalan (remaining)) alanında bir intdöndürür, kalan yani remaining ise rand işlev nesnesinin operator( )( ) üne değişken olarak aktarılır. Başka bir seçenekte; rastgelesayı üreteci, int remaining değişkeni bekleyen ve [0, remaining) alanındarastgele bir int değer döndüren işlev olarakta alınabilir. İşlev nesnesikullanıldığı zaman, bu nesne anonim nesne olamaz.

Page 325: C++ 2 (Kitap - Selami KOÇLU)

Örnek:

//:B05:rastgele.cpp//random_shuffle( ) algoritma kullanımı #include <iostream>#include <algorithm>#include <ctime>#include <string>#include <iterator>int randomValue(int remaining){ return static_cast<int>( ((0.0+remaining)*rand( ))/(RAND_MAX+1.0) );}class RandomGenerator{ public: RandomGenerator( ){ srand(time(0)); } int operator( )(int remaining) const{ return randomValue(remaining) ; }};void show(std::string* begin, std::string* end){ std::copy(begin, end, std::ostream_iterator<std::string>(std::cout, “ “)); std::cout<<std::endl<<std::endl;}using namespace std;int main( ){ string words[]={“selami”, “kamil”, “turkan”, “cevat”, “ilkan”, “damla”}; unsigned const size=sizeof(words)/sizeof(string) ; cout<<”Varsayilan rastgeleyi kullan:\n”; random_shuffle(words, words+size); show(words, words+size); cout<<”RandomGenerator kullan:\n”; RandomGenerator rg; random_shuffle(words, words+size, rg); show(words, words+size); srand(time(0)<<1);

Page 326: C++ 2 (Kitap - Selami KOÇLU)

cout<<”randomValue( ) islevini kullan:\n”; random_shuffle(words, words+size, randomValue); show(words, words+size); return 0;}///:~//üretilen çıktı:Varsayılan rastgeleyi kullan: kamil ilkan turkan cevat damla selamiRandomGenerator kullan:selami kamil damla ilkan turkan cevatrandomValue( ) kullan:turkan damla cevat selami ilkan kamil//

remove( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- ForwardIterator remove(ForwardIterator first, ForwardIterator last, Type& value);

Açıklama:-- Öntip: [first, last) alanında işaret edilen öğeler, value değerine eşitolmayanları alanın başlangıcında olmak üzere, yeniden sıralanırlar. DönenForwardIterator yeni sıralamadan sonra silinecek ilk öğeyi işaret eder.[return value, last) algoritmanın artık alanıdır. Artıklar arasında value denbaşka değerler de olabilir, ama bunlar güvenli bir şekilde silinebilir, aynı[first, return value) alanında olduğu gibi. İşlev, silinecek öğeleri işaret edenyineleyicilerin veri tipinin operator==( ) işlecini, kullanır.

Örnek://:B05:silin.cpp//remove( ) algoritmasının kullanımı#include <iostream>#include <string>#include <iterator>

Page 327: C++ 2 (Kitap - Selami KOÇLU)

#include <algorithm>using namespace std;int main( ){ string words[]={“kilo”, “alpha”, “lima”, “mike”, “alpha”, “november”, “alpha”, “oscar”, “alpha”, “alpha”, “papa”, “quebec”}; string* removed; unsigned const size=sizeof(words)/sizeof(string); cout<<”Hepsini sil \”alpha\”nin:\n”; removed=remove(words, words+size, “alpha”); copy(words, removed, ostream_iterator<string>(cout, “ “)); cout<<endl<<”Artik olan ogeler:\n”; copy(removed, words+size, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:Hepsini sil “alpha” nın:kilo lima mike november oscar papa quebec Artik olan ogeler:oscar alpha alpha papa quebec//

remove_copy( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- OutputIterator remove_copy(InputIterator first, InputIterator last, OutputIterator result, Type& value),

Açıklama:-- [first, last) tarafından işaret edilen öğelerin value ye eşit olmayanları,[result, returnvalue) alanına kopyalanır. returnvalue işlev tarafındandöndürülen değerdir. [first, last) alanı değiştirilmez. İşlev, yineleyicilerinişaret ettiği veri tipinin operator==( ) işlecini, kopyalanmayacak öğeleribelirlemekte kullanılır.

Page 328: C++ 2 (Kitap - Selami KOÇLU)

Örnek:

//:B05:kopyasil.cpp//remove_copy( ) algoritma kullanımı#include <iostream>#include <string>#include <algorithm>#include <iterator>#include <functional>using namespace std;int main( ){ string words[]={“kilo”, “alpha”, “lima”, “mike”, “alpha”, november”, “alpha”, oscar”, “alpha”, “alpha”, “papa”, “quebec”}; unsigned const size=sizeof(words)/sizeof(string); string remaining[size-count_if(words, words+size, bind2nd(equal_to<string>( ), string(“alpha”)))]; string* returnvalue=remove_copy(words, words+size, remaining, “alpha”); cout<<”Hepsini kaldir \”alpha\” nin: \n”; copy(remaining, returnvalue, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:Hepsini kaldir “alpha” nin kilo lima mike november oscar papa quebec//

remove_if( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- ForwardIterator remove_if(ForwardIterator first, ForwardIterator last, UnaryPredicate pred);

Page 329: C++ 2 (Kitap - Selami KOÇLU)

Açıklama:-- [first, last) alanındaki öğeler, tekyanlı kıstas pred in false hesaplananbütün değerleri alanın en başında yer alacak şekilde, yeniden sıralamayakonur. Dönen ileri yineleyici ilk öğeyi işaret eder, yeniden sıralama işlemiyapıldıktan sonra, pred için true döner. [dönen değer (returnvalue), last)alanı algoritmanın arta kalanıdır. Arta kalan kısımda, value den başkadeğerlerde olabilir, ama bunlar gayet güvenli bir şekilde silinebilir, [first, returnvalue) alanında olduğu gibi.

Örnek:

//:B05:sileger.cpp//remove_if( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <functional>#include <string>#include <iterator>using namespace std;int main( ){ string words[]={“kilo”, “alpha”, “lima”, “mike”, “alpha”, “november”, “alpha”, “oscar”, “alpha”, “alpha”, “papa”, “quebec”}; unsigned const size=sizeof(words)/sizeof(string); cout<<”Hepsini \”alpha\” larin sil :\n”; string* removed=remove_if(words, words+size, bind2nd(equal_to<string>( ), string(“alpha”))); copy(words, removed, ostream_iterator<string>(cout, “ “)); cout<<endl <<”Arkadaki ogeler:\n”; copy(removed, words+size, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:Hepsini “alpha” larin sil:kilo lima mike november oscar papa quebecArkadaki ogeler:oscar alpha alpha papa quebec

Page 330: C++ 2 (Kitap - Selami KOÇLU)

//

remove_copy_if( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- OutputIterator remove_copy_if(InputIterator first, InputIterator last, OutputIterator result, UnaryPredicate pred);

Açıklama:-- [first, last) alanındaki öğeler tekyanlı kıstas pred true döndürdüğünde[result, returnvalue) alanına kopyalanır, burada returnvalue işlev tarafındandöndürülen değerdir. [first, last) alanı değiştirilemez.

Örnek:

//B05:silkopeger.cpp//remove_copy_if( ) algoritma kullanımı#include <iostream>#include <string>#include <algorithm>#include <functional>#include <iterator>using namespace std; int main( ){ string words[]={“kilo”, “alpha”, “lima”, “mike”, “alpha”, “november”, “alpha”, “oscar”, “alpha”, “alpha”, “papa”, “quebec”}; unsigned const size=sizeof(words)/sizeof(string); string remaining[size-count_if(words, words+size, bind2nd(equal_to<string>( ), “alpha”))]; string* returnvalue=remove_copy_if(words, words+size, remaining, bind2nd(equal_to<string>( ), “alpha”)); cout<<”Hepsini sil \”alpha\” larin:\n”; copy(remaining, returnvalue, ostream_iterator<string>(cout, “ “)); cout<<endl;

Page 331: C++ 2 (Kitap - Selami KOÇLU)

return 0;}///:~//üretilen çıktı:Hepsini sil “alpha” larin:kilo lima mike november oscar papa quebec//

replace( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- ForwardIterator replace(ForwardIterator first, ForwardIterator last, Type& oldvalue, Type& newvalue);

Açıklamalar:-- [first, last) alanında yer alan oldvalue ye eşit bütün öğeler, newvalue ninbir kopyası ile yer değiştirir. Algoritma, yineleyicilerin işaret ettiği veritipinin operator==( ) işlecini kullanır.

Örnek:

//:B05:yerdeg.cpp//replace( ) algoritma kullanımı#include <algorithm>#include <string>#include <iostream>#include <iterator>using namespace std;int main( ){ string words[]={“kilo”, “”alpha”, “lima”, “mike”, “alpha”, “november”,“alpha”, “oscar”, “alpha”, “alpha”, “papa”, “quebec”}; unsigned const size=sizeof(words)/sizeof(string); replace(words, words+size, string(“alpha”), string(“ALPHA”)); copy(words, words+size, ostream_iterator<string>(cout, “ “)); cout<<endl;

Page 332: C++ 2 (Kitap - Selami KOÇLU)

return 0;}///:~//üretilen çıktı:kilo ALPHA lime mike ALPHA november ALPHA oscar ALPHAALPHA papa quebec//

replace_copy( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- OutputIterator replace_copy(InputIterator first, InputIterator last, OutputIterator result, Type& oldvalue, Type& newvalue);

Açıklamalar:-- [first, last) alanınca işaret edilen oldvalue ye eşit bütün öğeler, [result, returnvalue) yeni alanında newvalue nin bir kopyası ile yerdeğiştirirler. returnvalue işlevin geri dönüş değeridir. Algoritma,yineleyicilerin işaret ettiği veri tipinin operator==( ) işlecini kullanır.

Örnek:

//:B05:kopyadeg.cpp//replace_copy( ) algoritma kullanımı#include <iostream>#include <string>#include <algorithm>#include <iterator>using namespace std;int main( ){ string words[]={“kilo”, “alpha”, “lima”, “mike”, “alpha”, “november”,“alpha”, “oscar”, “alpha”, “alpha”, “papa”, “quebec”}; unsigned const size=sizeof(words)/sizeof(string); string remaining[size]; copy(remaining, replace_copy(words, words+size, remaining,

Page 333: C++ 2 (Kitap - Selami KOÇLU)

string(“alpha”), string(“ALPHA”)), ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:kilo ALPHA lima mike ALPHA november ALPHA oscar ALPHAALPHA papa quebec//

replace_if( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- ForwardIterator replace_if(ForwardIterator first, ForwardIterator last, UnaryPredicate pred, Type const& value);

Açıklamalar:-- [first, last) alanınca işaret edilen öğelerin, tekyanlı kıstas pred tarafındandoğru (true) olarak belirlenenleri newvalue (yeni değer) ile yer değiştirir.

Örnek:

//:B05:degeger.cpp//replace_if( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <functional>#include <iterator>using namespace std;int main( ){ string words[ ]={“kilo”, “alpha”, “lima”, “mike”, “alpha”, “november”, alpha”, “oscar”, “alpha”, “alpha”, “papa”, quebec}; unsigned const size=sizeof(words)/sizeof(string); replace_if(words, words+size,

Page 334: C++ 2 (Kitap - Selami KOÇLU)

bind1st(greater<string>( ), string(“mike”)), string(“ALPHA”)); copy(words, words+size, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:kilo ALPHA lima mike ALPHA november ALPHA oscar ALPHA ALPHA papa quebec//

replace_copy_if( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- OutputIterator replace_copy_if(ForwardIterator first, ForwardIterator last, OutputIterator result, UnaryPredicate pred, Typeconst& value);

--Açıklamalar:-- [first, last) tarafından işaret edilen öğeler [result, returvalue) alanınakopyalanır, returnvalue işlev tarafından döndürülen değerdir. Tekyanlıkıstas pred tarafından doğru (true) döndüren öğeler newvalue değeri ile yerdeğiştirir. [first, last) alanı değiştirilmez.

Örnek:

//:B05:kopyerde.cpp//replace_copy_if( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <functional>#include <iterator>using namespace std;int main( ){ string words[]={“kilo”, “alpha”, “lima”, “mike”, “alpha”,

Page 335: C++ 2 (Kitap - Selami KOÇLU)

“november”, “alpha”, “oscar”, “alpha”, “alpha”, “papa”, “quebec”}; unsigned const size=sizeof(words)/sizeof(string); string result[size]; replace_copy_if(words, words+size, result, bind1st(greater<string>( ), string(“mike”)), string(“ALPHA”)); copy(result, result+size, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çözüm:(dikkat hepsi tek satırda olacak!!. aşağıya sığmadı)ALPHA ALPHA ALPHA mike ALPHA november ALPHA oscar ALPHA ALPHA papa quebec//

reverse( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- void reverse(BidirectionalIterator first, BidirectionalIterator last);

Açıklama:-- [first, last) alanındaki öğeler tersten yazılır.

Örnek:

//:B05:tersten.cpp//reverse( ) algoritma kullanımı#include <algorithm>#include <iostream>#include <string>using namespace std;int main( ){ string line;

Page 336: C++ 2 (Kitap - Selami KOÇLU)

while(getline(cin, line)){ reverse(line.begin( ), line.end( )); cout<<line<<endl; } return 0;}///:~//bunu anlatmaya gerek yok. Satırı olduğu gibi tersten yazar

reverse_copy( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- OutputIterator reverse_copy(BidirectionalIterator first, BidirectionalIterator last, OutputIterator result);

Açıklamalar:-- [first, last) alanındaki öğeler [result, returnvalue) alanına ters sıralamadakopyalanır. returnvalue değeri işlevden dönen değerdir.

Örnek:

//:B05:terskopya.cpp//reverse_copy( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>using namespace std;int main( ){ string line; while(getline(cin, line)){ unsigned size=line.size( ); char copy[size+1]; cout<<”line: “<<line<<endl<<”reversed: “; reverse_copy(line.begin( ), line.end( ), copy); copy[size]=0; // 0 terslenmiş satırın bir parçası değil

Page 337: C++ 2 (Kitap - Selami KOÇLU)

cout<<copy<<endl; } return 0;}///:~//bunu da açıklamaya gerek yok

rotate( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- void rotate(ForwardIterator first, ForwardIterator middle, ForwardIterator last);

Açıklamalar:-- [first, middle) alanındaki öğeler kabın sonuna, [middle, last) alanındakiöğeler kabın başlangıç bölümüne aktarılır. Öğe sıralaması iki altkümebirbirini bozmadan düzenlenir.

Örnek:

//:B05:turla.cpp//rotate( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <iterator>using namespace std;int main( ){ string words[]={“kilo”, “lima”, “mike”, “november”, “oscar”, “papa”, echo”, “foxtrot”, “golf”, “hotel”, “india”, “juliet”}; unsigned const size=sizeof(words)/sizeof(string); unsigned const midsize=6; rotate(words, words+midsize, words+size); copy(words, words+size, ostream_iterator<string>(cout, “ “)); cout<<endl;

Page 338: C++ 2 (Kitap - Selami KOÇLU)

return 0;}///:~//üretilen çıktı:echo foxtrot golf hotel india juliet kilo lima mike november oscar papa//

rotate_copy( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- OutputIterator rotate_copy(ForwardIterator first, ForwardIterator middle, ForwardIterator last, OutputIterator result);

Açıklamalar:-- Önce [middle, last) öğeleri, daha sonra da [first, middle) öğeleri [result, returnvalue) alanına sahip varış kabına kopyalanırlar. Burada returnvalue işlev tarafından döndürülen yineleyicidir. İki alt kümede yer alan öğelerin sıralaması değiştirilemez.

Örnek:

//:B05:turukop.cpp//rotate_copy( ) algoritma kullanımı#include <algorithm>#include <iostream>#include <string>#include <iterator>using namespace std;int main( ){ string words[]={“kilo”,”lima”, “mike”, “november”, “oscar”, “papa”, “echo”, ”foxtrot”, “golf”, “hotel”, “india”, “juliet”}; unsigned const size=sizeof(words)/sizeof(string); unsigned midsize=6; string out[size]; copy(out, rotate_copy(words, words+midsize, words+size, out), ostream_iterator<string>(cout, “ “));

Page 339: C++ 2 (Kitap - Selami KOÇLU)

cout<<endl; return 0;}///:~//üretilen çıktı:echo foxtrot golf hotel india juliet kilo lima mike november oscar papa//

search( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2);-- ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1, ForwardIterator2 first2, ForwardIterator2 last2, BinaryPredicate pred);

Açıklamalar:-- Birinci öntip: Yineleyicilerin işaret ettiği veri tipinin operator==( ) işleci kullanılarak [first2, last2) alanındaki öğelerin saptandığı yerde, [first1, last1) alanına bir yineleyici döndürülür. Böyle bir yer bulunamazsa last1 döner.-- İkinci öntip: Her iki alandaki öğeleri karşılaştırmak için ikici (binary) kıstas pred kullanılarak [first2, last2) alanındaki öğelerin saptandığı yerde, [first1, last1) alanına bir yineleyici döndürülür. Böyle bir yer bulunamazsa last1 döner.

Örnek://:B05:ara.cpp//search( ) algoritma kullanımı#include <algorithm>#include <iterator>#include <iostream>class absInt{ public:

Page 340: C++ 2 (Kitap - Selami KOÇLU)

bool operator( )(int i1, int i2){ return abs(i1)==abs(i2); }};using namespace std;int main( ){ int range1[]={-2, -4, -6, -8, 2, 4, 6, 8}; int range2[]={6, 8}; copy(search(range1, range1+8, range2, range2+2), range1+8, ostream_iterator<int>(cout, “ “)); cout<<endl; copy(search(range1, range1+8, range2, range2+2, absInt( )), range1+8, ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:6 8-6 -8 2 4 6 8//

search_n( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1, Size count, Type const& value);-- ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1, Size count, Type const& value, BinaryPredicate pred);

Açıklamalar:-- Birinci öntip: Öğeleri karşılaştırmak için kullanılan yineleyicilerin veritipinin operator==( ) işleci kullanılır. value değerine sahip n tane öğenin bulunduğu yerde, [first1, last1) ilk alana bir yineleyici döner.Böyle bir yer yoksa o zaman last1 döner.

Page 341: C++ 2 (Kitap - Selami KOÇLU)

-- İkinci öntip: Öğeleri karşılaştırmak için verilen ikici (binary) kıstas predkullanılır. value değerine sahip n tane öğenin bulunduğu yerde, [first1, last1) ilk alana bir yineleyici döner. Böyle bir yer yoksa last1 döner.

Örnek:

//:B05:nara.cpp//search_n( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <iterator> class absInt{ public: bool operator( )(int i1, int i2){ return abs(i1)==abs(i2); }};using namespace std;int main( ){ int range1[]={-2, -4, -4, -6, -8, 2, 4, 4, 6, 8}; int range2[]={6, 8}; copy(search_n(range1, range1+8, 2, 4), range1+8, ostream_iterator<int>(cout, “ “)); cout<<endl; copy(search_n(range1, range1+8, 2, 4, absInt( )), range1+8, ostream_iterator<int>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:4 4-4 -4 -6 -8 2 4 4//

set_difference( )

Başlık dosyası:

Page 342: C++ 2 (Kitap - Selami KOÇLU)

#include algorithm>

İşlev öntipleri:-- OutputIterator set_difference(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);-- OutputIterator set_difference(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);

Açıklamalar:-- Birinci öntip: [first2, last2) alanında bulunmayan [first1, last1)alanındaki sıralı öğeler dizisi döner, bu result ta başlar işlev tarafındandöndürülen OutputIterator da biter. Her iki alandaki öğeler, yineleyicininişaret ettiği veri tipinin operator<( ) işleci kullanılarak sıraya dizilmekzorundadır.--İkinci öntip: [first2, last2) alanında bulunmayan [first1, last1) alanındaki sıralı öğeler dizisi döner, bu result ta başlar işlev tarafındandöndürülen OutputIterator da biter. Her iki alandaki öğeler comp işlevnesnesi kullanılarak sıraya dizilmek zorundadır.

Örnek:

//:B05:farkayar.cpp//set_difference( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <iterator>class CaseLess{ public: bool operator( )(std::string const& left, std::string const& right){ return strcasecmp(left.c_str( ), right.c_str( ))<0; }};using namespace std;int main( ){ string set1[]={“kilo”, “lima”, “mike”, “november”, “oscar”, “papa”, quebec”}; string set2[]={“papa”, “quebec”, “romeo”}; string result[7];

Page 343: C++ 2 (Kitap - Selami KOÇLU)

string* returned; copy(result, set_difference(set1, set1+7, set2, set2+3, result), ostream_iterator<string>(cout, “ “)); cout<<endl; string set3[]={“PAPA”, “QUEBEC”, “ROMEO”}; copy(result, set_difference(set1, set1+7, set3, set3+3, result, CaseLess( )), ostream_iterator<string(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:kilo lima mike november oscarkilo lima mike november oscar//

set_intersection( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- OutputIterator set_intersection(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);-- OutputIterator set_intersection(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);

Açıklamalr:-- Birinci öntip: [first1, last1) alanınca işaret edilen ve ayrıca [first2, last2)alanında da bulunan öğelerin sıralı dizisi geri döner. Bu result ta başlar,işlev tarafından döndürülen OutputIterator de biter. Her iki alanın öğeleri,yineleyicilerin işaret ettiği veri tipinin operator<( ) işleci kullanılaraksıralanmak zorundadır.-- İkinci öntip: [first1, last1) alanınca işaret edilen ve ayrıca [first2, last2) alanında da bulunan öğelerin sıralı dizisi geri döner. Bu result ta başlar, işlev tarafından döndürülen OutpuIterator da biter. Her iki alanın öğeleri,

Page 344: C++ 2 (Kitap - Selami KOÇLU)

comp işlev nesnesi kullanılarak sıralanmak zorundadır.

Örnek:

//:B05:kesimay.cpp//set_intersection( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <iterator>class CaseLess{ public: bool operator( )(std::string const& left, std::string const& right){ return strcasecmp(left.c_str( ), right.c_str( ))<0; }};using namespace std;int main( ){ string set1[]={“kilo”, “lima”, “mike”, “november”, “oscar”, “papa”, quebec”}; string set2[]={“papa”, “quebec”, “romeo”}; string result[7]; string* returned; copy(result, set_intersection(set1, set1+7, set2, set2+3, result), ostream_iterator<string>(cout, “ “)); cout<<endl; string set3[]={“PAPA”, “QUEBEC”, “ROMEO”}; copy(result, set_intersection(set1, set1+7, set3, set3+3, result, CaseLess( )), ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:papa quebecpapa quebec//

set_symmetric_difference( )

Page 345: C++ 2 (Kitap - Selami KOÇLU)

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- OutputIterator set_symmetric_difference(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result),-- OutputIterator set_symmetric_difference(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);

Açıklamalar:-- Birinci öntip: [first2, last2) alanında bulunmayıp [first1, last1) alanıncagösterilen ve [first1, last1) alanında bulunmayıp [first2, last2) alanıncagösterilen öğelerin sıralı dizisi döner. Bu result ta başlar, işlev tarafındandöndürülen OutputIterator da biter. Her iki alandaki, öğeler yineleyicilerinişaret ettiği veri tipinin, operator<( ) işleci kullanılarak sıralanmakzorundadır.-- İkinci öntip: [first2, last2) alanında bulunmayıp [first1, last1) alnıncagösterilen ve [first1, last1) alnında bulunmayıp [first2, last2) alanıncagösterilen öğelerin sıralı dizisi döner. Bu result ta başlar, işlev tarafındandöndürülen OutputIterator da biter. Her iki alandaki öğeler comp işlevnesnesi kullanılarak sıralanmak zorundadır.

Örnek;

//:B05:aybakfark.cpp//set_symmetric_difference( ) algoritma kullanımı#include <iostream>#include <string>#include <algorithm>#include <iterator>class CaseLess{ public: bool operator( )(std::string const& left, std::string const& right){ return strcasecmp(left.c_str( ), right.c_str( ))<0; }};using namespace std;

Page 346: C++ 2 (Kitap - Selami KOÇLU)

int main( ){ string set1[]={“kilo”, “lima”, “mike”, “november”, “oscar”, “papa”, quebec”}; string set2[]={“papa”, “quebec”, “romeo”}; string result[7]; string* returned; copy(result, set_symmetric_difference(set1, set1+7, set2, set2+3, result, ostream_iterator<string>(cout, “ “)); cout<<endl; string set3[ ]={“PAPA”, “QUEBEC”, “ROMEO”}; copy(result, set_symmetric_difference(set1, set1+7, set3, set3+3, result, CaseLess( )), ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:kilo lima mike november oscar romeokilo lima mike november oscar ROMEO//

set_union( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- OutputIterator set_union(InputIterator first1, InputIterator last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result);-- OutputIterator set_union(InputIterator first1, InputIterator last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result, Compare comp);

Açıklamalar:-- Birinci öntip: [first1, last1) alanınca işaret edilen ve ayrıca [first2, last2)alanında da bulunan öğelerin sıralı dizisi döner. Bu result ta başlar ve işlevtarafından döndürülen OutputIterator da biter. Her iki alandaki öğeleryineleyicilerin işaret ettiği veri tipinin operator<( ) işleci kullanılaraksıralanmak zorundadır.

Page 347: C++ 2 (Kitap - Selami KOÇLU)

-- İkinci öntip: [first1, last1) alanınca işaret edilen ve ayrıca [first2, last2)alanında da bulunan öğelerin sıralı dizisi döner. Bu result ta başlar, ve işlev tarafından döndürülen OutputIterator da biter. Her iki alandaki öğeler comp işlev nesnesi kullanılarak sıralanmak zorundadır.

Örnek:

//:B05:birlikkur.cpp//set_union( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <iterator>class CaseLess{ public: bool operator( )(std::string const& left, std::string const& right){ return strcasecmp(left.c_str( ), right.c_str( ))<0; }};using namespace std;int main( ){ string set1[]={“kilo”, lima”, “mike”, “november”, “oscar”, “papa”, “quebec”}; string set2[]={“papa”, “quebec”, “romeo”}; string result[7]; string* returned; copy(result, set_union(set1, set1+7, set2, set2+3, result), ostream_iterator<string>(cout, “ “)); cout<<endl; string set3[]={“PAPA”, “QUEBEC”, “ROMEO”}; copy(result, set_union(set1, set1+7, set3, set3+3, result, CaseLess( )), ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:kilo lima mike november oscar papa quebec romeokilo lima mike november oscar papa quebec ROMEO//

Page 348: C++ 2 (Kitap - Selami KOÇLU)

sort( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void sort(RandomAccessIterator first, RandomAccessIterator last);-- void sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);

Açıklamalar:--Birinci öntip: [first, last) alanındaki öğeler yineleyicilerin işaret ettiği veritipinin operator<( ) işleci kullanılarak artarak sıralanır.-- İkinci öntip: [first, last) alanındaki öğeler comp işlev nesnesikullanılarak karşılaştılırlar ve değerleri artarak sıralanırlar. Sıralı dizide ilkdeğişken ikinci değişkenden önce yer alırsa, o zaman ikici (binary) kıstascomp doğru (true) değer döndürür.

Örnek:

//:B05:sirala.cpp //sort( ) algoritma kullanımı#include <iostream>#include <string>#include <algorithm>#include <iterator>#include <functional>using namespace std;int main( ){ string words[]={“november”, “kilo”, “mike”, “lima”, “oscar”, “quebec”, “papa”}; sort(words, words+7); copy(words, words+7, ostream_iterator<string>(cout, “ “)); cout<<endl; sort(words, words+7, greater<string>( )); copy(words, words+7, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;

Page 349: C++ 2 (Kitap - Selami KOÇLU)

}///:~//üretilen çıktı:kilo lima mike november oscar papa quebecquebec papa oscar november mike lima kilo//

stable_partition( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- BidirectionalIterator stable_partition(BidirectionalIterator first, BidirectionalIterator last, UnaryPredicate pred);

Açıklamalar:-- Tekyanlı kıstas pred in [first, last) alanında doğru (true) hesapladığıöğelerin hepsi, yanlış (false) hesaplanan öğelerden önce yerleştirilir. Kaptayer alan eşit öğelerin göreceli konumu korunur. Dönüş değeri predtarafından doğru (true) hesaplanmış bölmelenmiş alandaki son öğedenötesini işaret eder. Örnek:

//:B05:kararpay.cpp//stable_partition( ) algoritma kullanımı#include <iostream>#include <functional>#include <algorithm> #include <string>#include <iterator>using namespace std;int main( ){ int org[]={1, 3, 5, 7, 9, 10, 2, 8, 6, 4}; int ia[10]; int* split; copy(org, org+10, ia); split=partition(ia, ia+10, bind2nd(less_equal<int>( ), ia[9]));

Page 350: C++ 2 (Kitap - Selami KOÇLU)

cout<<”Son oge<=4 : ia[“<<split-ia-1<< ”]\n”; copy(ia, ia+10, ostream_iterator<int>(cout, “ “ )); cout<<endl; copy(org, org+10, ia); split=stable_partition(ia, ia+10, bind2nd(less_equal<int>( ), ia[9])); cout<<”Son oge<=4 : ia[“<<split-ia-1<< ”]\n”; copy(ia, ia+10, ostream_iterator<int(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:Son oge<=4 : ia[3]1 3 4 2 9 10 7 8 6 5Son oge<=4 : ia[3]1 3 2 4 5 7 9 10 8 6//

stable_sort( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void stable_sort(RandomAccessIterator first, RandomAccessIterator last);-- void stable_sort(RandomAccessIterator first, RandomAccessIterator Last, Compare comp);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki öğeler yineleyicilerin işaret ettiğiveri tipinin operator<( ) işleci kullanılarak artan şekilde kararlı olaraksıralanırlar. Aynı öğelerin biribirlerine göre yerleri korunur.-- İkinci öntip: [first, last) alanındaki öğeler ikici (binary) kıstas compkullanılarak aratan şekilde kararlı olarak sıralanırlar. Bu kıstas, sıralanmışöğe kümesinde ilk değişken ikinci değişkenden önce yerleştirilmişse,doğru (true) değer döndürmesi lazımdır.

Örnek:

Page 351: C++ 2 (Kitap - Selami KOÇLU)

//:B05:karsira.cpp//stable_sort( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <functional>#include <vector>#include <iterator>typedef sdt::pair<std::string, std::string> pss ; //metne baknamespace sdt{ ostream& operator<<(ostream& out, pss const& p){ return out(<<” “<<p.first<<” “<<p.second<<endl; }}class sortby{ std::string pss::*d_field; public: sortby(std::string pss::*field) : d_field(field){ } bool operator( )(pss const& p1, pss const& p2) const{ return p1.*d_field<p2.*d_field; }};using namespace std;int main( ){ vector<pss> namecity; namecity.push_back(pss(“Hampson”, “Godalming”)); namecity.push_back(pss(“Moran”, “Eugene”)); namecity.push_back(pss(“Goldberg”, “Eugene”)); namecity.push_back(pss(“Moran”, “Godalming”)); namecity.push_back(pss(“Goldberg”, “Chicago”)); namecity.push_back(pss(“Hampson”, “Eugene”)); sort(namecity.begin( ), namecity.end( ), sortby(&pss::first)); //6 cout<<”isimleri siralanmis: \n”; copy(namecity.begin( ), namecity.end( ), ostream_iterator<pss>(cout)); //7 stable_sort(namecity.begin( ), namecity.end( ), sortby(&pss::second));

Page 352: C++ 2 (Kitap - Selami KOÇLU)

cout<<”sirali illerde isimlerin siralanmasi: \n”; copy(namecity.begin( ), namecity.end( ), ostream_iterator<pss>(cout)); return 0;}///:~//üretilen çıktı:isimleri siralanmis:Goldberg EugeneGoldberg ChicagoHampson GodalmingHampson EugeneMoran EugeneMoran Godalmingsirali illerde isimlerin siralanmasi:Goldberg ChicagoGoldberg EugeneHampson EugeneMoran EugeneHampson GodalmingMoran Godalming//

Örneğimiz çok sık rastlanılan bir soruna çözüm üretmektedir: sıralamayıbirden fazla hiyerarşik kıstas uygulayarak yapmak. Örneğimiz ayrıca bazıdikkat çekici özellikler de taşımaktadır:

1- İlki; typedef kullanarak pair<string, string> tekrarları engellenmiş vedağınıklıktan da kurtulunmuş oldu.2- İkincisi; operator<<( ) bindirilmiş işleci sayesinde bir pair in ostreamnesnesine yerleştirilmesi sağlandı. Bu hayatı oldukça kolaylaştıran birservis işlevi idi. Bir şeyi daha belirtelim bu işlev std isim alanında. Eğerstd isim alanına konmamış olsaydı, kullanılamazdı. Ostream inoperator<<( ) işleci mutlaka std isim alanında bulunmalıdır. 3- Tanımı verilmiş olan sortby sınıfı anonim nesne oluşturmaya ve bu dasıralamada kullanılacak olan pair veri üyelerinin birini işaret edengöstergeç alır. Bu durumda her iki üye string nesnedir. Yapıcı işlevkolaylıkla tanımlanabilir; değişkeni, pair<string, string> sınıfının stringüyesine göstergeçtir.4- operator( )( ) üyesi iki pair dayancı alır, ve daha sonra üyeleregöstergeci kullanır, pair in uygun alanlarını karşılaştırmak için sortedby

Page 353: C++ 2 (Kitap - Selami KOÇLU)

nesnesinde saklanır. 5- main( ) işlevinde bazı ilk veriler vector de saklanır.6- Daha sonra ilk sıralama yapılır. En az önemli kıstas öncesıralanmalıdır, ve bunun için basit sort( ) işlevi yeterli gelir. Mademkişehirlerin isimlerini sıralamak istiyoruz, o zaman isimler en az önemlikıstası yerine getirmelidir; bu yüzden isimleri sortby(&pss::first)kullanarak sıralıyoruz.7- Bir sonraki önemli kıstas; şehirleri sıralamaktır. İsimlerin birbirinegöre düzeni stable_sort( ) tarafından hiçbir şekilde bozulmayacağı için ,şehirler sıralanırken görülen bağlantılar varolan göreceli düzenlemebozulmadan çözümlenir. Böylece üretilen çıktının ikinci yarısındakisonuçlar elde edilir. Şehirleri sıralamak için ikinci anonim sortbynesnesini kullanırız: sortby(&pss::second).

swap( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- void swap(Type& object1, Type& object2);

Açıklama:-- object1 ile object2 öğelerinin değerleri yerdeğiştirir.

Örnek:

//:B05:yerdeg.cpp//swap( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <string>#include <iterator>using namespace std;int main( ){

Page 354: C++ 2 (Kitap - Selami KOÇLU)

string first[]={“alpha”, “bravo”, “charley”}; string second[]={“echo”, “foxtrot”, “golf”}; unsigned const n=sizeof(first)/sizeof(string); cout<<”once: \”; copy(first, first+n, ostream_iterator<string>(cout, “ “ )); cout<<endl; copy(second, second+n, ostream_iterator<string>(cout, “ “)); cout<<endl; for(unsigned idx=0; idx<n; ++idx) swap(first[idx, second[idx]); cout<<”sonra: \n”; copy(first, first+n, ostream_iterator<string>(cout, “ “)); cout<<endl; copy(second, second+n, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:once:alpha bravo charleyecho foxtrot golfsonra:echo foxtrot golfalpha bravo charley//

swap_ranges( )

Başlık dosyası:#include <algorithm>

İşlev öntipi:-- ForwardIterator2 swap_ranges(ForwardIterator1 first, ForwardIterator1 last, ForwardIterator2 result);

Açıklama:-- [first1, last1) alanındaki öğeler [result, returnvalue) alanındaki öğelerleyer değiştirir. returnvalue işlevden dönen değerdir. Heriki alan birbirinden

Page 355: C++ 2 (Kitap - Selami KOÇLU)

tamamen ayrıktır.

Örnek:

//:B05:alandeg.cpp//swap_ranges( ) algoritma kullanımı#include <algorithm>#include <iostream>#include <string>#include <iterator>using namespace std;int main( ){ string first[]={“alpha”, “bravo”, “charley”}; string second[]={“echo”, “foxtrot”, “golf”}; unsigned const n=sizeof(first)/sizeof(string); cout<<”once: \n”; copy(first, first+n, ostream_iterator<string>(cout, “ “)); cout<<endl; copy(second, second+n, ostream_iterator<string>(cout, “ “ )); cout<<endl; swap_ranges(first, first+n, second); cout<<”sonra: \n”; copy(first, first+n, ostream_iterator<string>(cout, “ “)); cout<<endl; copy(second, second+n, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0; }///:~//üretilen çıktı:once:alpha bravo charleyecho foxtrot golfsonra:echo foxtrot golfalpha bravo charley//

transform( )

Page 356: C++ 2 (Kitap - Selami KOÇLU)

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- OutpuIterator transform(InputIterator first, InputIterator last, OutputIterator result, UnaryOperator op);-- OutputIterator transform(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, BinaryOperator op);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki her öğeye tekyanlı işleç opuygulanır, sonuçlar result tan başlayan alana saklanır. Dönen değer, sonüretilen öğenin hemen sonrasını işaret eder.-- İkinci öntip: [first1, last1) alanındaki her öğeye ve denk gelen first2 denbaşlayan alanın öğelerine ikici (binary) işleç op uygulanır. Sonuç değerlerresult tan başlayan alana saklanır. Dönen değer, son üretilen öğenin hemensonrasını işaret eder.

Örnek:

//:B05:donus.cpp//transform( ) algoritma kullanımı#include <iostream>#include <string>#include <algorithm>#include <vector>#include <functional>#include <cctype>#include <iterator>class Caps{ public: std::string operator( )(std::string const& src){ std::string tmp=src; transform(tmp.begin( ), tmp.end(), tmp.begin( ), toupper); return tmp; }};using namespace std;int main( ){

Page 357: C++ 2 (Kitap - Selami KOÇLU)

string words[]={“alpha”, “bravo”, “charley”}; copy(words, transform(words, words+3, words, Caps( )), ostream_iterator<string>(cout, “ “)); cout<<endl; int values[]={1 2 3 4 5}; vector<int> squares; transform(values, values+5, values, back_inserter(squares), multiplies<int>( )); copy(squares.begin( ), squares.end( ), ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:ALPHA BRAVO CHARLEY1 4 9 16 25//

Daha önce incelenen for_each( ) ile transform( ) algoritmaları arasındakifarklar:1- transform( ) ile işlev nesnesinin operator( )( ) üyesinin dönüş değerikullanılır: operator( )( ) üyesine aktarılan değişkenin kendisi değişmez.2- for_each( ) ile işlev nesnesi operator( )( ) ü değişkenine bir dayanç alır,işlev nesnesinin operator( )( ) ü tarafından kendisi değiştirilebilir.

unique( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri: -- ForwardIterator unique(ForwardIterator first, ForwardIterator last);-- ForwardIterator unique(ForwardIterator first, ForwardIterator last, BinaryPredicate pred);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki birbirine eşit olan ardışık öğeler teköğeye indirgenir. (yineleyicilerin işaret ettiği veri tipinin operator==( )

Page 358: C++ 2 (Kitap - Selami KOÇLU)

işleci kullanılır). Dönen ileri yineleyici algoritmanın artanını işaretler, veunique( ) işlevi çağrılmadan önceki, alanda tek olarak bulunanları tutar.-- İkinci öntip: [first, last) alanındaki ikici (binary) karşılaştırma unsurupred in doğru (true) döndürdüğü ardışık öğeler tek öğeye indirgenir.Dönen ileri yineleyici algoritmanın artanını işaretler, ve unique( ) işleviçağrılmadan önceki, alanda tek olarak bulunanları tutar.

Örnek:

//:B05:tele.cpp//unique( ) algoritma kullanımı#include <algorithm>#include <iostream>#include <string >#include <iterator>class CaseString{ public: bool operator( )(std::string const& first, std:.string const& second) const{ return !strcasecmp(first.c_str( ), second.c_str( )); }};using namespace std;int main( ){ string words[]={“alpha”, “alpha”, “ALPHA”, “papa”, “quebec”}; unsigned const size=siezof(words)/sizeof(string); string* removed=unique(words, words+size); copy(words, removed, ostream_iterator<string>(cout, “ “)); cout<<endl; <<”arkadaki ogeler: \n”; copy(removed, words+size, ostream_iterator<string>(cout, “ “)); cout<<endl; removed=unique(words, words+size, CaseString( )); copy(words, removed, ostream_iterator<string>(cout, “ “)); cout<<endl; <<”arkadaki ogeler: \n”; copy(removed, words+size, ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~

Page 359: C++ 2 (Kitap - Selami KOÇLU)

//üretilen çıktı:alpha ALPHA papa quebecarkadaki ogeler:quebecalpha papa quebecarkadaki ogeler:quebec quebec//

unique_copy( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- OutputIterator unique_copy(InputIterator first, InputIterator last, OutputIterator result);-- OutputIterator unique_copy(InputIterator first, InputIterator last, OutputIterator result, BinaryPredicate pred);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki bütün öğeler result tan başlayaraksonuç kabına kopyalanır. Aynı ardışık öğeler (yineleyicinin işaret ettiğiveri tipinin operator==( )işleci kullanılarak) sadece bir defa kopyalanır.Çıktı yineleyicisinin dönen değeri son kopyalanan öğenin hemen sonrasınıişaret eder.-- İkincin öntip: [first, last) alanındaki bütün öğeler result tan başlayaraksonuç kabına kopyalanır. [first, last) alanında ikici (binary) karşılaştırmaunsuru pred in doğru (true) döndürdüğü ardışık öğeler sadece bir defakopyalanır. Çıktı yineleyicisinin dönen değeri son kopyalanan öğeninhemen sonrasını işaret eder.

Örnek:

//:B05:tekkopya.cpp//unique_copy( ) algoritma kullanımı#include <iostream>#include <string>

Page 360: C++ 2 (Kitap - Selami KOÇLU)

#include <algorithm>#include <vector>#include <iterator>#include <functional>class CaseString{ public: bool operator( )(std::string const& first, std::string second&second) const{ return !strcasecmp(first.c_str( ), second.c_str( )); }};using namespace std;int main( ){ string words[]={“oscar”, “Alpha”, “alpha”, “alpha”, “papa”, “quebec”}; unsigned const size=sizeof(words)/sizeof(string); vector<string> remaining; unique_copy(words, words+size, back_inserter(remaining)); copy(remaining.begin( ), remaining.end( ), ostream_iterator<string>(cout, “ “)); cout<<endl; vector<string> remaining2; unique_copy(words, words+size, back_inserter(remaining2), CaseString( )); copy(remaining2.begin( ), remaining2.end( ), ostream_iterator<string>(cout, “ “)); cout<<endl; return 0;}///:~//üretilen çıktı:oscar Alpha alpha papa quebecoscar Alpha papa quebec//

upper_bound( )

Başlık dosyası:

Page 361: C++ 2 (Kitap - Selami KOÇLU)

#include <algorithm>

İşlev öntipleri:-- ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, Type const& value);-- ForwardIterator upper_bound(ForwardIterator first, ForwardIterator last, Type const& value, Compare comp);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki sıralı öğeler value den büyük ilköğeye rastlanana kadar araştırılır. Yineleyici, sıralı öğelerin düzenibozulmaksızın value nin yerleştirilebildiği yeri göstermek için döndüğüyerle işaret eder. Böyle bir öğe yoksa last döner.-- İkinci öntip: [first, last) alanındaki öğeler comp işlevi veya işlev nesnesikullanılarak sıraya dizilir. Alandaki her öğe value ile, comp işlevikullanılarak karşılaştırılır. İkici (binary) kıstas pred in, doğru (true) değerdöndüren alandaki öğeler ve value değerine uygulanması ile, ilk öğeyineleyicisi döner. Böyle bir öğe yoksa last döner.

Örnek:

//:B05:ustsinir.cpp//upper_bound( ) algoritma kullanımı#include <iostream>#include <algorithm>#include <functional>#include <iterator>using namespace std;int main( ){ int ia[ ]={10, 15, 15, 20, 30}; unsigned n=sizeof(ia)/sizeof(int); cout<<”Dizi: \n”; copy(ia, ia+n, ostream_iterator<int>(cout, “ “)); cout<<endl;cout<<”15 daha once yerlesebilir”<< *upper_bound(ia, ia+n, 15)<<endl; cout<<”35 daha sonra yerlesebilir”<< *upper_bound(ia, ia+n, 35)==ia+n ? “son ogeden” : “???”)<<endl; sort(ia, ia+n, greater<int>( )); cout<<”Dizi “ ; copy(ia, ia+n, ostream_iterator<int>(cout, “ “));

Page 362: C++ 2 (Kitap - Selami KOÇLU)

cout<<endl; cout<<”15 daha once yerlesebilir”<< *upper_bound(ia, ia+n, 15, greater<int>( ))<<endl; cout<<35 daha once yerlesebilir”<< *upper_bound(ia, ia+n, 35, greater<int>( ))==ia ? “ilk ogeden” : “???”)<<endl; return 0;}///:~//üretilen çıktı:Dizi: 10 15 15 20 3015 daha once yerlesebilir 2035 daha sonra yerlesebilir son ogedenDizi: 30 20 15 15 1015 daha once yerlesebilir 1035 daha once yerlesebilir ilk ogeden//

Küme algoritmaları(heap algoritmaları)

Küme ağaç biçiminde olan bir dizidir. Standart bir kümede, bir öğeninanahtarı yavrusunun anahtarından küçük olmaz. Bu çeşit kümeler maxkümeler diye adlandırılır. Sayıların anahtar olduğu, aşağıdaki şekildeverilen kümenin düzeni; bu ağaç bir dizide şöyle yazılır:

12, 11, 10, 8, 9, 7, 6, 1, 2, 4, 3, 5

Açıklamasını verdiğimiz bu dizide, iki tane göstergeç bulunduğunuaklınızdan çıkarmayın; düğüm (node) göstergeç bir sonraki ağaç düğüm(node) yerini gösterir, bir yavru (child) göstergeç düğüm (node)göstergecinin yavrusu olan bir sonraki öğeyi işaret eder. Başlangıçta,düğüm (node) ilk öğeyi, yavru (child) ise ikinci öğeyi işaret eder.

Page 363: C++ 2 (Kitap - Selami KOÇLU)

Şekil 5.6- Küme örneği

@@ *node++ (==12). 12 en üstteki düğümdür. Yavruları ise *child(11) ve *child(10) olup her ikisi de 12 den küçüktür. @@ Bir sonraki düğüm (*node++ (==11)) ve sırası ile, *child++(8) ve *child++(9) onun yavrularıdır. @@ Bir sonraki düğüm (*node++ (==10)) ve sırası ile, *child++(7) ve *child++(6) onun yavrularıdır. @@ Bir sonraki düğüm (*node++ (==8))) ve sırası ile, *child++(1) ve *child++(2) onun yavrularıdır. @@ Daha sonraki düğüm (*node++ (==9)) ve sırası ile, *child++(4) *child++(3) onun yavrularıdır. @@ Ve son olarak (*node++ (==7)) ve tek yavrusu *child++(5)

child dizideki bir sonraki öğeyi işaret ettiği için, geri kalan düğümlerdeyavru bulunmamaktadır. Bu yüzden 1, 2, 4, 3, 5, ve 6 düğümlerininyavrusu yoktur.Burada bir konuyu belirtelim; sağ ve sol dallar düzenlenmemiştir. 8, 9 dan

6

1 2 4 3 5

8 9 7

11 10

12

Page 364: C++ 2 (Kitap - Selami KOÇLU)

küçük ama 7, 6 dan büyüktür.Küme, ikici ağacı (binary tree) seviye düzeyinde en üstteki düğümdenbaşlayarak, baştan aşağı geçerek oluşturulur. En üst düğüm 12, sıfırıncıseviyedir. Birinci seviyede 11 ve 12 bulunur. İkinci seviyede ise 6, 7, 8, 9bulunur. Bu böyle gider... Kümeler (heaps) kaplarda rastgele erişimi destekleyecek şekildeoluşturulur. Bu yüzden küme örneğin; bir liste içinde inşa edilemez.Kümeler sırasız bir diziden inşa (make_heap( ) kullanarak) edilebilir.En üstteki öğe kümeden yeniden düzenleme için alınabilir. Bunun içinpop_heap( ) işlevi kullanılır. Kümeye yeni bir öğe de eklenebilir, bununiçinde push_heap( ) işlevi kullanılır. Ve kümedeki öğeler (her ne kadarheap anlamını kaldırsa da) sort_heap( ) işlevi ile sıraya konulabilir.Aşağıdaki bölümler bazı küme algoritmalarını ve bunların kullanıldığıküçük bir örneği vermektedir.

make_heap( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void make_heap(RandomAccessIterator first, RandomAccessIterator last);-- void make_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki öğeler, yineleyicinin işaret ettiği veritipinin operator<( ) işleci kullanılarak, max küme (max-heap) oluşturmakiçin yeniden düzenlenir.-- İkinci öntip: [first, last) alanındaki öğeler, ikici (binary) karşılaştırmaişlev nesnesi comp kullanılarak, bir küme oluşturmak için yenidendüzenlenir.

pop_heap( )

Başlık dosyası:

Page 365: C++ 2 (Kitap - Selami KOÇLU)

#include <algorithm>

İşlev öntipleri:-- void pop_heap(RandomAccessIterator first, RandomAccessIterator last); -- void pop_heap(RandomAccesssIterator first, RandomAccessIterator last, Compare comp);

Açıklamalar:-- Birinci öntip: [first, last) alanındaki ilk öğe last-1 e gönderilir. Dahasonra [first, last-1) alanındaki öğeler, yineleyicinin işaret ettiği veri tipininoperator<( ) işelci kullanılarak, bir max küme (max-heap) oluşturmak içinyeniden düzenlenir.-- İkinci öntip: [first, last) alanındaki ilk öğe last-1 e gönderilir. Daha sonra[first, last-1) alanındaki öğeler, ikici (binary) karşılaştırma işlev nesnesicomp kullanılarak, bir küme oluşturmak için yeniden düzenlenir.

push_heap( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void push_heap(RandomAccessIterator first, RandomAccessIterator last);-- void push_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);

Açıklamalar:--Birinci öntip: [first, last-2) alanında geçerli bir küme olduğunu kabulediyoruz, last-1 deki öğe kümeye eklenecek bir öğeyi taşıyor, veyineleyicilerin işaret ettiği veri tipinin operator<( ) işleci kullanılarak ,[first, last-1) alanındaki öğeler max küme (max-heap) oluşturmak içinyeniden düzenleniyor.-- İkinci öntip: [first, last-2) alanında geçerli küme olduğunu kabulediyoruz, last-1 deki öğe kümeye eklenecek öğeyi taşıyor, ve ikici (binary)karşılaştırma işlev nesnesi comp kullanılarak, [first, last-1) alanındakiöğeler küme oluşturmak için yeniden düzenleniyor.

Page 366: C++ 2 (Kitap - Selami KOÇLU)

sort_heap( )

Başlık dosyası:#include <algorithm>

İşlev öntipleri:-- void sort_heap(RandomAccessIterator first, RandomAccessIterator last);-- void sort_heap(RandomAccessIterator first, RandomAccessIterator last, Compare comp);

Açıklamalar:Birinci öntip: [first, last) alanındaki öğelerin max küme (max-heap)olduğunu kabul ediyoruz, yineleyicilerin işaret ettiği veri tipinin operator<( ) işleci kullanılarak [first, last) öğeleri sıraya konur.İkinci öntip: [first, last) alanındaki öğelerin geçerli bir küme (heap)oluşturduğunu kabul ediyoruz, ikici (binary) karşılaştırma işlev nesnesicomp kullanılarak, [first, last) alanındaki öğeler sıraya konur.

Küme (heap) İşlevleri ilgili örnek:

//:B05:kumeler.cpp//heap işlevlerinin kullanıldığı #include <iostream>#include <algorithm>#include <functional>#include <iterator>void show(int* ia, char const* header){ std::cout<<header<<”:\n”; std::copy(ia, ia+20, ostream_iterator<int>(std::cout, “ “)); std::cout<<std::endl;}using namespace std;int main( ){ int ia[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}; make_heap(ia, ia+20);

Page 367: C++ 2 (Kitap - Selami KOÇLU)

show(ia, “max-heap teki 1-20 “); pop_heap(ia, ia+20); show(ia, “ilk ogeyi sil (simdi sonda)”); push_heap(ia, ia+20); show(ia, “20 (en sonda) ekle heap e “); sort_heap(ia, ia+20); show(ia, “heap teki ogeleri sirala”); make_heap(ia, ia+20, greater<int>( )); show(ia, “heap teki 1-20 arasi degerler > kullanildi”); pop_heap(ia, ia+20, greater<int>( )); show(ia, “ilk ogeyi sil”); push_heap(ia, ia+20, greater<int>( )); show(ia, heap yine 20 ekle”); sort_heap(ia, ia+20, greater<int>( )); show(ia, “heap teki ogeleri siraya koy”); return 0;}///:~//üretilen çıktı:max-heap teki 1-2020 19 15 18 11 13 14 17 9 10 2 12 6 3 7 16 8 4 1 5ilk ogeyi sil (simdi sonda)19 18 15 17 11 13 14 16 9 10 2 12 6 3 7 5 8 4 1 2020 (en sonda) ekle heap e20 19 15 17 18 13 14 16 9 11 2 12 6 3 7 5 8 4 1 10heapteki ogeleri sirala 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20heap teki 1-20 arasi degerler > kullanildi1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20ilk ogeyi sil2 4 3 8 5 6 7 16 9 10 11 12 13 14 15 20 17 18 19 1heap yine 20 ekle 1 2 3 8 4 6 7 16 9 5 11 12 13 14 15 20 17 18 19 10heap teki ogeleri siraya koy20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1//

472

Page 368: C++ 2 (Kitap - Selami KOÇLU)

auto_ptr sınıfı

Göstergeçlerin kullanımı ile ilgili, C ve C++ dillerinde birçok efsaneyazılmıştır. Hatta göstergeçlerin şeytan olduğu konusunda, epey yazıvardır. Bunun temel nedeni; göstergeçler hatalı kullanıldığında bellekkaçaklarına yolaçmaları, sistem çökmelerine neden olmaları ve sonolarakta, krakerlerin (umarım bisküvi olarak algılamamışsınızdır) yakındostları olmalarıdır.Göstergeçlerin sorun yaratmaması için, bellekteki yerleri ve ömürleri tamolarak belirlenmelidir. Göstergeç değişkeni görüntü dışına taştığı zaman,göstergecin işaret ettiği bellek yeri artık erişilemez olur. Program da bellekkaçağı oluşur. Aşağıdaki örnek matrak( ) işlevinde, matrak( ) işleviçağrıldığında, bellek kaçağı oluşur. Buradaki int değeri erişilemez olur.

void matrak( ){ new int ;}

Bellek kaçaklarını engelleyebilmek için, kesin ve açık yer belirlemesiyapılmalıdır. Göstergeç değişkeni görüntü dışına çıkmadan hemen önce,göstergecin işaret ettiği bellek alanı mutlaka silinmelidir. Yukarıdaki örnekbellek kaçağının onarımı, aşağıdaki gibi yapılır:

void matrak( ){ delete new int;}

Artık matrak( ) işlevi biraz fazla zaman harcar o kadar.

Bir göstergeç değişkeni tek bir değeri veya nesneyi işaret ettiğinde, bugöstergeç değişkeni std::auto_ptr nesnesi olarak tanımlandıysa, artık sizinbu göstergeçle ilgili bellek yeri sorununuz kalmamış demektir. Auto_ptrler göstergeç kılığına girmiş nesnelerdir. Yani aslında onlar nesne ama sizgöstergeç olarak kullanırsınız. Nesne oldukları için görüntü dışınaçıkıldığında derhal yıkıcı işlevleri çağrılır, ilgili bellek yeri boşaltılır. Yani

Page 369: C++ 2 (Kitap - Selami KOÇLU)

silinir. Bu işlem sadece dinamik olarak yerleşim yapılmış bellek yerlerineuygulanır. Dinamik bellek kullanımının program çalışırken kullanılanbellek olduğunu hatırlatalım.auto_ptr ler kullanılmadan önce, aşağıdaki önişlemleyici yönergesimutlaka başlık dosyaları arasına yazılmalıdır.

#include <memory>

Bir auto_ptr nesnesi, dinamik olarak oluşturulmuş bir nesne veya değerkullanılarak başlatılır (yani ilk değr ataması yapılır). Auto_ptr lereuygulanan bazı sınırlamalar da bulunmaktadır, bunlar:

** auto_ptr nesnesi, nesneler dizisini işaret etmek için kullanılamaz.** auto_ptr nesnesi sadece dinamik olarak yazılmış bellek yerleri için işarette bulunabilir, ve sadece dinamik olarak yerleşmiş olan bellek yerlerini boşaltabilir.** Dinamik olarak yerleşmiş olan aynı bellek bütününü, birden fazla sayıda auto_ptr nesnesinin işaret etmesine izin verilmez. auto_ptr nin arayüzü (yani sınıf işlevleri) bunu engeller. auto_ptr nesnesi görüntü dışına çıkar çıkmaz, işaret ettiği bellek yeri derhal boşaltılır.

Göstergecin kendisine erişebilmek veya bellek bütününün başka bir yeriniişaret etmek için, class auto_ptr de çok sayıda üye işlev bulunur. Bu üyeişlevleri ve auto_ptr nesnesi oluşturmayı, birazdan inceleyeceğiz.

auto_ptr Değişkenlerinin Tanımı

auto_ptr nesnesi tanımlamanın üç türlü yolu vardır. Tanımların her birinde<type> belirteci bulunur. Ayrıntılı örneklerini daha sonra vermek üzere,şimdilik sadece uygulamaların özetini verelim:

** Temel biçim, bellek yerleşimi new işleci tarafından yapılmış olan yeri işaret eden, auto_ptr nesnesinin başlatılmasıdır. auto_ptr<type> identifier (new- açıklama);

** İkinci biçim ise, bir kopya yapıcı işlev kullanarak auto_ptr nesnesini

Page 370: C++ 2 (Kitap - Selami KOÇLU)

başlatmaktır.

auto_ptr<type> identifier (type için başka bir auto_ptr);

** Üçüncü biçim de ise basitçe oluşturulan auto_ptr nesnesi belli bir bellek bütününü işaret etmez.

auto_ptr<type> identifier ;

Yeni Yerleşen Nesnenin İşaret Edilmesi

auto_ptr nesnesini başlatmanın, yani ilk değer atamasını yapmanın basityolu; yapıcı işlevini, operator new işleci ile sağlanan bellek ile birliktetemin etmektir. Genel biçim:

auto_ptr<type> identifier (new- expression);

Örnek olarak, bir string nesnesini işaret eden auto_ptr yi başlatmak için,aşağıdaki biçim kullanılabilir:

auto_ptr<string> strPtr(new string (“selam selami”));

double değeri işaret eden auto_ptr yi başlatmak için ise:

auto_ptr<double> dPtr(new double(654.321));

Yukarıdaki örneklerde operator new işlecinin kullanımına iyice dikkatedin. new kullanımı, auto_ptr nesnelerinin işaret ettiği bellek alanlarınındinamik karakterini güvenceye alır. Ve auto_ptr nesnesi görüntü dışınaçıktığında bellek alanını bir kez boşaltır. Dikkat edilecek başka bir noktada type ın göstergeç olmadığıdır. auto_ptr inşaatında kullanılan type newile kullanılan açıklama ile aynıdır.Konunun başında verdiğimiz matrak( ) örneğini şimdi daha güvenliyazalım:

#include <memory>using namespace std;void matrak( ){

Page 371: C++ 2 (Kitap - Selami KOÇLU)

auto_ptr<int> ip(new int);}

new işleci tarafından yerleştirilen nesneler için sağlanan bütün üyeişlevlere auto_ptr aracılığı ile erişilir, burada auto_ptr sanki, dinamikolarak yerleşmiş nesneyi gösteren, herkesin bildiği göstergeç gibi davranır.Aşağıdaki örnekte 'C++' ifadesi 'selam' kelimesinin ardına yerleştirilir.

//:B05:kendiyer.cpp//auto_ptr kullanımı#include <iostream>#include <memory>using namespace std;int main( ){ auto_ptr<string> sp(new string(“Selam anadolu”)); cout<<*cp<<endl; sp->insert(strlen(“Selam”), “C++”); cout<<*sp<<endl;}///:~//üretilen çıktı:Selam anadoluSelam C++//

Başka bir auto_ptr i işaret etmek

Bir auto_ptr aynı tipteki başka bir auto_ptr nesnesi tarafından başlatılabilir.Genel biçim:

auto_ptr<type> identifier(başka bir auto_ptr nesnesi);

Örnek olarak auto_ptr<string> i başlatmak için, daha önce tanımı verilensp değişkenini kullanalım;

auto_ptr<string> strPtr(sp);

Atama işleci de (=) benzer şekilde kullanılabilir. Bir auto_ptr nesnesi aynıtipteki başka bir auto_ptr nesnesine atanabilir. Örnek:

Page 372: C++ 2 (Kitap - Selami KOÇLU)

//:B05:atamak.cpp//atama işlecinin kullanımı#include <iostream>#include <memory>#include <string>using namespace std;int main( ){ auto_ptr<string> selam1(new string(“Selam anadolu”)); auto_ptr<string> selam2(selam1); auto_ptr<string> selam3; selam3=selam2; cout<<*selam1<<endl<< *selam2<<endl<< *selam3<<endl;}///:~//üretilen çıktı:Segmentation fault (Bellek parça hatası)//

Yukarıdaki örneği incelediğimizde göze çarpanlar:

-- selam1 önceki örneğe benzer başlatıldı.-- Daha sonra selam2 tanımlandı, ve değerini kopya yapıcı işlevle başlatarak selam1 den aldı. Bu uygulama selam1 i 0-göstergece değiştirdi.-- Ve en son olarak selam3, auto_ptr<string> olarak tanımlandı. Ama değerini atama ile selam2 den aldı. Tabii o da o zaman 0-göstergeç oldu.

Program bellek parça hatası üretti. Bunun sebebi 0- göstergeçlerindayançlarından kurtulmalarıdır. En sonunda aslında sadece selam3 birstring i işaret eder.

Basit bir auto_ptr oluşturmak

Biraz önce auto_ptr oluşturmanın üçüncü yolunu görmüştük: değişkenleri

Page 373: C++ 2 (Kitap - Selami KOÇLU)

olmadan, belli bir bellek alanını göstermeyen, boş bir auto_ptr nesnesi inşaedilmişti.

auto_ptr<type> identifier ;

Bu durumda temel göstergeç 0 (sıfır) a ayarlanır. auto_ptr nesnesininkendisi bizzat göstergeç olmadığı için, başlatılmadığını görmek için 0(sıfır) değeri ile karşılaştırma yapılmaz. Örnek:

auto_ptr<int> ip;if(!ip) cout<<”0-gostergeci olan auto_ptr nesnesi”<<endl;

Tabii, bu herhangi bir çıktı üretmez.

İşleçler ve Üyeler

auto_ptr sınıfı için tanımlanmış olan işleçler:

** auto_ptr& auto_ptr<Type>operator=(auto_ptr<Type>& other) :Bu işleç sağyan değer auto_ptr nesnesinin belleğini, solyan değer auto_ptrnesne belleğine aktarır. Bunun sonucunda sağyan değerini gösteren bellekalanı değerini kaybeder ve 0-göstergeç olur.

** Type& auto_ptr<Type>operator*( ) :Bu işleç auto_ptr nesnesinde saklı bilgiye bir dayanç döndürür.Dayancından kurtulmuş işlecin normal göstergeci gibi davranır.

** Type* auto_ptr<Type>operator->( ) :Bu işleç auto_ptr nesnesinde saklı bilgiye bir göstergeç döndürür. Bu işleçsayesinde, bellekte bulunan nesnenin üyeleri seçilebilir. Örnek:

auto_ptr<string> sp(new string(“selam”));cout<<sp->c_str( )<<endl;

auto_ptr nesneleri için aşağıdaki üye işlevler tanımlanmıştır:

** Type* auto_ptr<Type>::get( );

Page 374: C++ 2 (Kitap - Selami KOÇLU)

Bu işlev operator->( ): ile aynı işi görür. auto_ptr nesnesinde saklı bilgiyebir göstergeç döndürür. Bu göstergeç için şunlar söylenebilir: Değeri eğersıfırsa (0), auto_ptr nesnesi herhangi bir bellek alanını işaret etmiyordemektir. Bu üye, auto_ptr nesnesinin başka bir bellek bütününü işaretetmesine izin vermez.

** Type* auto_ptr<Type>::release( ) :Bu işlev boşaltılacak bilginin bulunduğu auto_ptr nesnesine bir göstergeçdöndürür. (böylece o bellek alanı 0-göstergeç olur). Bu üye işlev auto_ptrnesnesinde saklı bilgiyi, basit Type göstergecine çevirmede kullanılabilir.Bu üye işlevden dönen değerin bellek alanının boşaltılması, programcınınsorumluluğundadır, hemen belirtelim.

** void auto_ptr<Type>::reset(Type*) :Bu işlev auto_ptr nesnesinde kullanılan belleği boşaltmak için değişkensizçağrılabilir, veya dinamik olarak yerleştirilmiş bellekler için göstergeçleçağrılabilir, veya auto_ptr nesnesi tarafından erişilen bellekler dekullanılır. Bu üye işlev auto_ptr nesnesine yeni bir belelk alanı tamaktakullanılabilir.

Yapıcı İşlevler ve Göstergeç Veri Üyeleri

auto_ptr in temel özellikleri anlatıldı, şimdi aşağıdaki örneği ele alalım:

//gereken #include larclass Map{ std:.map<string, Data>* d_map; public: Map(char const* filename) throw(std::exception);};

Map sınıfının yapıcı işlevi aşağıdaki görevleri yerine getirir:

** Bir std::map nesnesini belleğe yerleştirir.** Adı yapıcı işlev değişkeni olarak belirtilen bir dosya açar.** Dosyayı okur ve map i o suretle doldurur.

Page 375: C++ 2 (Kitap - Selami KOÇLU)

Tabii dosya açmak mümkün olmadığında, uygun bir istisna (exception)fırlatılır (throw). Böylece yapıcı işlev uygulaması şu hali alır:

Map::Map(char const* fname): d_map(new std::map<std::string, Data>) throw(std::exception){ ifstream istr(fname); if(!istr){ throw std::exception(“dosya acilmaz”); fillMap(istr);}

Peki bu uygulamada hata nerede?. Temel zayıflık bellek kaçağının yatağıolmasıdır. Bellek kaçağı sadece istisna fırlatıldığı zaman oluşur. Diğerzamanlar ise işlev hatasız ve mükemmel çalışır. İstisna fırlatıldığında, map dinamik olarak yerleşime tabi tutulmaktadır. Bununla birlikte sınıfyıkıcı işlevi delete d_map i çağırsa bile, yıkıcı işlev aslında hiçbir zamançağrılmaz. Yıkıcı işlev sadece tam olarak inşa edilmiş nesneler içinçağrılır. Yapıcının görevi bir istisna ile sonlandığı için, o sırada inşaedilmekte olan nesne, tam olarak inşa edilmemiş demektir. Bundan dolayıyıkıcı işlev hiçbir zaman çağrılamaz.auto_ptr ler bu çeşit sorunları çözmekte de kullanılabilirler. d_map iaşağıdaki gibi tanımlayalım:

std::auto_ptr<std::map<std::string, Data> >

(burada yeri gelmişken bir konuyu daha belirtelim; bilindiği gibi > >şeklinde belirtilen gösterim sağa kayma ile karışmasın diye bir aralıklayapılmaktadır. Fakat 2006 yılında, standart komitesi 1. numaralı teknikraporunda (TR. 1 diye geçiyor) artık boşluğa gerek kalmayacağını belirtti,yani >> veya << kaymalarla karışmayacak. 2007 den sonra programyazacaklara bilgi olsun dedik)

Artık aniden bir nesneye dönüşür. Şimdi Map yapıcısı güvenle istisnafırlatabilir. d_map in kendisi bir nesnedir, yıkıcı işlevi artık tam zamanındayani Map nesnesi görüntü dışına çıktığında görevini (inşaatıtamamlanmamış olsa bile) yerine getirir.Burada ana kural: yapıcı işlevin bir istisna ile görevini tamamlayamamasıgibi bir durum söz konusu olursa, sınıflarda göstergeç veri üyelerininyerinde, basit göstergeçler kullanmayın, auto_ptr ler kullanın. Neymiş!!!

Page 376: C++ 2 (Kitap - Selami KOÇLU)

auto_ptr ler istisnai durumlarda sizi madara olmaktan kurtarırlar. Hayattaistisnası olmayan hangi durum var zaten ?.. Sonra programım nedençalışmıyor diye zırlamayın.

Page 377: C++ 2 (Kitap - Selami KOÇLU)

6. Bölüm

İstisnalar (Exceptions)

C dili programın normal akışını engelleyen istisnalara karşı değişikyollarla yardım sağlamıştır. Bunlar;

** İşlevle anormalliği haber verir, ve bir ileti üretir. Bu büyük ihtimalle birprogramın gösterebileceği en az tehlikeli reaksiyondur.** Anormallik gözlendiğinde işlev o anki görevi sonlandırır, onu çağıranabir hata kodu gönderir. Bu, kararları erteleme ilgili önemli bir durum olup,şimdi çağrılan işlev bir sorunla karşı karşıya demektir. Tabii çağrılan işlevde benzer reaksiyon gösterip çağırana hata kodu iletebilir.** İşlev, denetim dışına çıkan unsurlara bakıp karar vererek, uygunsa exit( ) işlevini çağırıp, programı tamamen sonlandırabilir. Bu sorunu

Page 378: C++ 2 (Kitap - Selami KOÇLU)

çözmenin en kökten yoludur.** İşlev, setjmp( ) ve longjmp( ) işlevlerinin bir kombinasyonunu kullanıp,yörel olmayan çıkışlara yönelebilir. Bu mekanizma goto nun başka birçeşidi olup, istisnanın oluştuğu ortamın dışında programın sürmesinisağlar. Bu durum, kullanılmış olan yuvalı işlevlerden çok sayıda geridönüşler olmuşsa, ziyaret edilmesi gereken ara seviyeler atlanarakgerçekleştirilir.

C++ dilinde ise, program akışını sakata uğratan durumlarda yukarıdakikullanılan çözüm yolları yine geçerlidir. Bununla birlikte başkaseçeneklerde bulunur. Örnek olarak; setjmp( ) ve longjmp( )kombinasyonlarına C++ (aslında C de de sık rastlanmaz) dilinde pekrastlanmaz. Zira o zaman program akışı tamamen kesintiye uğrar.C++ dili bunların yerine istisnaları (exceptions) kullanıma sunar. İstisnalar,C++ programlarına denetlenebilir yörel olmayan geri dönüşleri sağlar.Böylece longjmp( ) ve setjmp( ) sakıncaları ortadan kalkar. İstisnalar, işlevin kendisi tarafından çözümlenemeyen durumlardankurtulmanın en uygun yoludur. Özellikle, bir programı tamamensonlandırmak için tam çöküntü ortaya çıkmadığı durumlarda. Bütünbunların yanında, istisnalar kısa return (geri dön) ile kaba exit (çık)arasında esnek bir aralık sağlar.Bu bölümde istisnalar ve yazım biçimleri irdelenecek.

İstisnaların kullanımı: yazım öğeleri

İstisnalarla birlikte aşağıdaki yazım öğeleri kullanılır:

** try: try bloğu istisnaları üreten deyimleri (bunlar fırlatılacak istisnalardır) kapsar. Örnek:

try{ //istisnaların fırlatılacağı deyimler }

** throw: belli bir tipte açıklama tarafından takip edilir, istisna olarak kabul edilen açıklamanın değerini fırlatır. throw deyimi try bloğunun içinde mutlaka çalıştırılmalıdır: ya doğrudan, veya doğrudan çağrılan bir işlev içinde, ya da try bloğundan dolaylı

Page 379: C++ 2 (Kitap - Selami KOÇLU)

olarak. Örnek: throw “Bu bir char* istisnası üretir” ;

** catch: try bloğunun hemen ardından gelir, catch bloğu fırlatılan istisnaları yakalar. char* istisnalarını alan catch bloğu örneği:

catch (char* message){ //fırlatılan char* istisnalarını yöneten deyimleri }

İstisnaların kullanıldığı örnek Takip eden iki bölümde aynı basit programı kullanacağız. ProgramdaOuter ve Inner isimli iki sınıf bulunmaktadır. main( ) işlevinde Outernesnesi oluşturulur, ve Outer::fun( ) üyesi çağrılır. Daha sonra Outer::fun( ) da bir Inner nesnesi oluşturulur. Inner nesnesi oluşturuluncaInner::fun( ) üyesi çağrılır. Şimdi herşeyin ilgili olduğu yere gelelim. Outer::fun( ) işlevi sonlanır,Inner nesnesinin yıkıcı işlevi çağrılır. Daha sonra program sonlanır, Outernesnesinin yıkıcı işlevi çağrılır. İşte program:

//:B06:istisna.cpp//örnek çalışma#include <iostream>using namespace std;class Inner{ public: Inner( ); ~Inner( ), void fun( );};class Outer{ public: Outer( ); ~Outer( ); void fun( );};

Page 380: C++ 2 (Kitap - Selami KOÇLU)

Inner::Inner( ){ cout<<”Inner constructor\n”;}Inner::~Inner( ){ cout<<”Inner destructor\n”;}void Inner::fun( ){ cout<<”Inner fun\n”;}Outer::Outer( ){ cout<<”Outer constructor\n”;}Outer::~Outer( ){ cout<<”Outer destructor\n”;}void Outer::fun( ){ Inner in; cout<<”Outer fun\n”; in.fun( );}int main( ){ Outer out; out.fun( );}///:~//üretilen çıktı:Outer constructorInner constructorOuter funInner funInner destructorOuter destructor//

Yukarıdaki program derlenip çalıştırıldığı zaman, herşeyin beklendiği gibiilerlediği görülecektir. Yani yıkıcı işlevler yapıcı işlevlerin çağrılmasırasının tersine çağrılacaklardır.Şimdi konumuzla ilgili olsun diye, main( ) işlevinin son taraflarında yeralan, Inner::fun( ) işlevinde tam çöküntü yaratmayan bir olay olduğunufarz edelim. İki durumu irdeleyelim. Birincisinde, bu durumu yönetmekiçin setjmp( ) ve longjmp( ) işlevlerini kullanalım, ikincisinde ise C++

Page 381: C++ 2 (Kitap - Selami KOÇLU)

dilinin istisnalarını (exceptions) kullanalım.

setjmp( ) ve longjmp( ) değişimleri

setjmp( ) ve longjmp( ) işlevlerini, önceki bölümde verilen basit programdakullanabilmek için, program jmp_buf jmpbuf değişkenini içerecek şekildebiraz değiştirilir. Inner::fun( ) işlevi lonjmp yi çağırır, main( ) işlevininsonunda kullanmak için, bir çöküntü olayının benzeri oluşturulur. main( )işlevinde long sıçrayışın yerini tanımlamak için setjmp( ) işlevi kullanılır. Sıfır geri dönüş değeri, çağrılan Outer::fun( ) işlevine göre jump_bufdeğişkeninin başlatılışını belirtir. Bu durum 'normal akış' ı temsil eder.Çöküntü olayının benzerini tamamlarken, programın dönüş değerinin sıfırolması için ancak programın Outer::fun( ) işlevinden normal olarakdönebilmesi lazımdır. Bununla birlikte biz biliyoruz ki; bu olmaz,Inner::fun( ) longjmp( ) ı çağırır, setjmp( ) işlevine döner, bu sırada sıfırdönüş değeri dönmez. Böylece, Outer:.fun( ) dan Inner::fun( ) işleviçağrıldıktan sonra, program main( ) işlevindeki if deyiminin ötesine ilerler,Ve program 1 (bir) dönüş değeri ile sonlanır. Şimdi aşağıdaki kaynakprogramı adım adım inceleyerek, daha önceki bölümde verilen temelprogramda değişiklikler yapalım.

//:B06:hatalar.cpp//setjmp ve longjmp kullanımı#include <iostream>#include <setjmp.h>#include <cstdlib>using namespace std;class Inner{ public: Inner( ); ~Inner( ); void fun( );};class Outer{ public: Outer( ); ~Outer( );

Page 382: C++ 2 (Kitap - Selami KOÇLU)

void fun( );};jmp_buf jmpBuf;Inner::Inner( ){ cout<<”Inner constructor\n”;}void Inner::fun( ){ cout<<”Inner fun( )\n”; longjmp(jmpBuf, 0);} Inner::~Inner( ){ cout<<”Inner destructor\n”;}Outer::Outer( ){ cout<<”Outer constructor\n”;}Outer::~Outer( ){ cout<<”Outer destructor\n”;}void Outer::fun( ){ Inner in; cout<<”Outer fun\n”; in.fun( );}int main( ){ Outer out; if(!setjmp(jmpBuf)){ out.fun( ); return 0; } return 1;}///:~//üretilen çıktı:Outer constructorInner constructorOuter funInner fun( )Outer destructor//

Page 383: C++ 2 (Kitap - Selami KOÇLU)

Programın çıktısından da açıkça görüldüğü üzere Inner sınıfının, yıkıcıişlevi çalıştırılmamıştır. Yani nesnelerin yıkıcı işlevleri, setjmp( ) velongjmp( ) kullanılarak kolaylıkla atlanabilir.

Başka bir tercih: İstisnalar (exceptions)

C++ dilinde, setjmp( ) ve longjmp( ) lerin en iyi alternatifi istisnalardır,yani exceptions. Bu bölümde istisnaların kullanılldığı bir örnekle konuyuanlatacağız. Yine daha önce anlatılan basit programla işe başlayacağız:

//:B06:istisna.cpp//exception ların kullanımı#include <iostream> using namespace std;class Inner{ public: Inner( ); ~Inner( ); void fun( );};class Outer{ public: Outer( ); ~Outer( ); void fun( );};Inner::Inner( ){ cout<<”Inner constructor\n”;}Inner::~Inner( ){ cout<<”Inner destructor\n”;}void Inner::fun( ){ cout<<”Inner fun\n”; throw 1; cout<<”bu deyim calismaz\n”;}

Page 384: C++ 2 (Kitap - Selami KOÇLU)

Outer::Outer( ){ cout<<”Outer constructor\n”;}Outer::~Outer( ){ cout<<”Outer destructor\n”;}void Outer::fun( ){ Inner in; cout<<”Outer fun\n”; in.fun( );}int main( ){ Outer out; try{ Out.fun( ); } catch (.... ){ }}///:~//üretilen çıktı:Outer constructorInner constructorOuter funInner funInner destructorOuter destructor//

Bu programda, bir önceki programın lonjmp( ) kullanılan yerinde, biristisna (exception) fırlatılır. Önceki programın karşılaştırma yapılması içinkullanılan setjmp( ) çağrısı yerine de, burada try ve catch blokları yer alır.Try bloğu istisnaların fırlatıldığı deyimleri (işlev çağrıları da dahil) kuşatır,catcth bloğu ise istisnalar fırlatıldıktan sonra çalıştırılacak deyimlerikapsar.Bir önceki örnekle karşılaştırılacak olursa; Inner::fun( ), bir istisna ilelongjmp( ) çağrısından daha fazlası olsa bile, sonlanır. İstisna main( )işlevinde yakalanır, ve program sonlanır. Şu anki programın çıktısıincelenirse, Outer::fun( ) içinde oluşturulan Inner nesne yıkıcı işlevi, artıkdoğru şekilde çağrılır. Inner::fun( ) işlevinin çalışması throw deyimleri

Page 385: C++ 2 (Kitap - Selami KOÇLU)

arasında sonlanır: throw deyiminin hemen ötesinde bulunan cout ayerleştirilen metin görülmez.İsitisnalar için iştahınız arttığını düşünüyoruz, zira görüldü ki:** İstisnalar programı tamamen sonlandırmadan ve ardarda return deyimleri gerekmeden, programın normal akışının dışına çıkılmasını sağlar. ** İstisnalar yıkıcı işlevlerin etkinliğini kaldırmaz, bu yüzden setjmp( ) ve longjmp( ) işlevlerinin yerine tercih edilirler.

İstisnaların fırlatılması

İstisnalar throw deyiminin içinde yer alabilir. Throw anahtar kelimesi bellibir tipin değerini alan bir açıklama tarafından takip edilir. Örnek:

throw “Selam Selami” ; //bir char* fırlatırthrow 99 ; //bir int fırlatırthrow string(“Selam”); //bir string fırlatır

İşlevlerde yörel olarak tanımlanmış nesneler bu işlevler tarafındanistisnalar fırlatıldığında kendiliğinden bir kez silinir, işlevi terkederler.Bununla birlikte eğer nesnenin kendisi fırlatılırsa, istisna yakalayıcısıfırlatılan nesnenin bir kopyasını alır. Bu kopya, yörel nesne silinmedenhemen önce inşa edilir.Bir sonraki örnek işte bu noktaya parmak basacak. Object::fun( ) içindefırlatmak için yörel bir nesne oluşturulacak, ki bu sonra istisna olarakfırlatılacak. İstisna, Object::fun( ) dışında, main( ) işlevinde yakalanacak.Bu noktada fırlatılan nesne herhangi bir yerde görülmeyecek. Şimdigelelim kaynak koda:

//:B06:nesneistisna.cpp//fırlatılan nesne #include <iostream>#include <string>using namespace std;class Object{ string d_name; public:

Page 386: C++ 2 (Kitap - Selami KOÇLU)

Object(string name) : d_name(name){ cout<<”Object constructor of “<<d_name<<”\n”; } Object(Object const& other) : d_name(other.d_name+” (copy)”){ cout<<”Copy constructor for “<<d_name<<”\n”; } ~Object( ){ cout<<”Object destructor of “<<d_name<<”\n”; } void fun( ){ Object toThrow(“'local object'”); cout<<”Object fun( ) of “<<d_name<<”\n”; throw toThrow; } void selam( ){ cout<<”selam “<<d_name<<”\n”; }};int main( ){ Object out(“'main object'”); try{ out.fun( ); } catch (Object o){ cout<<”Yakalanan istisna \n”; o.selam( ); }}///:~//üretilen çıktı:Object constructor of 'main object'Object constructor of 'local object'Object 'fun( ) of 'main object'Copy constructor for 'local object' (copy)Object destructor of 'local object'Copy constructor for 'local object' (copy) (copy)Yakalanan istisnaSelam 'local object' (copy) (copy)Object destructor of 'local object' (copy) (copy)Object destructor of 'local object' (copy)Object destructor of 'main object'

Page 387: C++ 2 (Kitap - Selami KOÇLU)

//

Object sınıfı basit birkaç yapıcı işlev ve üyeler tanımlar. Kopya yapıcıişlev ise, alınan isme “ copy” ekler. Ve bu, nesnelerin inşasını veyıkılmalarını (yani silinmelerinde), çok daha yakından görmemizi sağlar. İstisnayı Object::fun( ) üye işlevi üretir, ve yörel tanımlanmış nesnesinifırlatır. İstisnayı fırlatmadan biraz önce program aşağıdaki çıktıyı üretir:

Object constructor of 'main object'Object constructor of 'local object'Object fun( ) of 'main object'

Şimdi istisna üretilir ve programdan aşağıdaki çıktı elde edilir:

Copy constructor for 'local object' (copy)

throw cümlesi yörel nesneyi alır, ve onu değişken değeri olarak işlemler;yani yörel nesnenin bir kopyasını oluşturur. Bunu takiben istisnaişlemlenir, yörel nesne imha edilir (yani silinir), yakalayıcı (catcher) birObject yakalar, ve yine bir değer değişkeni. Böylece başka bir kopyaoluşur. Daha sonra aşağıdaki satırları görürüz:

Object destructor of 'local object' Copy constructor for 'local object' (copy) (copy)

Şimdi artık yakalayıcının (catcher) içindeyizaşağıdaki iletiyi görürüz:

Yakalanan istisna

Alınan nesnenin selam( ) üyesinin çağrısı takip eder. Buradan dagördüğümüz: Object::fun( ) üye işlev yörel nesnesinin kopyasının kopyasıdır.

Selam 'local object' (copy) (copy)

Neticede program sonlanır, ve hala oluşturulduğunun tersi sıralamadasilinecek olan canlı nesneler bulunmaktadır.

Object destructor of 'local object' (copy) (copy)

Page 388: C++ 2 (Kitap - Selami KOÇLU)

Object destructor of 'local object' (copy)Object destructor of 'main object'

Eğer yakalayıcı nesneye dayanç alacak şekilde uygulansaydı (yani 'catch (Object& o)'), o zaman kopya yapıcı işlevin tekrarlanançağrılarından sakınılmış olurdu. O durumda programın çıktısı:

Object constructor of 'main object'Object constructor of 'local object'Object fun( ) of 'main object'Copy constructor for 'local object' (copy)Object destructor of 'local object'Yakalanan istisnaSelam 'local object' (copy)Object destructor of 'local object' (copy)Object destructor of 'main object'

Bunun bize gösterdiği, yörel nesnenin sadece bir kopyası kullanılmaktadır.Tabii, yörel olarak tanımlanmış bir nesneye göstergeç fırlatmak kötü birfikirdir; göstergeç fırlatılır, ama istisna fırlatıldığında göstergecin işaretettiği nesne bir kez ölür, ve yakalayıcı kaba göstergeci yakalar, yani kötühaberler ...

Özetle:** Yörel nesneler kopyalanmış nesneler olarak fırlatılır.** Yörel nesnelerin göstergeçleri fırlatılmaması lazım.** Bununla birlikte, dinamik olarak üretilmiş olan nesnelerin göstergeç veya dayançlarını fırlatmaları mümkündür. Bu durumda istisna yakalandığında, bellek kaçağını engellemek için, dinamik üretilen nesne uygun şekilde ortadan kaldırılmalıdır (yani silinmelidir).

Bir işlev görevini normal şekilde devam ettiremediği zaman, program haladevam etse bile, istisnaların fırlatılmaları zamanı geldi demektir.Etkileşimli hesap makinesini düşünelim. Program daha sonra hesaplamakiçin, sürekli olarak açıklamalar ister. Bu durumda açıklamanın derleyiciyeuygun parçalanması (parsing) yazım hataları içerebilir: ve açıklamanınhesaplaması mümkün olmaz, buna örnek olarak; açıklamanın sıfırabölünmesi durumu gibi. Ayrıca; hesaplayıcı değişkenlerin kullanılmasınaizin verir, ve kullanıcı olmayan değişkenlere atıfta bulunur; istisnalarınfırlatılması için çok sayıda sebep ortaya çıkar, ama programı sonlandırmak

Page 389: C++ 2 (Kitap - Selami KOÇLU)

için boğucu bir neden olmaz. Programda aşağıdaki kodları istisnalarıfırlatmak için kullanabilirsiniz;

if(!parse(expressionBuffer)) //parçalama yok throw “açıklamada yazım hatası”;if(!lookup(variableName)) //değişken bulunamadı throw “değişken tanımlanmadı”;if(!divisionByZero( )) //mümkün olmayan bölüm throw “sıfıra bölüm tanımlanmamıştır”;

Bu throw deyimlerinin yerleşimi duruma göre değişir; programın içindederinlerde yuvalı şekilde, veya daha üst düzeyde yerleştirilir. Daha dafazlası, fırlatılacak açıklamaları üretmek için işlevler kullanılabilir.Aşağıdaki işlev benzeri;

char const* formatMessage(char const* fmt, .... );

çok daha özgün iletiler gönderilmesini sağlar;

if(!lookup(variableName)) throw formatMessage(“Variable '%s' tanımlanmadı”,variableName);

gibi.

Boş 'throw' deyimi

Bazı durumlarda, fırlatılan throw deyiminin gerekliliği incelenir. Budurumda alınan istisnanın doğasına bakarak programın devamına, veyadaha ciddi bir davranış gösterilip, programın durdurulması yoluna gidilir.Sunucu-istemci uygulamalarında istemci sunucuya dileklerini kuyruğa(queue) yerleştirerek bildirir. Kuyruğa yerleştirilen her dilek, sunucutarafından ya dileğin başarı ile tamamlandığı, veya bazı hataların ortayaçıktığı istemciye bildirilerek yanıtlanır. Aslında sunucu ölmüş olabilir, yaniartık hiç yanıt veremez durumda olabilir, bu durumda istemci bu felaketikeşfedebilmelidir, böylece anlamsız şekilde sunucunun yanıt vermesi için

Page 390: C++ 2 (Kitap - Selami KOÇLU)

beklemek gerekmez.Böyle zamanlarda orta katmanda bir istisna yöneticisi devreye girer.Fırlatılan istisna önce orta katmanda incelenir. Mümkün olursa orada işlemsürdürülür. İstisnanın orta katmanda işlemlenmesi mümkün olmazsa,değiştirilmeden, daha üst seviyeli sert istisna yönetiminin gerçekleştirildiğiyere aktarılır.İstisna yönetiminin yapıldığı koda, boş bir throw deyiminin yerleştirilmesiile, alınan istisnanın bir sonraki seviyeye aktarılıp özel istisna tipi olarakişlemlenmesi sağlanır.Sunucu ve istemcimizdeki işlev:

InitialExceptionHandler(char* exception)

şeklinde tasarlanmalıdır. Alınan ileti incelenir. Basit bir ileti ise, işlemlenir.Yok değilse, bir üst düzeye aktarılır. InitialExceptionHandler( ) işlevindeboş bir throw deyimi bulunur:

void InitialExceptionHandler(char* exception){ if(!plainMessage(exception)) throw; handleTheMessage(exception);}

Birazdan göreceğimiz gibi, boş throw iletisi catch bloğunca alınanistisnaya aktarılır. Bundan dolayı, initialExceptionHandler( ) benzeri birişlev, işlevin değişkeni alınan açıklamanın karakteri ile uyumlu olduğusürece, fırlatılan istisnanın bir çeşidi için kullanılabilir. Nasıl size biraz ilginç geldimi?. Bir sonraki örneğe bakarsanızgöreceksiniz ki bu konu çokbiçimlilikle ilgili. Çokbiçimlilik 1. kitabımızdanesne yönelimli paradigmanın olmazsa olmazları arasındaydı. İlkkitabımızda çokbiçimlilik bütün ayrıntıları ile anlatıldığı için, buradatekrarlamaya gerek görmüyoruz.Artık özel istisnaların türetildiği, temel istisna yönetim sınıfını inşaedebiliriz. Bir Exception sınıfımızın olduğunu kabul edelim, ve bu sınıfExceptionType Exception::severity( ) üye işlevine sahip olsun. Bu üyeişlev bize, fırlatılan açıklamanın sertlik derecesini bildirsin. Bunlar; İleti(Message), Uyarı (Warning), Yanlışlık (Mistake), Hata (Error), veyaFelaket (Fatal) olsun. Bunlara ilaveten, sertliğin derecesine bağlı olarakfırlatılan açıklama, process( ) işlevince gerçekleştirilecek işlem hakkında

Page 391: C++ 2 (Kitap - Selami KOÇLU)

az ya da çok bilgi içersin. Buna ek olarak, bütün istisnalarda üye işlevlerüreten (örnek toString( )) basit metinler bulunur. Bu metinlerde, üretilenistisnalar hakkında bir miktar bilgi bulunur. process( ) işlevi, temel Exception göstergeci veya dayancı çağrıldığında,fırlatılan istisnanın karakterine bağlı olarak, çokbiçimlilik kullanılarakfarklı davranışlar göstermesi sağlanır. Bu durumda, program beş istisna tipinden herhangi birini fırlatır. ŞimdiinitialExceptionHandler( ) işlevimizin Message ve Warning istisnalarınıişlemlediğini kabul edelim. O zaman kodumuz aldığı şekil;

void initialExceptionHandler(Exception const* e){ cout<<e->toString( )<<endl; //basit metin bilgisi if(e->severity( )!=ExceptionWarning && e->severity( )!=ExceptionWarning) throw; //diğer istisnalara aktar e->process( ); // iletiyi veya uyarıyı işlemle delete e;}

Çokbiçimlilikten dolayı, e->process( ) ya Message ya da Warningsonucunu üretir.Aşağıdaki istisnalar fırlatılır:

throw new Message(<arguments>);throw new Warning(<arguments>);throw new Mistake(<arguments>);throw new Error(<arguments>);throw new Fatal(<arguments>);

Yukarıdaki bütün istisnalar initialExceptionHandler( ) tarafındanişlemlenebilir. Yani, ya istisnanın kendisi işlemlenir ya da, istisna daha üstseviyelere gönderilir. Çokbiçimliliğin kullanıldığı istisna sınıfını da artıksiz geliştirin.

Try

Page 392: C++ 2 (Kitap - Selami KOÇLU)

Try bloğu, fırlatılabilecek istisnaları içeren deyimleri kuşatır. Görüleceğiüzere asıl try deyimi her yere konabilir, yani doğrudan try bloğunda olmasışart değildir. Try bloğundan çağrılan bir işlevin içinde de olabilir. Tryanahtar kelimesini bir grup zincirli ayraç takip eder, ve ayraçlara arasındaçok sayıda deyim ve tanımlar bulunur, yani aynı C++ bileşik deyimlerigibi. İstisnaları fırlatmak için (yaygın olarak) değişik seviyeler oluşturulur.Örnek olarak main( ) işlevinin try bloğu ile kuşatıldığı durum. Burada dışkatman oluşturarak istisnalar yönetilebilir. main( ) işlevinin kendi trybloğunun içinde, daha üst düzeyde istisnalar üreten try blokları bulunduranişlevler çağrılabilir. Önce de anlatıldığı üzere, daha iç kısımlardaki trybloklarındaki istisnalar, o seviyelerde ya işlemlenir veya işlemlenmez.İstisna yöneticisine boş bir try yerleştirerek, fırlatılan istisna bir üst (dış)seviyeye aktarılabilir.Bir istisna herhangi bir try bloğunun dışına fırlatılırsa, o zaman istisnayönetiminde varsayılan yol (yakalanmayan) kullanılır, yani normal olarakprogram terkedilir. Şimdi aşağıdaki programı yazın derleyin ve çalıştırın,bakalım ne olacak?.

//:B06:firlat.cpp//bak bakalım ne olacakint main( ){ throw “selam”;}///:~

İstisnaların Yakalanması

catch bloğu istisna fırlatıldığı zaman çalışan kodları kapsar. Açıklamalarfırlatıldığı için, catch bloğu ne çeşit istisnaları yöneteceğini bilmekzorundadır. Bundan dolayı catch anahtar kelimesini bir değişken listesitakip eder, tabii bunlardan biri catch bloğunun yöneteceği istisnanıntipidir. char const* istisnalarını yöneten bir istisna yöneticisi, aşağıdakigibi gösterilebilir;

catch (char const* message){ //iletiyi yöneten kodları}

Page 393: C++ 2 (Kitap - Selami KOÇLU)

Daha önce görmüştük, böyle bir ileti statik string olarak fırlatılmakzorunda değildir. Bir işlev istisna olarak fırlatılacak bir string döndürebilir.Eğer böyle bir işlev dinamik fırlatılan istisna stringi oluşturursa, istisnayöneticisi normal olarak kullanılan bellek alanını, bellek kaçağı olmamasıiçin, boşaltır. İstisna yönetcisinin değişkenlerinin karakterine yeterli derecede dikkatlebakılırsa, dinamik üretilen istisnalar işlemlendikten sonra bir kez silinirler.Tabii istisna eğer dış katmandaki istisna yöneticisine aktarılırsa, alınanistisna iç katmandaki istisna yöneticisi tarafından silinmemelidir.Farklı istisna çeşidi fırlatılabilir:char* s, int ler, nesnelerin göstergeçleridayançları vs.. Bütün bu farklı tipler istisnaların fırlatılmasında veyakalanmasında kullanılabilir. Böylece değişik tipte istisnalar try bloğunundışına çıkabilir. Try bloğundan gelen bütün istisnaları yakalamak için çoksayıda istisna yöneticisi (yani catch blokları) try bloğunun ardındangelmelidir.İstisna yöneticisinin düzeni oldukça önemlidir. Bir istisna fırlatıldığındafırlatılan istisnanın tipine uyan ilk istisna yöneticisi kullanılır, sonra gelenistisna yöneticileri gözardı edilir. Böylece try bloğunu takip eden sadecebir istisna yöneticisi çalıştırılır. Sonuç olarak istisna yöneticileri, en özeldeğişkenlere sahip olanlardan, daha genel değişkenlere sahip olanlaradoğru yerleştirilmelidirler. Örnek olarak; istisna yöneticileri eğer char* sve void* s (yani eski tip göstergeç) ile tanımlanırlarsa, o zaman ilk istisnatipi (yani char* s) için olan istisna yöneticisi daha sonraki tipin (void* s)istisna yöneticisinden önce yerleştirilir:

try{ //bütün göstergeç çeşitlerini fırlat }catch (char const* ileti){ //fırlatılan char göstergeçleri işlemlenir}catch (void* neysene){ //bütün öteki fırlatılan göstergeçleri işlemler}

Farklı tiplerdeki istisnaların farklı istisna yöneticilerini inşa etmek içinbaşka bir seçenek olarak; nesneleri istisna hakkında bilgi bulunduran özel

Page 394: C++ 2 (Kitap - Selami KOÇLU)

bir sınıf tasarlanabilir. Böyle bir yaklaşım geçmiş bölümlerden birindeanlatılmıştı. Bu yaklaşım kullanıldığı zaman sadece bir yöneticiye gerekduyulur, zira istisnanın öbür tiplerini fırlatmayacağımızı biliyoruz.

try{ //sadece istisna (Exception) göstergeçlerini fırlatan kodları}}catch (Exception* e){ e->process( ); delete e;}

Yukarıdaki delete e deyimi istisnanın dinamik olarak oluştuğunugöstermektedir.İstisna yönetici kodlarının ötesine yerleşmiş olan try bloğu işlemlendiğizaman, programın çalışması try bloğunu takip eden son istisnayöneticisinin ötesine kadar sürer. (tabii yönetici return, throw veya exit( )gibi işlevlerle ortamdan çıkmadıysa). Bundan dolayı aşağıdaki farklıdurumlarla karşılaşırız:

** try bloğunun içinden herhangi bir istisna fırlatılmadıysa, hiçbir istisnayöneticisi etkinleşmez, programın çalışması try bloğunun son deyimindenson catch bloğunun ötesindeki ilk deyime sürer.** try bloğundan bir istisna fırlatıldıysa, ama o an ki katmanda veya dahasonraki katmanlarda uygun bir istisna yöneticisi bulunmuyorsa, programınvarsayılan yöneticisi çağrılır, bu genellikle programın terki şeklinde olur.** try bloğundan bir istisna fırlatıldıysa ve uygun bir istisna yöneticisivarsa, istisna yöneticisinin kodu işlemlenir. İstisna yönetici kodununçalışmasını takiben, programın çalışması catch bloğu son deyimininötesindeki ilk deyimde sürer.Çalışan bir try bloğunun altında yer alan try bloğundaki deyimlerin hiçbiridikkate alınmaz. Bununla birlikte, try bloğunda yörel olarak tanımlanmışnesnelerin yıkıcı işlevleri, herhangi bir istisna yöneticisinin kodlarıişlemlenmeden çağrılır.İstisna inşası veya asıl hesaplaması, iyileştirmenin değişik derecelerikullanılarak gerçekleştirilir. Örneğin; new işlecini: bir sınıfın statik üyeişlevlerini; bir nesne göstergeci döndürmeği; bir sınıftan türetilmişsınıfların nesnelerini; çokbiçimliliği, kullanmak mümkündür.

Page 395: C++ 2 (Kitap - Selami KOÇLU)

Varsayılan Yakalayıcı

Farklı tiplerde istisnaların fırlatılabildiği durumlarda, programın bellidüzeylerinde sadece sınırlı sayıda yönetici istenir. Böylece sınırlı sayıdakiyöneticilere ait olan istisna tipleri işlemlenir, öteki istisna tipleri ise dahaüst düzey dışarıdaki istisna yöneticilerine aktarılır. İstisna yönetiminin ara katmanında yer alanlar ise, varsayılan istisnayöneticisi tarafından işlemlenir. (bu işlem istisna yöneticilerinin hiyerarşikyapılanmasına göre belirlenir). Özel tipteki istisna yöneticilerinin hemenötesinde yer alırlar. Bu durumda istisna yönetiminin o an ki seviyesindebazı şeyler varsayarak gerçekleştirilir, ama daha sonra boş throw deyimikullanılır, fırlatılan istisna dış katmana gönderilir. Şimdi varsayılan istisnayöneticisinin kullanıldığı bir örnek verelim;

//:B06:varsay.cpp//varsayılan istisna yöneticisi #include <iostream>using namespace std;int main( ){ try{ try{ throw 12.25; //double için belli bir yönetici yok catch (char const* message){ cout<<”Inner level: yakalanan char const* \n; } catch (int value){ cout<<”Inner level: yakalanan int\n”; } catch (... ){ cout<<”Inner level: istisnanin generic kullanimi\n”; throw; } catch (double d){ cout<<”Outer level double olarak bilir: “<<d<<endl; }}///:~

Page 396: C++ 2 (Kitap - Selami KOÇLU)

//üretilen çıktı:Inner level: istisnanın generic kullanimiOuter level double olarak bilir: 12.25//

Örnekten de gördüğümüz gibi; boş throw deyimi, alınan istisnayı tip vedeğerini koruyarak bir sonraki seviyede (dış katmanda) bulunan istisnayakalayıcısına fırlatır: temel veya belli bir kalıba uygun istisna yönetimi içseviyede başarılabilir, ama özgün istisna yönetimleri fırlatılan istisnanıntipine yaslanarak, daha sonra dış katmanlarda gerçeklenir.

İstisna fırlatıcılarının bildirimi

Herhangi bir yerde tanımlanmış işlevler, yine bu işlevler kullanılarak kodile ilişimlenir. Bu tür işlevler normal de, ya tek başlarına işlev olarak yadasınıf üye işlevleri olarak başlık dosyalarında bildirilir.Tabii dış işlevler de istisna fırlatabilirler. Bu tür işlevlerin bildirimlerindeişlev fırlatım listesi veya istisna tanıtım listesi bulunur. Bu listelerde işlevtarafından fırlatılan istisnaların tipleri belirtilir. Örnek olarak char* ve intistisnaları fırlatan bir işlev bildirimi;

void exceptionThrower( ) throw(char*, int);

biçiminde yapılır. Eğer belirtildiyse, işlev fırlatma listesi işlev başlığınınhemen ötesinde görünür (ve yarıca olası const belirtecinin ötesinde), ve,throw listelerinin boş olduğu belirtilebilir, kalıbı aşağıdaki verilmiştir;

throw([type1 [, type2, type3, . . .]])

Eğer bir işlev istisnalar fırlatmazsa, o zaman boş işlev fırlatma listesikullanılabilir. Örnek:

void noExceptions( ) throw( );

Bütün durumlarda işlev tanımında kullanılan işlev başlığı bildirimde

Page 397: C++ 2 (Kitap - Selami KOÇLU)

bulunulan işlev başlığı ile tam uyumlu olmalıdır. Örnek; buna olası boşişlev fırlatma listesi de dahildir.Bir işlev fırlatma listesinin ait olduğu işlev diğer tipte istisnalarıfırlatmayacak şekilde belirtilmelidir. Eğer işlev fırlatma lsitesinde bulunanistisnalardan başka istisnalar fırlatılmaya çalışılırsa, çalışma sırasındahatalar (run time errors=çalışma zamanı hataları) ortaya çıkar. Aşağıdakiörnekte tanım ve bildirimlere dikkat eelim;

//:B06:bak.cpp//tanımlar ve bildirimler#include <iostream>using namespace std;void charPintThrower( ) throw(char const*, int) ; //bildirimlerclass Thrower{ public: void intThrower(int) const throw(int) ;};void Thrower::intThrower(int x) const throw(int){//tanımlarif(x) throw(x);} void charPintThrower( ) throw(char const*, int){ int x; cerr<<”bir int gir: “; cin>>x; Thrower( ).intThrower(x); throw “0 girilirse bu metin ortaya gelir”;}void runTimeError( ) throw(int){ throw 12.5;}int main( ){ try{ charPintThrower( ); } catch (char const* message){ cerr<<”metin istisnasi: “<<message<<endl; } catch (int value){

Page 398: C++ 2 (Kitap - Selami KOÇLU)

cerr<<”int istisnasi: “<<value<<endl; } try{ cerr<<”up-to run time hatasi\n”; runTimeError( ); } catch (....){ cerr<<”erisilemedi\n”; }}///:~//üretilen çıktı: çalışma sırasında ortaya çıkar.

İşlev fırlatma listesi kullanılmasaydı, işlev ya herhangi bir istisnayıfırlatırdı ya da hiç istisna fırlatmazdı. İşlev fırlatma listesi kullanılmazsa,doğru istisna yöneticisi sağlama sorumluluğu, tamamen programtasarımcısına aittir.

İstisnalar ve iostream ler

C++ dilinin girdi/çıktı kütüphanesi (iostream) C++ da istisnalar gündemdeyokken de vardı, yani kullanılıyorlardı. Bu yüzden normalde, iostreamkütüphane sınıfları istisnaları fırlatmazlar. Bununla beraber bu sınıflarındavranışlarını, ios::excetions( ) üye işlevini kullanarak değiştirmekmümkündür. Bu işlevin iki tane, bindirime uğramış biçimi bulunur;

** iostate::exceptions( ) : Bu üye istisnaları fırlatan akışın durumbayraklarını döndürür.** void exceptions(iostate state) : Bu üye state durumu gözlendiğizaman bir istisna fırlatır.

İstisnalar, G/Ç kütüphanesi ile bağlantılı olan, ios::exception dan türetilenios::failure sınıfının nesneleridir. failure nesnesi string const& messageile inşa edilir. Bu, virtual char const* what( ) const üyesinden elde edilir.İstisnalar, sadece istisnai durumlarda kullanılması lazımdır. Bundan dolayıEOF (end of file=dosya sonu) gibi standart sonlandırmalarda, akışnesnelerinin istisna fırlatması sorgulanmamalıdır. Girdi hatalarınıyönetmek için ise, istisnaların fırlatılması tabii savunulabilir, örnek olarak

Page 399: C++ 2 (Kitap - Selami KOÇLU)

girdi hataları olmaması lazım geldiği ve çökmüş bir dosyayı ima ettiğizaman. Burada uygun bir hata iletisi ile durumu bildirip, programdançıkmak genellikle en doğru davranıştır. Şimdi istisnaların kullanımınıetkileşimli, sayılar bekleyen programla gösterelim;

//:B06:expnum.cpp//saylar bekleniyor#include <iostream>using namespace::std;int main( ){ cin::exceptions(ios::failbit); while(true){ try{ cout<<”bir sayi gir: “; int value; cin>>value; cout<<”girdigin sayi: “<<value<<endl; } catch (ios::failure const& problem){ cout<<problem.what( )<<endl; cin.clear( ); string s; getline(cin, s); } }}///:~

Yapıcı ve Yıkıcı işlevlerde İstisnalar Sadece “inşa edilmiş” olan nesneler yeri geldiği zaman yıkıcı işlevlercesilinirler. Bu cümle doğrudur ama, yine de bir incelik taşımaktadır. Buinceliğe gelelim şimdi; ya nesnenin inşası çeşitli sebeplerdentamamlanamışsa, o zaman nesne görüntü dışına çıktığında, yıkıcı işlev çağrılamaz. Yapıcı işlev tarafından yakalanamayan istisnaüretildiğinde, bu vuku bulmuş demektir. Nesne belleğe yerleşirken (yanitam doğru yerleşim yok) sonra istisna fırlatılırsa, o zaman kaplanan bellekalanını silmek için yıkıcı işlev görev yapamaz. Sonuç olarak, bellek kaçağı

Page 400: C++ 2 (Kitap - Selami KOÇLU)

ortaya çıkar. Aşağıdaki örnek bunun basit biçimini göstermektedir; Incomplete sınıfıönce bir ileti göstermekte daha sonra istisna fırlatmaktadır. Yıkıcı işlevi debir ileti göstermektedir.

class Incomplete{ public: Incomplete( ){ cerr<<”bellek alani biraz kaplandi\n”; throw 0; } ~Incomplete( ){ cerr<<”kaplanan bellek alani siliniyor\n”; }};

Daha sonra main( ) işlevinde try bloğu içinde Incomplete nesnesioluşturulur. Üretilen herhangi bir istisna yerine göre yakalanır.

int main( ){ try{ cerr<<”'Incomplete' nesne olusumu\n” ; Incomplete( ); cerr<<”nesne olusturuldu\n”; } catch (... ){ cerr<<”istisna yakalandi\n”; }}///:~

Bu program çalıştırıldığı zaman aşağıdaki sonuç üretilir:

'Incomplete' nesne oluşumubellek alani biraz kaplandi istisna yakalandi

Bu yüzden eğer Incomplete yapıcı işlevi bir nesne oluşturmuş olsaydı

Page 401: C++ 2 (Kitap - Selami KOÇLU)

program bellek kaçağına maruz kalacaktı. Bunu engellemek için aşağıdakiönlemler alınabilir;

** İstisnalar yapıcı işlevi terketmemeliler. Yapıcı işlev kodunda bir yerdeistisnalar üretilirse, o zaman kodun bu yeri try bloğunca kuşatılmalı veyapıcı işlevin içinde istisna yakalanmalıdır. Tabii, yapıcı işlevin dışındanda istisnaları fırlatmanın geçerli nedenleri olabilir, nesnenin henüzoluşmadığı yapıcı işlev kullanılarak kod doğrudan bilgilendirilebilir. Lakinistisna yapıcı işlevi terketmeden önce, kullanılmış olan bellek alanınısilmek için bir şans verilmesi lazımdır. Bunun nasıl yapılacağı aşağıdakiyapıcı işlev örneğinden görülebilir. Burada dikkat edilecek nokta; üretilenherhangi bir istisnanın dış kodlar kullanılıp nasıl yeniden fırlatılacağıdır.

Incomplete::Incomplete( ){ try{ d_memory=new Type; code_maybe_throwing_exceptions( ); } catch (... ){ delete d_memory; throw; }};

** İstisnalar üyelere ilk değer atamaları yapılırken ortaya çıkabilir. Böyledurumlarda yapıcı işlev gövdesinde yer alan try bloğunun, istisnalarıyakalama şansı yoktur. Bir sınıf göstergeç veri üyeleri kullandığı zaman,ve bu veri üyelerinin ilk değer atamaları yapılırken istisnalar ortayaçıkarsa, bellek kaçaklarından yine de kurtulunabilir. Bu cingözgöstergeçler (smart pointers) kullanılarak başarılabilir. Örnek olarakauto_ptr nesnelerinin kullanımını gösterebiliriz. auto_ptr nesneleri nesneoldukları için, bunların nesne oluşumları tamamlanmasa bile yine de yıkıcıişlevleri çağrılır. Bu durumda temel kuralımız olan “nesne tamoluşturulduğunda, görüntü dışına çıktığı zaman, yıkıcı işlevi çağrılır.” halageçerlidir. auto_ptr nesnelerinin kullanımı istisnalar yapıcı işlev dışına fırlatıldığızaman, hatta istisna üye başaltıcı (ilk değer atayıcısı) tarafında üretilse bile,bellek kaçaklarını engeller.

Page 402: C++ 2 (Kitap - Selami KOÇLU)

Bunların yanında C++, işlevlerden (veya yapıcı işlevlerden) çıkanistisnaları engellemek için, daha düzenli yapılar da bulundurmaktadır; işlevdeneme bloklar (function try block). Bunları bir sonra ki bölümdeinceleyeceğiz. Yıkıcı işlevler istisna fırlattıklarında, zaten kendileri sorunlu demektir.İstisna fırlatıldığında kaplanan bellek alanının tamamının silinmemesiyüzünden, yıkıcı işlevlerden çıkan istisnalar da bellek kaçaklarına yolaçar.Tamamlanmamış uygulamaların başka çeşitlerine de rastlanabilir. Örnekolarak; bir veritabanı sınıfı, veritabanında meydana gelen değişikleribellekte saklamaktadır, veritabanını bulunduran dosyanın güncellenmesigörevi yapıcı işleve bırakılmıştır. Dosya güncellenmesinden önce yıkıcıişlev istisna üretirse, artık güncelleme yani yenileme olmaz. Fakat bu kadardeğil, başka bir, çok daha ince, yıkıcılardan kaynaklanan bir istisna dahabulunmaktadır. Bunu bir benzetmeyle anlatmayla çalışalım; bir marangozvar tek çekmeceli bir dolap inşa ediyor. Dolap bitiyor, müşteri geliyordolabı alıp parayı ödüyor, ve daha sonra dolabın istediği gibi çalıştığınıgörüp mutlu oluyor. Dolabıyla mutlu müşteri daha sonra marangoza yinegeliyor bu sefer iki çekmeceli bir dolap siparişi veriyor. İkinci dolapbittiğinde müşteri gelip alıyor ve evine götürüyor. Ama müşteri ikiçekmeceli dolabını ilk kullandığında, dolap parça pinçik oluyor. Müşteriacayip şaşkın, ilk dolap fıstık gibi idi, ikincisi rezalet.Nasıl uzun hikaye değil mi?. Şimdi aşağıdaki programa bakalım;

//:B06:bak_bakalim.cpp//iyi inceleyinint main( ){ try{ cerr<<”Dolap1 i yap\n”; Dolap1( ); cerr<<”Dolap1 nesne ilerisi\n”; } catch (... ){ cerr<<”Dolap1 beklendigi gibi\n”; } try{ cerr<<”Dolap2 yi yap\n”; Doalp2( ); cerr<<”Dolap2 nesne ilerisi\n”; }

Page 403: C++ 2 (Kitap - Selami KOÇLU)

catch (.... ){ cerr<<”Dolap2 beklendigi gibi\n”; }}

Bu program çalıştırıldığı zaman aşağıdaki sonuçalr üretilir:

Dolap1 i yapCekmece 1 kullanildiDolap1 beklendigi gibiDolap2 yi yapCekmece 2 kullanildiCekmece 1 kullanildiTerket

Yukarıda görüldüğü üzere “Dolap2 beklendigi gibi” yerine, terket emrigelmiş programdan çıkılmıştır. Şimdi 3 tane sınıf inşa edelim ve bunlarbildiğimiz sınıflar olsun, ama sadece Cekmece sınıfnda yıkıcı işlev istisnafırlatsın.

class Cekmece{ unsigned d_nr; public: Cekmece(unsigned nr) : d_nr{} ~Cekmece( ){ cerr<<”Cekmece”<<d_nr<<” kullanildi\n”; throw 0; }};

class Dolap1{ Cekmece left; public: Dolap1( ) : left(1){}};

Page 404: C++ 2 (Kitap - Selami KOÇLU)

class Dolap2{ Cekmece left; Cekmece right; public: Dolap2( ) : left(1), right(2){}};

Dolap1 in yıkıcı işlevi çağrıldığı zaman, Cekmece nin yıkıcı işlevi harçbiçiminde oluşturulmuş olan nesneyi silmek için, sonunda çağrılır. Buyıkıcı işlevin fırlattığı istisna, programın ilk try bloğunun ilerisindeyakalanır. İşler tamamen beklendiği gibi gerçkleşir. Bununla birlikteDolap2 nin yıkıcı işlev çağrımı ile, sorun ortaya çıkar. İki harçnesnesinden, ikinci Cekmece nin yıkıcı işlevi önce çağrılır. Bu yıkıcı işlev,programın ikinci try bloğunun ilerisinde yakalanabilen, bir istisna fırlatır.Bununla birlikte denetim akışı o zamn Dolap2 nin yıkıcı işlevi ilebağlantılı olarak bırakılmasına rağmen, o nesne tamamen silinemez, henüzdiğer Cekmece (left) nin yıkıcı işlevi çağrılmak zorundadır. Normalde bubüyük bir sorun değildir; Cekmece2 nin yıkıcı işlevinden çıkan istisna birkez fırlatılır, geri kalan eylemler gözardı edilir, left in yıkıcı işlevi çağrılsabile bu böyledir. Bununla birlikte left in yıkıcı işlevi de bir istisna fırlatır.İkinci try bloğunun bağlantısını bıraktığımız için, programlanmış akışdenetimi tamamen karışır, ve programın başka bir seçeneği kalmadığındanterk işlemi olur. Bu da sıra ile, terminate( ) veya abort( ) işlevi çağrılarakyerine getirilir. İşte şimdi tek çekmeceli dolap mükemmel çalışsa bile, ikiCekmeceli parça pinçik olan dolabımız var.Program terkedilir, zira yıkıcıları terkeden, yıkıcı işlevlerin fırlattığıistisnaları olan çok sayıda harç nesnesi bulunur. Bu durumda harçnesnelerinden bir tanesi, program akış denetimi uygun ilişkiye bırakıldığıanda bir istisna fırlatır. Bu da programdan çıkılmasını, yani programınterkini sağlar.Eğer istisnaların yıkıcıları hiçbir zaman terketmemesi güvenceye alınırsa,bu durum kolaylıkla engellenebilir. Dolap örneğinde, Cekmece nin yıkıcısıyıkıcı işlevden çıkan bir istisna fırlatır. Bunun olmaması lazım: istisnaCekmece yıkıcısının kendisi tarafından yakalanmış olması lazımdır. Yıkıcıişlevlerin dışına hiçbir zaman istisna fırlatılmadığında, bizim de dışkatmanda istisnaları yakalayabilmemiz mümkün olmaz. Aslında bu ciddibir sonuç değildir zira, yıkıcı işlevler denetim akışı ile değil, doğrudansilinecek nesnelerle bağlantısı olan görev üyeleridir. Şimdi örnek olarak

Page 405: C++ 2 (Kitap - Selami KOÇLU)

istisnalar fırlatan bir yıkıcı işlev iskeleti verelim.

Class::~Class( ){ try{ maybe_throw_exceptions( ); } catch (... ){ }}

try işlev blokları

Yapıcı işlev, üyelerinin ilk değerlerini atarken istisnalar ortaya çıkabilir.Böyle durumlarda yapıcı işlevin dışında değil de, bizzat yapıcı işlevinkendisi tarafından yakalanabilen istisnalar nasıl üretilebilir?. Nesneoluşumunu, yuvalama yolu ile yuvalı try bloğuna yerleştirmek zekice birçözüm olmadığı gibi, çekici de olmaz. Sebebi ise yeni yuvalama (biraz dayapay) seviyesi eklenmesidir. Aşağıdaki örnekte, DataBase sınıfının nesnesini tanımlayan, main( )işlevinin bulunduğu yuvalı try bloğu kullanılmıştır. DataBase yapıcıişlevinin bir istisna fırlattığı kabul ediliyor, dış blokta bu istisnayıyakalayabilmek (yani main( ) de çağıran kod) imkansız, ve bu durumda dışkatman yok demektir. Bu nedenle biz de daha az estetik çözümüseçmekliyiz, aşağıya bakınız;

//:B06:not_elegant.cpp//pek estetik olmayan çözümint main(int argc, char** argv){ try{ DataBase db(argc, argv); //istisnaları fırlatır .... //main( ) işlevinin öteki kodları } catch (... ){ //ve/veya öbür yöneticiler

...... }}///:~

Page 406: C++ 2 (Kitap - Selami KOÇLU)

Bu yaklaşımın oldukça karışık kod üretme potansiyeli vardır. Çok sayıdanesne tanımlanacak olursa veya, try bloğunda çok sayıda istisna kaynaklarıtanımlanırsa, ya çok sayıda karmaşık istisna yöneticilerimiz olur, veya çoksayıda yuvalı try bloğu kullanmak zorunda kalırız, ve tabii herbiri kendiyakalama (catch) yöneticisine sahiptir.Tabii bu yaklaşımların hiçbiri temel sorunu çözmez; yörel bağlantı ortadankaybolmadan önce, yörel bağlantıda üretilen istisnalar nasıl yakalanabilir?.Bir işlevin yörel bağlantısı, işlevin gövdesi try işlev bloğu olaraktanımlandığı zaman erişilebilir kalır. Bir try işlev bloğu, işlev gövdesinitanımlayan try bloğu ile onun yöneticilerinden oluşmuştur. Try işlev bloğukullanıldığı zaman, istisnalar yapıcı işlev başlatıcı listelerindenkaynaklansa bile, işlevin kendisi kendi kodunun ürettiği istisnalarıyakalayabilir.Aşağıdaki örnek, önceki main( ) işlevinde bir try işlev bloğununyerleşimini göstermektedir.

int main(int argc, char** argv){ try{ DataBase db(argc, argv); //istisnaları fırlatır ..... //öbür kodlar } catch(... ){ //ve diğe yöneticiler ....... }

Tabii bu hala bize DataBase yapıcı işlevince fırlatılan istisnaların,DataBase yapıcı işlevince yakalanması imkanını vermez. Bununla birliktetry işlev blokları yapıcı işlevleri oluştururken kullanılabilir. Bu durumdaana sınıf veya üye başlatıcıları tarafından fırlatılan istisnalar, yapıcı işlevinistisna yöneticileri tarafından yakalanabilir. Aşağıdaki örnek yapıcı işlevinbu doğrultudaki kullanımını göstermektedir. Try anahtar kelimesininbulunduğu yere dikkat edin!! (üye başlatma listesindeki önceliğine!!)

//:B06:try_yeri.cpp//yapıcı işlevde try yeri

Page 407: C++ 2 (Kitap - Selami KOÇLU)

#include <iostream>class Throw{ public: Throw(int values) try{ throw value; } catch(... ){ std::cout<<”Throw( ) tarafından yönetilen 'Throw' un istisnası\n”; throw; }};class Composer{ Throw d_t; public: Composer( ) try //Dikkat!! ilk değer atamalarından önce gelen try! : d_t(5){} catch(.... ){ std::cout<<”Composer( ) istisnayı yerinde yakalar\n”; }};int main( ){ Composer c;}///:~//üretilen çıktı:Throw( ) tarafından yönetilen 'Throw' un istisnasıComposer( ) istisnayı yerinde yakalar //

Yukarıdaki örnekte, Throw nesnesi tarafından fırlatılan istisna öncenesnenin kendisi tarafından yakalanır. Daha sonra tekrar yeniden fırlatılır.Composer yapıcı işlevi bir try işlev bloğu kullanırken, istisna ilk değeratama listesinin içinde üretilmiş olsa bile, Throw un yeniden fırlatılanistisnası Composer un istisna yöneticisi tarafından ayrıca yakalanır.Son bir not; try işlev bloğu kullanan bir işlev veya yapıcı işlev ayrıcafırlatılacak istisna tiplerini bildirir, daha sonra try işlev bloğu işlevinistisnalarını belirten listeyi takip etmek zorundadır.

Page 408: C++ 2 (Kitap - Selami KOÇLU)
Page 409: C++ 2 (Kitap - Selami KOÇLU)

7. Bölüm

SKK nesne işlevler

Algoritmalarda kullanılan işlevik değişkenler işlev olmak zorundadeğildirler. İşlevler gibi davranan nesneler olabilirler. Böyle bir nesneyenesne işlev, işlevci veya functor (telaffuzu: fanktır) denir. Bu tür nesnelerbazen normal işlevler isteneni yapamadığı zaman kullanılabilir. SKK(standart kalıp kütüphanesi) sık sık nesne işlevleri kullanır, ve çok yararlıbir sürü nesne işlevi sağlar.

Nesne işlevleri

Page 410: C++ 2 (Kitap - Selami KOÇLU)

Nesne işlevleri kalıplı programlamanın gücünün, ve saf soyutlamanın birörneğidir. İşlev gibi davranan bir öğeye işlev diyebilirsiniz. İşlev gibidavranan bir nesne tanımlarsanız, o zaman onu işlev gibi kullanabilirsiniz.Peki sizce işlev davranışı nedir?. Yanıt: işlevik davranış öyle bir şeydir ki,siz onu ayraçlar kullanarak ve değişkenler atayarak çağırabilirsiniz. Örnek:

fonksiyon(arg1, arg2); //bir işlev çağrısı

İşte bu yüzden nesnelerin de böyle davranması istenirse, onlar da ayraçlarkullanarak ve değişkenler atayarak çağrılmaları lazımdır. Evet bu mümkün.(aslında C++ da hemen her şey mümkündür). Yapılacak bütün şey; uygundeğişken tiplerine sahip operator( ) ü tanımlamaktır.

class S{ public: //'işlev çağrı' işlecini tanımlayın dönüş değeri operator( ) (değişkenler) const; .....};

Şimdi artık, çağırdığınız işlevler gibi davranan bu sınıfın nesnelerinikullanabilirsiniz.

S fo;. ..fo(arg1, arg2); //nesne işlevi fo için operator( ) ü çağırın

Bu çağrının eşdeğeri:

fo.operator( ) (arg1, arg2); //yukarıdakine eşdeğer çağrı

Aşağıda tam bir örnek verilmiştir.

//:B07:herbiri.cpp//nesne işlev kullanımı #include <iostream>#include <algorithm>#include <vector>

Page 411: C++ 2 (Kitap - Selami KOÇLU)

using namespace std;class PrintIn{ public: void operator( ) (int eleme) const{ cout<<eleme<<' '; } }; //aktarılan değişkenleri yazdıran nesne işlevi int main( ){ vector<int> coll; for(int i=0; i<=9; ++i){ //1 den 9 a öğeleri yerleştir coll.push_back(i); } //bütün öğeleri yaz for_each(coll.begin( ), coll.end( ), PrintIn( )); cout<<endl;}///:~

Şimdi merak edeceksiniz, bunun bize ne faydası oldu. Hatta bu nesneişlevlerini, acayip, kodları karıştıran yazım biçimi olduğunudüşünebilirsiniz. Evet kodların biraz karışık göründüğü doğrudur. Amanesne işlevleri, işlevlerin bizzat kendilerinden daha fazla yeteneklidirler.İşte üstünlükleri:

** Nesne işlevler “cingöz işlevlerdir”Bilindiği gibi göstergeç gibi davranan nesneler “cingöz göstergeç” lerdir.Aynı şey, işlev gibi davranan nesneler için de geçerlidir. Bunlar operator( ) den daha fazla yeteneğe sahip oldukları için, “cingöz işlev”olabilirler. Nesne işlevler, başka üyeler ve özelliklere de sahip olabilirler.Bunun anlamı; nesne işlevinin bir durumu vardır. Aslında nesne işlevincetemsil edilen aynı işlev, aynı anda birden fazla duruma sahip olabilir. Bunormal işlevler için geçerli değildir. Nesne işlevlerinin başka birüstünlüğü, program işlerken kullanılmadan hemen önce ilk değer atamasınıyapabilirsiniz, yani nesne işlevine dinamik atama gerçekleştirebilirsiniz.

** Her nesne işlevinin kendi tipi vardır.Normal işlevler sadece imzaları farklı olduğu zaman farklı tipleresahiptirler. Nesne işlevleri ise, imzaları aynı olsa dahi farklı tiplere sahipolabilirler. Aslında nesne işlevin tanımladığı her işlevik davranış içinkendi tipi bulunur. Bu, kalıpların kullanıldığı kalıplı programlamada

Page 412: C++ 2 (Kitap - Selami KOÇLU)

önemli bir araçtır, böylece işlevik davranışı kalıp değişkeni olarakaktarabilirsiniz. Sıralama kıstası olarak aynı nesne işlevini farklı tiplerinkaplarında kullanabilirsiniz.. Farklı sıralama kıstası olan topluluklarınatamalarını, bileştirilmelerini, ve karşılaştırılmalarını yapmamanızıgüvenceye alır. Hatta siz, farklı özel bir çeşit genel kıstasa sahip, işlevnesneler hiyerarşileri tasarlayabilirsiniz.

** Nesne işlevler normal işlevlerden genellikle daha hızlıdırlar.Kalıplar, derleme esnasında daha fazla ayrıntı tanımlandığı için daha çokiyileştirmeye izin verirler. Bu yüzden normal işlevler yerine nesne işlevleriaktarmak, daha üstün başarılar sağlar.

Bu bölümün geri kalan kısmında, nesne işlevlerin normal işlevlere görenasıl daha cingöz (veya akıllı) olabildiklerini anlatacağız. Daha sonra daayrıntılara gireceğiz.Bir kümede bulunan bütün öğelere belli bir değerin eklendiğini varsayalım.Derleme esnasında siz bu eklenecek değeri biliyorsanız, normal bir işlevibu iş için kullanabilirsiniz.

void add100(int& elem){ elem+=100;}void f1( ){ vector<int> coll; ..... for_each(coll.begin( ), coll.end( ), add100);}

Eğer derleme esnasında bilinen farklı değerleri eklemek istiyorsanız, ozaman kalıp kullanmalısınız.

template<int theValue>void add(int& elem){ elem+=theValue;}void f1( ){ vector<int> coll; .... for_each(coll.begin( ), coll.end( ), add100);}

Page 413: C++ 2 (Kitap - Selami KOÇLU)

Derleme esnasında farklı değerlere gerek duyulursa, kalıp kullanabilirsiniz.

template <int theValue>void add(int& elem){ elem+=theValue;}void f1( ){ vector<int> coll ; ... for_each(coll.begin( ), coll.end( ), add<100>);}

Yok, derleme esnasında değil, program çalışırken değerleri eklemekisterseniz, olaylar karmaşıklaşır. Zira işlev çağrılmadan önce işleve değeraktarımı zorunludur. Bunun sonucunda hem algoritmayı çağıran işlev, hemde toplamayı yapan algoritmayı çağıran işlevde kullanılan globaldeğişkene gerek duyulur. Tabii bu müşkül bir durumdur. Karışık bir çözümyani. Böyle bir işleve iki kere gerek duyarsanız, ve iki farklı değer eklemekisterseniz, ve her iki değerde program çalışırken eklenirse, bunu bir tanenormal işlevle yapamazsınız. Ya bir işaret eklersiniz, veya iki farklı işlevtanımlarsınız, yani buna mecbursunuz. Hiç, durumunu korumak için staticdeğişkeni olan, ve aynı anda başka bir durumu için de aynı işleve ihtiyaçduyduğunuzda, bir işlev tanımını kopyaladınızmı?. İşte durum buradaaynen böyledir.İşlev nesneleri ile istendiği gibi davranan daha “cingöz” işlevleryazabilirsiniz. Zira nesnenin bir durumu vardır, ve siz onu doğru değerlebaşlatabilirsiniz. İşte size buna uygun tam bir örnek;

//:B07:add1.cpp//işlev nesnesi örneği #include <iostream>#include <list>#include <algorithm>#include “print.hpp”class AddValue{ private: int Value; //eklenecek değer

Page 414: C++ 2 (Kitap - Selami KOÇLU)

public: AddValue(int v) : Value(v){ } //yapıcı işlev eklenecek değerle başlatılır void operator( ) (int& elem) const{ //değeri ekleyen işlevin çağrısı eleme+=Value; }};int main( ){ list<int> coll; for_each(coll.begin( ), coll.end( ), AddValue(10));

//for_each( ) in ilk çağrılışında her değere 10 eklenir. Burada //AddValue(10) açıklaması, AddValue tipinde, ilk değeri 10 olan bir //nesne oluşturur. AddValue yapıcı işlevi bu değeri Value üyesi olarak //saklar. for_each( ) içinde ise coll ün her öğesi için “( )” çağrılır. Ayrıca//bu, aktarılan geçici AddValue tipindeki nesne işlevinin operator( )//çağrısıdır. Asıl öğe değişken olarak aktarılır. Nesne işlevi 10 değerini //her öğeye ekler. Öğeler aşağıdaki değerlere sahip olurlar:

10 ekledikten sonra: 11 12 13 14 15 16 17 18 19

//for_each( ) in ikinci çağrılışında ilk öğenin değeri bütün öğelere tek tek//eklenir. Burada ilk öğe değeri ile geçici nesne işlev başlatılır. Yani:

AddValue(*coll.begin( ))

//şimdi çıktı: 22 23 24 25 26 27 28 29 30

Bu teknik kullanılarak iki farklı nesne işlevi ile, aynı anda iki durumasahip işlevli sorunlar çözülebilir. Örneğin; iki tane nesne işlev bildirip,bunları birbirinden bağımsız kullanabilirsiniz.

AddValue addx( ); //nesne işlev x değeri ekler.AddValue addy( ); //nesne işlev y değeri ekler.for_each(coll.begin( ), coll.end( ), addx); //her öğeye x değerini eklerfor_each(coll.begin( ), coll.end( ), addy); //her öğeye y değerini ekler

Benzer şekilde yeni üye işlevlerle nesne işlevlerin durumunu sorgulayabilirveya değiştirebilirsiniz.

Page 415: C++ 2 (Kitap - Selami KOÇLU)

Öntanımlı nesne işlevler C++ standart kütüphanesinde temel işlemler için, çok sayıda öntanımlınesne işlevi bulunmaktadır. Bunları kullandığınızda birçok durum içinkendi nesne işlevlerinizi yazmak zorunda kalmazsınız. Tipik örnek olarak,sıralama kıstası olarak kullanılan nesne işlevini verebiliriz. Operator< ünvarsayılan sıralama kıstası, less< > öntanımlı sıralama kıstasıdır. Eğer;

set<int> coll; //bildirirseniz

set<int, less<int> > coll; //öğeleri < ile sırala

ters sıralama için ise;

set<int, greater<int> > coll; //öğeleri > ile sırala

Benzer şekilde çok sayıda nesne işlevi, sayılarla işlem yapabilirler.Örneğin aşağıdaki deyim bir topluluktaki öğelerin tamamının tersini alır;

transform(coll.begin( ), coll.end( ), //kaynak coll.begin( ), //varış negate<int> ( )); //işlem

negate<int> ( ) açıklaması öntanımlı kalıp sınıfı negate in nesne işlevinioluşturur. Çağrılan int tipi öğelerin tersini geri döndürür. transformalgoritması ilk topluluktaki bütün öğeleri ikinci topluluktaki öğeleredönüştürür. Kaynak ve varış örneğimizde olduğu gibi aynı ise, o zamandönen terslenmiş öğeler öncekilerin üzerine yazılır. Böylece bütün öğelerintersleri alınmış olur. Benzer şekilde bir topluluktaki bütün öğelerin kareleriniişlemleyebilirsiniz;

transform(coll.begin( ), coll.end( ), //ilk kaynak coll.begin( ), //ikinci kaynak coll.begin( ), //varış multiplies<int> ( )); //işlem

Page 416: C++ 2 (Kitap - Selami KOÇLU)

Öntanımlı nesne işlevler

Nesne işlevler kalıplı algoritmalarla birlikte, programlarda önemli rolleralırlar. Örnek verecek olursak, sıralaması yapılacak olan nesnelerin alansınırlarını koyan yineleyicileri ile birlikte, SKK da sort algoritmasıbulunmaktadır. Burada bir nesne işlev, iki nesne arasındaki karşılaştırmayıyapmak için uygun işleci çağırır. Şimdi bu duruma yakından bakalım.Stringlerin bir vector de saklandığını kabul edelim, ve vector ün öğeleriniazalan sıra ile dizelim. Bu durumda vector stringVec ün sıralamasıaşağıdaki gibi basit olur;

sort(stringVec.begin( ), stringVec.end( ), greater<std::string>( ));

son değişken bir yapıcı işlev olarak düşünülmelidir: bu stringlereuygulanan, greater< >( ) kalıp sınıfının anlık durumunu verir. Bu nesnesort algoritması tarafından nesne işlevi olarak adlandırılır. operator( )( )işleci çağrılmış olsa bile, veri tipinin (burada std::string) operator>( )işlecini çağırır. Sonuç olarak sort algoritmasının geri dönüşünde, vector ünilk öğesi en büyük öğedir. Burada operator( )( ) ün kendisi görünmez: greater<string>( ) ile çağıranoperator( )( ) ün ayraçlarını birbirine karıştırmayın. Bu işleç aslında sortiçinde kullanıldığı zaman iki değişken alır; yani daha büyüklüğü saptamakiçin iki string. İçeride ise, yineleyicilerin işaret ettiği veri tipinin operator>( ) işleci, iki nesneyi karşılaştırmak için greater<string> in işleçişlevince (operator( )( ) ) çağrılır. greater< > ın işlev çağrı işleci satıriçiolarak tanımlandığından, çağrının kendisi aslında kodda görülmez. Dahaziyade, greater< >::operator( )( ) ü çağırdığı düşünülerek sort( ),string::operator>( ) çağırır.Artık algoritmaya (çoğuna), bir yapıcı işlevin değişken olarak aktarıldığınıbiliyoruz. Şimdi kendi nesne işlevlerimizi tasarlayabiliriz. Burada karakterbüyüklüğü ve küçüklüğüne bakmadan vector ümüzü sıraladığımızdüşünelim. Önce burada hemen belirtmemiz gereken, string::operator<( )varsayımı uygun değildir. Zira bu varsayım, karakter büyüklüğü ileküçüklüğünü dikkate alır. O zaman bizim case_less sınıfını inşa etmemizlazım. Bu sınıf iki string i karakter boyutlarına bakmaksızın karşılaştırır.Aşağıdaki program standart C işlevi olan strcasecmp( ) yi kullanarak bu nu

Page 417: C++ 2 (Kitap - Selami KOÇLU)

gerçekler. Program komut satırı değişkenlerini artan sırada alfabetikolarark düzenler.

//:B07:sıral.cpp//artan sırada alfabetik düzenle#include <iostream>#include <functional>#include <string>#include <algorithm>using namespace std;class case_less{ public: bool operator( )(string const& left, string const& right) const{ return strcasecmp(left.c_str( ), right.c_str( ))<0; }};int main(int argc, char** argv){ sort(argv, argv+argc, case_less( )); for(int idx=0; idx<argc; ++idx) cout<<argv[idx]<<” “; cout<<endl;}///:~

Yukarıda case_less sınıfının varsayılan yapıcı işlevi, sort algoritmasındason değişken olarak kullanıldı. Bu yüzden case_less sınıfı ile tanımlanmasızorunlu tek üye işlev; operator( )( ) nesne işlev işlecidir. stringdeğişkenlerle çağrıldığını bildiğimizden, strcasecmp( ) işlevinde kullanılanonu iki string değişken bekleyen olarak tanımlarız. Daha da ötesi operator( )( ) işlevi satıriçi yapılır. Böylece sort işlevince çağrıldığında,masrafa neden olmaz. sort işlevi, string lerin değişik kombinasyonlarınıbulunduran nesne işlevini çağırır. Yani öyle olduğunu düşünür. Bununlabirlikte, aslında satıriçi olarak belirtilen case_less::operator( )( ) ya bağlıolarak strcasecmp( ) yi çağırır.Karşılaştırmada nesne işlevi olarak çoğu kez öntanımlı nesne işlevikullanılır, zira bunlar çok sayıda değişik işlemde yaygın olarak işe yarar.Aşağıdaki bölümlerde çok sayıda öntanımlı nesne işlevi örnek kullanımlarıile birlikte anlatılacak. Nesne işlevleri ile ilgili bölümün sonunda ise,uyarlayıcı işlevler (function adaptors) hakkında bilgi verilecek. Öntanımlı

Page 418: C++ 2 (Kitap - Selami KOÇLU)

nesne işlevi kullanılmadan önce mutlaka aşağıdaki önişlemleyici yönergesiprograma yazılmış olmalıdır:

#include <functional>

Öntanımlı nesne işlevleri esasen kalıp algoritmaları ile birlikte kullanılırlar.Öntanımlı nesne işlevleri; aritmetik, ilişkici ve mantık işlemlerinde yeralırlar. Daha sonra da, bit biçimli (bitwise) bir örnek vereceğiz.

Aritmetik nesne işlevleri

Aritmetik nesne işlevler bildiğimiz aritmetik işlemleri yapar: toplama,çıkarma, çarpma, bölme, mod alma ve tersleme. Bu öntanımlı nesneişlevler ilgili veri tipinin denk gelen işlecini uyarırlar. Örnek; toplama içinplus<Type> nesne işlevi vardır. Eğer tipi unsigned olarak belirtirsek, ozaman + işleci için unsigned değerler kullanılır. Eğer tipi string olarakbelirtirsek, o zaman da + işleci string ler için işlem yapacak şekildekullanılır. Örnek;

//:B07:ar_nes_is.cpp//aritmetik nesne işlev kullanımı#include <iostream>#include <functional>#include <string>using namespace std;int main(int argc, char** argv){ plus<unsigned> uAdd; //unsigned ları eklemek için nesne işlevi cout<<”3+5= “<<uAdd(3, 5)<<endl; plus<string> sAdd; //string leri ekleyen nesne işlevi cout<<”argv[0]+argv[1]= “<<sAdd(argv[0], argv[1])<<endl;}///:~//üretilen çıktı:a.outgoing çağrısı ile3+5=8argv[0]+argv[1]=a.outgoing

Page 419: C++ 2 (Kitap - Selami KOÇLU)

//

Ee bunun ne faydası var diyeceksiniz. Nesne işlevi bütün veri tipleri içinkullanılabilir (yani sadece öntanımlı oldukları veri tipi için değil), buradaherhangi bir veri tipine uygun olarak ilgili işleç bindirime uğratılır. Örnek:bir yandan ortak bir değişkende bir işlem gerçekleştirmek isteyelim, öteyandan da, bir dizinin her öğesine sıra ile işlem yapalım. Yani bir dizininöğelerinin toplamını hesaplayalım, veya metin biçimli dizinin öğeleriniardarda ekleyelim. İşte buna benzer durumlarda nesne işlevler gayetkullanışlıdırlar. Daha önce de belirttiğimiz gibi, nesne işlevler kalıpalgoritmalarla bağlantılı olarak yoğun şekilde kullanılırlar, şimdi buuygulamalardan birine bakalım.Algoritmlardan birisi de, accumulate( ) tir. Bu algoritma yineleyicilerinişaret ettiği alandaki öğeleri ziyaret eder, ortak bir öğede ve alandaki heröğede istenen bir ikici (binary) işlemi gerçekler, ve bütün öğeleri ziyaretettikten sonra toplam sonucu geri döndürür. Aşağıdaki örnek programda,bütün komut satırı değişkenleri toplanır ve son string olarak yazdırılır.

//:B07:yaz_bakalim.cpp//bütün komut satırını tek string e yerleştir#include <iostream>#include <string>#include <functional>#include <numeric>using namespace std;int main(int argc, char** argv){ string result= accumulate(argv, argv+argc, string( ), plus<string> ( )); cout<<”butun eklenen ardisik ogeler: “<<result<<endl;}///:~

İlk iki değişken yineleyicilerin ziyaret ettiği alanın sınırlarını belirtir.Üçüncü değişken ise string( ) tir. Anonim string nesne ilk değer atamasınıyani başlatmayı sağlar. Ve böylece başlatılır;

string(“butun eklenen ardisik ogeler: ”);

Page 420: C++ 2 (Kitap - Selami KOÇLU)

ve cout nesnesi de aşağıdaki gibi basitleşir;

cout<<result<<endl;

Daha sonra plus<string> ( ) işleci uygulanır. Hemen belirtelim buradaçağrılan yapıcı işlev plus<string> değil, plus<string> ( ) dir. En sonundaardarda eklenmiş string öğelerden oluşan bir string elde edilir. Şimdi de kendi Time sınıfımızı tanımlayıp, operator+( ) işlecini bindirimeuğratalım. Yine plus öntanımlı nesne işlevimizi kullanarak, tanımladığımızveri tipimizle zamanları toplayalım.

//:B07:zaman_topla.cpp//yeni veri tipini plus ile kullan#include <iostream>#include <string>#include <functional>#include <numeric>#include <sstream>#include <vector>using namespace std;class Time{ friend ostream& operator<<(ostream& str, Time const& time){ return cout<<time.d_days<<” days, ”<<time.d_hours<< ” hours, “<<time.d_minutes<<” minutes and “<< time.d_seconds<<” seconds. “; } unsigned d_days; unsigned d_hours; unsigned d_minutes; unsigned d_seconds; public: Time(unsigned hours, unsigned minutes, unsigned seconds) : d_days(0), d_hours(hours), d_minutes(minutes), d_seconds(seconds) {} Time& operator+=(Time const& rValue){ d_seconds +=rValue.d_seconds;

Page 421: C++ 2 (Kitap - Selami KOÇLU)

d_minutes +=rValue.d_minutes +d_seconds/60; d_hours +=rValue.d_hours +d_minutes/60; d_days +=rValue.d_days +d_hours/24; d_seconds %=60; d_minutes %=60; d_hours %=24; return* this; }};Time const& operator+(Time const& lValue, Time const& rValue){ return Time(lValue) +=rValue;}int main(int argc, char** argv){ vector<Time> tvector; tvector.push_back(Time(1, 10, 20)); tvector.push_back(Time(10, 30, 40)); tvector.push_back(Time(20, 50, 0)); tvector.push_back(Time(30, 20, 30)); cout<<accumulate(tvector.begin( ), tvector.end( ), Time(0, 0, 0), plus<Time> ( ))<<endl;}///:~//üretilen çıktı:2 days, 12 hours, 51 minutes, 30 seconds//

Buradaki dikkati çeken konu; Time sınıfının bütün üye işlevleri, satıriçiolarak tanımlanmışlardır. Bu da örneği göreceli olarak küçük tutmak ve,operator+( ) işlevini satıriçi yapmak içindir. Öte yandan gerçek hayattakiTime ın, büyüklüğünden ötürü operator+=( ) işlevi satıriçi yapılamaz. Daha önceki plus nesne işlevini gözönüne aldığımızda, bu örneğimizoldukça basittir. Time sınıfı yapıcı işlevi tanımlar, yerleştirme işlecinitanımlar, ve iki zaman nesnesini ekleyebilmek için de kendi operator+( )ünü tanımlar. main( ) işlevinde 4 tane Time nesnesi vector<Time> nesnesinde depolanır.Daha sonra toplam zamanı hesaplamak için, accumulate( ) algoritmasıçağrılır. Bu da, cout ostream nesnesine yerleştirilen, Time nesnesinidöndürür. İlk örnekte adı olan nesne işlevinin kullanımı gösterilmişken, daha sonrakiiki örnekte accumulate( ) işlevine aktarılan anonim (yani adı olmayan)nesnelerin kullanımı gösterildi.

Page 422: C++ 2 (Kitap - Selami KOÇLU)

Öntanımlı nesne olarak aşağıdaki aritmetik nesneler bulunmaktadır:** plus< >( ), daha önce de anlatıldığı gibi, nesnenin operator( )( ) üyesiikici (binary) işleç olarak operator+( ) ü çağırır, onun iki değişkeniniaktararak, operator+( ) ün dönüş değerini döndürür.

** minus< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarak operator-( ) ü çağırır, onun iki değişkenini aktararak, operator-( ) ün dönüşdeğerini döndürür.

** multiplies< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarakoperator*( ) ü çağırır, onun iki değişkenini aktararak, operator*( ) ün dönüşdeğerini döndürür.

** divides< >( ), bu nesnenin operator( )( ) üyesi operator/( ) ü çağırır, ikideğişkenini aktararak, operator/( ) ün dönüş değerini döndürür.

** modulus< >( ), bu nesnenin operator( )( ) üyesi operator%( ) ü çağırır,iki değişkenini aktararak, operator%( ) ün dönüş değerini döndürür.

** negate< >( ), bu nesnein operator( )( ) üyesi tekli (unary) işleç olarakoperator-( ) ü çağırır, değişkenini aktararak tekli operator-( ) ün dönüşdeğerini döndürür.

Takip eden tekli işlem (unary operator-( ) ) örneğinde, dizideki bütünöğelerin işaretleri değiştirilmektedir. Bunun için transform( ) algoritmasıkullanılır. transform( ) algoritması dönüştürülecek nesnelerin alanlarınıbelirleyen iki tane yineleyici bekler, bir yineleyici varış yerininbaşlangıcını tanımlar (aynı yineleyici ilk değişken olabilir), diğerinde isebir nesne işlev belirtilen veri tipi için tekli işlemi tanımlar.

//:B07:tekli.cpp//tekli işleç kullanımı#include <iostream>#include <string>#include <functional>#include <algorithm>using namespace std;int main(int argc, char** argv){ int iArr[ ]={1, -2, 3, -4, 5, -6};

Page 423: C++ 2 (Kitap - Selami KOÇLU)

transform(iArr, iArr+6, iArr, negate<int>( )); for(int idx=0; idx<6; ++idx){ cout<<iArr[idx]<<”, “; cout<<endl;}///:~//üretilen çıktı:-1, 2, -3, 4, -5, 6//

İlişkili nesne işlevler

İlişkili işleçler ilişkili nesne işlevler tarafından çağrılırlar. Kullanılan bütünilişkili işleçler; ==, !=, >, >=, <, ve <= dir. Kullanılan nesneler ise aşağıdaverilmiştir;

** equal_to< >( ), bu nesnenin operator( )( ) üyesi ikici işleç (binaryoperator) olarak operator==( ) ü çağırır, iki değişkenini aktarır veoperator==( ) ün dönüş değerini döndürür.

** not_equal_to< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarakoperator!=( ) ü çağırır, iki değişkenini aktarır ve operator!=( ) ün dönüşdeğerini döndürür.

** greater< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarak operator>( ) ü çağırır, iki değişkenini aktarır ve operator>( ) ün dönüşdeğerini döndürür.

** greater_equal< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarakoperator>=( ) ü çağırır, iki değişkenini aktarır ve operator>=( ) ün dönüşdeğerini döndürür.

** less< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarak operator<( )ü çağırır, iki değişkenini aktarır ve operator<( ) ün dönüş değerinidöndürür.

** less_equal< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarakoperator<=( ) ü çağırır, iki değişkenini aktarır ve operator<=( ) ün dönüş

Page 424: C++ 2 (Kitap - Selami KOÇLU)

değerini döndürür.

İlişkili nesne işlevler de aynı aritmetik nesne işlevler gibi, hem isimli hemde anonim nesneler olarak kullanılabilirler. sort( ) algoritmasını kullanarakbir ilişkili nesne işlev verelim;

//:B07:iliskili.cpp//sort kullanarak ilişkili nesne işlev örneği #include <iostream>#include <string>#include <functional>#include <algorithm>using namespace std;int main(inr argc, char** argv){ sort(argv, argv+argc, greater_equal<string>( )); for(int idx=0; idx<argc; ++idx){ cout<<argv[idx]<<” “; cout<<endl; sort(argv, argv+argc, less<string>( )); for(int idx=0; idx<rgc; ++idx){ cout<<argv[idx]<<” “; cout<<endl;}///:~

Burada sort algoritması, yineleyiciler alanını ve yineleyicilerin işaret ettiğikarşılaştırıcıyı bekler. Örnekte string ler, alfabetik olarak düz ve terssıralanırlar. greater_equal<string>( ) aktarılarak azalan düzende string lersıralanır (ilk kelime “en büyüktür”), less<string>( ) aktarılarakta string lerartan düzende sıralanır (ilk kelime “en küçüktür”). Dikkat edilmesi gereken husus; argv öğelerinin tipi char* dır, ve buyüzden ilişkili nesne işlev, string bekler. Bundan dolayıgreater_equal<string>( ) ilişkili nesnesi string lerin >= işlecini kullanır,ama char* değişkenleri ile çağrılır. char const* tan string e geçiş, sessizcegerçekleşir.

Mantık nesne işlevleri

Page 425: C++ 2 (Kitap - Selami KOÇLU)

Mantık nesne işlevi mantık işleçlerini çağırır. Kullanılan mantık işleçler:&&, ||, ve ! dir. Aşağıdaki nesneler kütüphane tarafından sağlanmıştır;

** logical_and< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarakoperator&&( ) ü çağırır, iki değişkenini aktarır ve operator&&( ) ün dönüşdeğerini döndürür.

** logical_or< >( ), bu nesnenin operator( )( ) üyesi ikici işleç olarakoperator||( ) ü çağırır, iki değişkenini aktarır ve operator||( ) ün dönüşdeğerini döndürür.

** logical_not< >( ), bu nesnenin operator( )( ) üyesi tekli (unary) işleçolarak operator!( ) ü çağırır, tek değişkenini aktarır ve operator!( ) ün teklidönüş değerini döndürür. Aşağıdaki basit program operator!( ) ünkullanımı hakkında size bir fikir verebilir. Burada transform( ) dizidekisaklı mantık değerleri dönüştürmek için kullanılan algoritmadır;

//:B07:mantik_don.cpp//! kullanımı#include <iostream>#include <string>#include <functional>#include <algorithm>using namespace std;int main(int argc, char** argv){ bool bArr[]={true, true, true, false, false, false}; unsigned const bArrSize=sizeof(bArr)/sizeof(bool); for(unsigned idx=0; idx<bArrSize; ++idx){ cout<<bArr[idx]<” “; cout<<endl; transform(bArr, bArr+bArrSize, bArr, logical_not<bool>( )); for(unsigned idx==; idx<bArrSize;++idx){ cout<<bArr[idx]<<” “; cout<<endl;}///:~//üretilen çıktı:1 1 1 0 0 0

Page 426: C++ 2 (Kitap - Selami KOÇLU)

0 0 0 1 1 1//

Uyarlayıcı işlevler

Uyarlayıcı işlev varolan çalışan nesne işlevi değiştirir. İki çeşit uyarlayıcıişlev bulunmaktadır: 1. Bağlayıcılar: bu uyarlayıcılar ikici (binary) nesne işlevlerini tekçi

(unary) nesne işlevlerine çevirirler. Bunu da bir nesneyi sabit bir nesneişlevine bağlayarak gerçekleştirirler. İkici nesne işlev olan minus<int>( )ile örnek verecek olursak, ilk değişken 100 ile bağlanabilir, bununanlamı her zaman sonuç (100-ikinci değişken) olacak demektir. Buradaya ilk değişken ya da ikinci değişken belli bir değere bağlanır. İlkdeğişkeni belli bir değere bağlamak için bind1st( ) nesne işlevi kullanılır.İkinci değişkeni belli bir değeri bağlamak için ise bind2nd( ) işlevikullanılır. Bir örnek verelim; belli bir kıstası geçen person nesnelerinisaymak için person nesnelerinin bulunduğu vector ün bu öğelerinisayalım. Bunun için count_if( ) algoritmasına, aşağıdaki bağlayıcı veilişkili nesne işlevini aktarırız. bind2nd(greater<Person>( ), referencePerson)

Böyle bir bağlayıcı ne iş yapar?. Önce bu bir nesne işlevdir ve operator( )( ) e gerek duyar. Daha sonra bu, iki değişken bekler; ikincinesne işleve bir dayanak, ve sabit değerli bir işlenen. Bağlayıcılar kalıpolarak tanımlanmalarına rağmen, uygulamaya baktığınızda düz işlev gibikavramak daha uygundur. Şimdi bağlayıcılarla ilgili hayali bir uygulamaörneği verelim;

class bind2nd{ FunctionObject const& d_object; Operand const& d_rvalue; public: bind2nd(FunctionObject const& object, Operand const&operand) : d_object(object), d_operand(operand)

Page 427: C++ 2 (Kitap - Selami KOÇLU)

{} ReturnType operator( )(Operand const& lvalue){ return d_object(lvalue, rvalue); }};

operator( )( ) üyesi çağrıldığı zaman, bağlayıcı çağrıyı nesnenin operator( )( ) üne aktararak iki değişken sağlar; lvalue kendisinden, sabitdeğer yani işlenen ise yapıcı işlev aracılığı ile alınır. Dikkat ederseniz butarz sınıfların basitliğini hemen görürsünüz, zira bütün üyeler satıriçiolarak uygulanır.count_if( ) algoritması yineleyici alanındaki bütün öğeleri ziyaret eder, sonöğe olarak true döndüren yüklemin (predicate) kaç tane olduğunudöndürür. Yineleyici alanındaki her öğe yükleme aktarılır, ki bu tekli(unary) işlevdir. Bağlayıcı kullanarak greater( ) ikici nesne işlevi teklinesne işlevine uyarlanır, alandaki öğelerin herbiri dayanak alınan personile karşılaştırılır. İşte count_if( ) işlev çağrısının tamamı;

count_if(pVector.begin( ), pVector.end( ), bind2nd(greater<Person>( ), referencePerson))

2- Tersleyiciler yüklem işlevinin gerçeklik (true ile false yerdeğiştirmek)değerini değiştiren uyarlayıcı işlevlerdir. Madem ki tekli ve ikici yüklemişlevler var o zaman iki tane de tersleyici uyarlayıcı işlev bulunur; not1( )tekli (unary) nesne işlevler ile kullanılan tersleyicidir, not2( ) ise ikici(binary) nesne işlevler ile kullanılan tersleyicidir.Eğer belli bir hedef person u aşmayan vector<Person> daki öğe sayısınıelde etmek istersek başka seçeneklerde bulunur;

** İstenen karşılaştırmayı doğrudan yapan ikici yüklem kullanımı;

count_if(pVector.begin( ), pVector.end( ), bind2nd(less_equal<Person>( ), referencePerson))

** not2( ) ile beraber greater( ) ı kullanımı;

count_if(pVector.begin( ), pVector.end( ), bind2nd(not2(greater<Person>( )), referencePerson))

Page 428: C++ 2 (Kitap - Selami KOÇLU)

** not1( ) ile beraber bind2nd( ) kullanımı;

count_if(pVector.begin( ), pVector.end( ), not1(bind2nd((greater<Person>( )), referencePerson)))

Aşağıda nesne işlevleri tamamlayıcı tersleyici uyarlayıcı işlev örneğivereceğiz;

//:B07:terso.cpp//negator örneği #include <iostream>#include <functional>#include <vector>#include <algorithm>using namespace std;int main(int argc, char** argv){ int iArr[ ]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; cout<<count_if(iArr, iArr+10, bind2nd(less_equal<int>( ), 6)) <<endl; cout<<count_if(iArr, iArr+10, bind2nd(not2(greater<int>( )), 6)) <<endl; cout<<count_if(iArr, iArr+10, not1(bind2nd(greater<int>( ), 6))) <<endl;}///:~//üretilen çıktı:666//

Şimdi merak edebilirsiniz hepsi aynı sonucu veriyorsa, bunların en hızlısıhangisi?. Doğrudan nesne işlevinin kullanıldığı ilk yaklaşımda, count_if( )tarafından her yineleme için iki eylem gerçekleştirilir;

** bağlayıcının operator( )( ) ü çağrılır.** int değerler için <= işlemi yapılır.

Page 429: C++ 2 (Kitap - Selami KOÇLU)

not2 tersleyicisinin kullanıldığı ikinci yaklaşımda, tamamlayıcı mantıkuyarlayıcı işlevinin gerçeklik değeri terslenir, count_if( ) tarafından heryineleme için üç eylem gerçekleştirilir.

** bağlayıcının operator( )( ) ü çağrılır.** tersleyicinin operator( )( ) ü çağrılır.** int değerler için > işlemi yapılır.

Bağlayıcının gerçeklik değerini tersleyen not1 tersleyicisinin kullanıldığıüçüncü yaklaşımda, count_if( ) tarafından her yineleleme için üç eylemyapılır.

** tersleyicinin operator( )( ) ü çağrılır.** bağlayıcının operator( )( ) ü çağrılır.** int değerler için > işlemi yapılır.

Buradan göreceğiniz gibi en hızlısı ilk yaklaşımdır. GNU g++derleyicisinin kullanıldığı eski bir bilgisayarda ilk yaklaşım diğer ikisininher birinin yaklaşık %70 inde işlemi tamamlamıştır.

Bit işlemlerinde kullanılan nesne işlevler Yukarıda nesne işlevler incelenirken bazı işleçlerin olmadığı hemen gözeçarpmaktadır; bit işlemleri için kullanılabilecek öntanımlı nesne yoktur.Bununla birlikte varolan öntanımlı nesne işlevleri ile, bunları inşa etmekzor değildir. Aşağıdaki örnekler bit ve (operator&( ) ) çağıran nesne işleviile tekçi değil (operator~( ) ) çağıran nesne işlevini gerçekleştiren kalıpsınıfları göstermektedir. Öteki işleçler için benzer nesne işlevleriningereken çalışmaları okura bırakılmıştır. Zira lafın tamamı aptala söylenir.İşte bit ve çağıran nesne işlevinin uygulaması;

//:B07:bitve.cpp//operator&( ) kullanımı#include <functional>template <typename _Tp>struct bit_and: public std::binary_function<Tp, _Tp, _Tp>{ _Tp operator( ) (const _Tp& __x, const _Tp& __y) const{

Page 430: C++ 2 (Kitap - Selami KOÇLU)

return __x& __y; }};

Şimdi de operator~( ) ü çağıran nesne işlevinin uygulaması;

#include <functional>template <typename _Tp>struct bit_not: public std::unary_function<_Tp, _Tp>{ _Tp operator~( )(const _Tp& __x) const{ return ~__x ; }};

Şimdi tek sayıları int vector ünden bit_and( ) kullanarak kaldıran birprogram örneği verelim;

//:B07:tekyok.cpp//bit_and( ) kullanımı #include <iostream>#include <algorithm>#include <vector>#include “bitand.h”using namespace std;int main( ){ vector<int> vi; for(int idx=0; idx<10; ++idx) vi.push_back(idx); copy(vi.begin( ), remove_if(vi.begin( ), vi.end( ), bind2nd(bit_and<int>( ), 1)), ostream_iterator<int>)(cout, “ “)); cout<<endl;}///:~//üretilen çıktı:0 2 4 6 8//

Page 431: C++ 2 (Kitap - Selami KOÇLU)

Her daim kullanılanlar

Bu bölümde anlatacaklarımız C/C++ programcısının gündelik hayatınıkolaylaştıran genel yardımcılarıdır. Bunlar;

** Çok sık rastlanılan görevleri yerine getiren basit, küçük sınıf ve işlevler.** Çok sayıda genel tip.** Bazı önemli C işlevleri.** Programlarda tiplerin rakam sınırları.

Bunların çoğu C++ standartlarında <utilities> başlığı altındatanımlanmışlardır. Ötekiler ise ya kütüphanenin ana bölümlerinde yer alırveya C ile geçmişten kalan bağlantıları vardır. Örnek olarak; algorithmbaşlığı altında bulunan bazı yardımcı işlevler, algoritmalarla hiç ilgileriolmamasına rağmen orada tanımlanmışlardır.Bazıları da ayrıca C++ dili standart kütüphanesinde kullanılır. Buna örnekolarak pair tipi verilebilir. Pair ile iki değer ihtiyaç duyulduğunda tek birimolarak işlemlenebilir. (bir işlev, iki değer döndürmek zorunda kaldığındakullanılabilir)

'pair' kabı

pair oldukça basit bir kap sınıftır. Birinci (first) ve ikinci (second) adıverilen iki öğeyi depolamakta kullanılır. İşte bu kadar basit. Pair kaplarıkullanılmadan önce programınıza aşağıdaki önişlemleyici yönergesieklenmelidir;

#include <utility>

Standart kalıp köşeli ayraç gösterimi kullanılarak, pair değişkenitanımlandığı (veya bildirildiği) zaman, pair veri tipleri belirlenir. İşte

Page 432: C++ 2 (Kitap - Selami KOÇLU)

örnek;

pair<string, string> piper(“PA28”, “PH-ANI”);pair<string, string> cessna(“C172”, “PH-ANG”);

Burada piper ve cessna pair değişkenlerinde iki tane string veri tipibulunmaktadır. Her iki string i yazdırmak için pair tipinin first ve secondalanları kullanılır;

cout<<piper.first<<endl<< //PA28 yazar cessna.second<<endl; //PH-ANG yazar

first ve second üyeler yeniden atamada da kullanılabilir;

cessna.first=”C152”;cessna.second=”PH-ANW”;

pair nesnesinin tamamına yeniden atama zorunluluğu doğarsa, atamaişlecinin sağ tarafında anonim bir pair nesnesi kullanılabilir. Buradaanonim değişken adsız geçici bir değişkeni, aynı tipte başka bir değişken(yeniden) atamak amacıyla tanımlanır. Temel yazımın kalıbı aşağıdakigibidir;

type(başlatma listesi)

pair nesnesi kullanıldığı zaman, sadece pair kap adının belirtilmesi ile tipbelirtimi tamamlanmaz. Ayrıca pair de bulunan veri tiplerinin debelirtilmesi gerekir. Bu yüzden yine kalıpların açılı ayracı kullanılır. Örnekolarak, cessna pair değişkeninin yeniden ataması aşağıdaki gibi yapılır;

cessna=pair<string, string>(“C152”, “PH-ANW”);

Benzer durumlarda bazen tip belirtimi oldukça karışık olabilir, bu yüzdentypedef anahtar kelimesinin imkanlarını kullanmak yararlıdır. Kaynakkodda çok sayıda pair<type1, type2> kullanılırsa, o zaman bunun içintypedef açıklamasını kullanmak daha akıllıcadır;

typedef pair<string, string> pairStrStr;cessna=pairStrStr(“C152”, “PH-ANW”);

Page 433: C++ 2 (Kitap - Selami KOÇLU)

Bunlardan başka (atama, karşılaştırma hariç) pair daha fazla işlevikliktaşımaz. Bununla birlikte map, multimap ve hash_map in temel unsurudur.

Explicit Explicit anahtar kelimesi kullanıldığı zaman, tek değişkenli yapıcı işlevinkendiliğinden tip çevrimi yapması engellenmiş olur. Bu özelliğinkullanıldığı tipik örnek olarak, topluluk sınıfının yapıcı işlev değişkenineaktarılan boyut değeri ele alınabilir. Örneğin bir yığıtın boyut değeriniyapıcı işlevin değişkeni yapıp bildirebilirsiniz;

class Stack{ explicit Stack(int size); //ilk boyutu ile yığıt oluştur .....};

Yukarıda explicit anahtar kelimesinin kullanımı oldukça önemlidir. Eğerburada yapıcı işlevi explicit kullanmadan yazacak olursak int ten Stack ekendiliğinden çevrim olur. Böylece Stack e int ataması yapılabilir.

Stack s;...s=50; //Hoop dur adam ezduk. 50 öğeli yeni bir Stack oluşur. //s ye atanır.

Kendiliğinden tip çevrimi 50 yi, 50 öğeli bir yığıt (Stack) haline getirir.Herhalde bu sizin önceden planladığınız bir şey değildi. Sizin int yapıcıişlevi explicit olarak tanımlamanız (explicit Stack(int size)) sayesinde,böyle bir atama olduğunda program derlenirken hata iletisi alınır. Yaniyaptığınız işin hatalı olduğunu anlarsınız.Bir şey daha ekleyelim; atama yapılırken ortaya çıkan tip çevriminde deexplicit etkilidir.

Stack s1(60); //uyar, tamam Stack s2=60; //olmaz, hatalı

Bunun nedeni aradaki çok küçük farktır;

Page 434: C++ 2 (Kitap - Selami KOÇLU)

X x;Y y(x); //explicit (aleni) çevrim

ve

X x;Y y=x; //iç (gizlice) çevrim

Örnekler arasındaki fark konuyu daha anlamanızı sağlar.

Bitset sınıfı ve bit işlemleri Bit ler bir bilgisayarda bulunan bilginin en küçük birimidir. Bitler ikicidir(binary). Yani iki durumu vardır. Şunları temsil etmek için kullanılabilir;doğru/yanlış, açık/kapalı, evet/hayır, davul/zurna ya da true/false. Bit lerdaha büyük veri tipleri için biraraya getirilirler. Örnek; bir bayt (byte) 8 bitten oluşur. Diğer veri tipleri ise, baytların birleşiminden oluşur. Programların birçoğu doğrudan bitlerin işlemlenmesine gerek duymaz, vebazı diller de bit düzeyinde işlemlere izin vermezler. Ama bazı alanlarvardır ki bit düzeyinde işlemler gerekir;

1- Araç sürücüleri 2- Veri sıkıştırıcıları 3- Görüntüler 4- Tabii ki bilgisayarınız

Araç sürücüleri Araçların birçoğunda örneğin arayüz kartları, hardisklerde durum vedenetim kaydedicileri bulunur. Denetim kaydedicisindeki belli bitler sıfırveya bir yapılarak araçlar denetlenir. Araçların o an ki durumlarınısaptamak için ise, durum kaydedicisinin bitlerine bakılır.

Veri sıkıştırma

Page 435: C++ 2 (Kitap - Selami KOÇLU)

int ve float gibi yerleşik veri tipleri çok sayıda bayt tan oluşur. Her bayt ta8 bitten oluşmuştur. Büyük boyutlu bilgiler karmaşık algoritmalaruygulanarak daha küçük boyutlu hale getirilebilir, buna sıkıştırılmış veridenir, daha sonra sıkışık veri gerektiği zaman tekrar ilk haline hiçbir verikaybı olmadan çevrilebilir. Bu uygulama genellikle iki durumda yapılır;ilki dosyaları daha küçük boyutlu hale winzip veya winrar (Unix te isecompressed) ile getirmek, ikinci kullanımı ise büyük boyutlu verilerin biryerden diğerine aktarılması durumudur. Artan miktarda veri sabit genişliklibanttan (havadan veya telden) aktarıldığı zaman sıkıştırılması gerekir.Gönderme ucunda yapılan sıkıştırılma daha fazla veri aktarılmasını sağlar,alma ucunda ise sıkışık veri açılarak esas haline, yani çalıştırılabilir halegetirilir. Veri aktarımında kullanılan sıkıştırma algoritmalarının bir yararıda, hata algılamaları ve hatayı düzeltebilmeleridir.

Görüntüler

Bilgisayar görüntüsündeki her nokta bir piksel değerini temsil eder. Siyahbeyaz bir görüntüde her piksel birden fazla bit ile tanımlanan biryoğunluğa sahiptir. Örneğin; tam siyah bir piksel 0 (sıfır) değer iletanımlanırken, tam beyaz bir piksel ise 255 (8- bitlik) ile gösterilir. Pikselyoğunluk değerleri bitler halinde saklanır. Renkli piksellerde aynı şekildeüçlü gruplar halinde bitler oluşturularak yapılır. Yani bir piksel üç tanesayı ile temsil edilir, ve bu sayılar genellikle bitler halinde saklanır. RGB(kırmızı-yeşil-mavi) diye adlandırılan renklendirme biçiminde, bu üç anarengin değrleri sayılarla açıklanarak, belli bir noktadaki ekran piksel rengibelirlenir.

Bilgisayar

Oldukça karmaşık işler yaptıkları izlenen bilgisayarların aslındayetenekleri oldukça sınırlıdır. En alt düzeyde yapabildikleri sadece bitişlemleridir. Bitleri eklerler, terslerini alırlar, ve iki biti karşılaştırırlar.Bunların üzerinede 50-100 arasında assembly işlemi bulunur. Bu işlemlerbaytlar ve kelimeler ile yapılır. Üst seviyeli diller hep bu assembly makinadili üzerine inşa edilmiştir. Uygulama programları ve işletim sistemleri üstseviyeli programlama dilleri ile yazılır. Yani bilgisayarınızdaki hayalet, üstseviyeli dille aslında ne demek istendiğini bilir.

Page 436: C++ 2 (Kitap - Selami KOÇLU)

Bit işlemleri Bit biçimli ve (&)

Bit1 Bit2 sonuç0 0 00 1 01 0 01 1 1

//:07:bitve.cpp//bitwise and (&)#include <iostream>using namespace std;int main( ){ unsigned short statusRegister ; unsigned short clearBits ; statusRegister=0xD1 ; //bazı bitler 1 yapıldı (yani set edildi) clearBits=0x0F ; //sadece üst rakamları sıfırla (yani clear et) cout<<hex<<”Status Register: ”<<statusRegister<<endl; statusRegister=(statusRegister&clearBits) ; cout<<hex<<”Status Register Cleared: ”<<statusRegister<<endl; return 0;}///:~//üretilen çıktı:Status Register: d1Status Register Cleared: 1//İşlemi inceleyelim:

OxD1= 11010001Ox0F= &00001111 --------------- 00000001Yukarıdaki statusRegister=(statusRegister&clearBits); ifadesini

statusRegister&=clearBits;

şeklinde de yazabilirsiniz.

Page 437: C++ 2 (Kitap - Selami KOÇLU)

Bit biçimli veya ( | )

Bit1 Bit2 Sonuç0 0 00 1 11 0 11 1 1

Şimdi de arayüz kartının denetim kaydedicisi kullanılacak. Cihazın istenenkonumda olabilmesi için denetim kaydedicisinin birinci, ikinci ve yedincibitleri 1 olacaktır.

//:B07:bitveya.cpp//bitwise or ( | ) kullanımı#include <iostream>using namespace std;int main( ){ unsigned short control Register ; unsigned short setBits ; controlRegister=0x00 ; //bütün bitler sıfırlandı (yani clear edildi) setBits=0x83 ; //3 tane bit bir yapıldı (yani set edildi) cout<<hex<<”Control Register: ”<<controlRegister<<endl ; controlRegister|=setBits ; cout<<hex<<” Control Register Baslatildi: “<<controlRegister <<endl; return 0;}///:~//üretilen çıktı:Control Register: 0Control Register Baslatildi: 83//

0x00= 00000000 |0x83= 01000011 ------------ 01000011

Bit biçimli XOR (^)

Page 438: C++ 2 (Kitap - Selami KOÇLU)

Bit1 Bit2 Sonuçlar----- ----- ------------ 0 0 00 1 11 0 11 1 0

//:B07:biteksor.cpp//exor and kullanımı#include <iostream>using namespace std;int main( ){ unsigned short flag1; unsigned short flag2; unsigned short flag3; flag1=0x0F; flag2=0xF0; flag3=flag1^flag2; //flag1 ile flag2 arasındaki farklı bitlerin bulunduğu yerleri sapta cout<<hex<<”flags1: “<<flag1<<endl; cout<<hex<<”flags2: “<<flag2<<endl; cout<<hex<<”flags3: “<<flag3<<endl; return 0;}///:~//üretilen çıktı:flags1: fflags2: f0flags3: ff//

0x0f= 0000ffff0x0f=^ffff0000 ------------ ffffffff

Bit biçimli değil (~)

Bit Sonuç --- -------

Page 439: C++ 2 (Kitap - Selami KOÇLU)

0 11 0//B07:bitdegil.cpp//negation kullanımı#include <iostream>using namespace std;int main( ){ unsigned short flag1; unsigned short flag2; flag1=0x00CC; flag2=~flag1; cout<<hex<<”flag1: “<<flag1<<endl; cout<<hex<<”flag2: “<<flag2<<endl; return 0;}///:~//üretilen çıktı:flag1: ccflag2: ff33//

0xCC= 0000000011001100ff33 = 1111111100110011Görüldüğü üzere bitler terslenmiş olur.

Programcılık aleminde en sık hatalardan biri mantık işleçleri ile bitişleçlerini birbirine karıştırmaktır. Mantık işleçleri: &&(mantık ve), ||(mantık veya), !(mantık değil) dir. Bit işleçleri ise: & (ve), |(veya), ile ~(değil) dir. Siz karıştırmamak için dikkatli olun!!...Şimdi her ikisi arasındaki farkı gösteren, basit bir örnek verelim;

//:B07:dikkatli_ol.cpp//bit işleçleri ve mantık işleçleri arasındaki fark#include <iostream>using namespace std;int main( ){ unsigned short flag1; unsigned short flag2; flag1=0x00FF; flag2=0xFF00; cout<<”bitwise operator: “<<(flag1&flag2)<<endl;

Page 440: C++ 2 (Kitap - Selami KOÇLU)

cout<<”logical operator: “<<(flag1&&flag2)<<endl; return 0,}///:~//üretilen çıktı:bitwise operator: 0logical operator: 1//

Mantık işleci açısından bakıldığında, her iki işlenenin değeri, 0 (sıfır) danfarklı olduğu için (doğru=true) olduğu kabul edilir. Bundan dolayı;(doğru)&&(doğru)=doğru (mantık işleçlerine göre)

Bit işleçlerine göre incelediğimizde ise bitlerin aynı noktalarda değerleriaynı anda “1” olmadığı için, her bitin birbiri ile çarpımı (ve lenmesidiyebiliriz) sıfır değerini üretir.

0x00FF= 00000000111111110xFF00= 1111111100000000

Sola kaydırma <<

<< işareti bitleri belitilen sayıda sola kaydırır. Şimdi basit bir örneklegösterelim:

İşlem Açıklama Sonuç (ikici olarak)------- ------------ ------------------------unsigned short İlk değer 0100000010001111bitvect=0x408Fbitvect<<1 sola bir basamak 100000010001110 kaydır bitvect<<3 sola üç basamak 000010001110000 kaydır

Asşağıda basit bir program örneği:

//:B07:kaydir.cpp//left shift

Page 441: C++ 2 (Kitap - Selami KOÇLU)

#include <iostream>using namespace std;int main( ){ unsigned short bitvect=1; for(int i=0; i<10; ++i){ cout<<bitvect<<endl; bitvect=bitvect<<1; //sola kaydırma işlecini cout ile kullanılan yer //işleci ile karıştırmayın, bitvect<<=1 de yazabilirdiniz } return 0;}///:~//üretilen çıktı:1248163264128256512//

Gördüğünüz gibi sola bir bit kaydırma iki ile çarpmaya eşdeğer.

Sağa kaydırma >>

Sağa kaydırma işleminde dikkate alınması gereken en önemli konu; signedve unsigned sayılarda en başa eklenecek sayıdır. İşaretli sayılarda en baştayer alan bit, sayı işaretini belirtir. Bu bit 1 ise sayının negatif olduğunu 0(sıfır) ise pozitif olduğunu gösterir. İşaretsiz (unsigned) sayılarda enbaştaki bit mutlak değere aittir. İşaretli (signed) sayılarda en baştaki bit,işareti belirtir. Sıfır ise pozitif, bir ise negatiftir. İşaretsiz (unsigned)sayılarda sağa kaydırma işlemi aynı sola kaydırma gibi yapılır tabii birfarkla; bu sefer bit katarı sağa doğru ötelenir, sol taraftan ise sıfır eklenir.İşaretli (signed) sayılarda ise en başta eğer 1 (bir, yani negatif sayı) varsasoldan 1 (bir) eklenerek, bit katarı sağa ötelenir. Yok eğer en başta 0 varsayani pozitif sayı ise, soldan sıfır eklenerek bit katarı sağa ötelenir.

Page 442: C++ 2 (Kitap - Selami KOÇLU)

Şimdi işaretsiz sayı kullanarak sağa kaydırma örneği verelim;

//:B07:sagakay.cpp//right shift >>kullanımı#include <iostream>using namespace std;int main( ){ unsigned short bvec=1024; for(int i=0; i<15; i++){ cout<<bvec<<endl; bvec>>=1; } return 0;}//üretilen çıktı:102451225612864321684210000//

Buradan gördüğünüz üzere sağa kaydırma 2 ye bölme demektir.

Şimdi de buraya kadar gördüğümüz bit işlemlerini, sağa ve solakaydırmayı ve mantık işleçlerini bit katarında bulunan bitlere uygulayalım.Böylece bit katarında bulunan herhangi bir biti, silebilir (clear), değerinibir (set) yapabilir veya sınayabiliriz (test).Metro istasyonunda kimyasal kalıntıları test eden bir sistem tasarlayalım.10 tane algılayıcımız olsun, bunları 10 bit kullanarak gösterelim. 1 (bir)

Page 443: C++ 2 (Kitap - Selami KOÇLU)

belli bir algılayıcıyı etkinleştirsin, 0 (sıfır) algılayıcıyı durdursun.Durdurma işlemi algılayıcıda hata veya ayar bozukluğu olduğunda yapılır.Böylece, durdurma yanlış bilgilenmeyi ve yanlış komutları engeller.Algılayıcı bitlerini bit katarının 1 numaralı yeri ile 10 numaralı yerleriarasına koyalım. Yani sıfırıncı yeri atlayalım. Kodlama ve düzenleme dahaaçık olur. Başlangıçta bütün algılayıcılar açık (off) durumundadır.

unsigned short int sensors=0;

Daha sonra her algılayıcının ayarları sınanır, ve uygun olanlaretkinleştirilir.

for(int i=1; i<10; i++){ if(isCalibrated(i)){ sensors|=(1<<i); }}

Burada;(1<<1)=0000000000000010(1<<2)=0000000000000100(1<<3)=0000000000001000

ve böyle devam eder. İlgili bit in veya işlemine tabi tutulduğu durumda,ayarlı algılayıcı 1 e set edilmiş olur. Şimdi bu algılama dizgesinde bulunan 5 numaralı algılayıcının, aşırıparfümlü bir kadının koku kalıntılarından dolayı ayarı bozulmuş olsun.Dizgemiz bunu algılar ve 5 numaralı algılayıcının etkinliğini ortadankaldırır. Peki bu nasıl yapılır?. Birşey sıfır ile “VE” (AND) yapılırsa,sonuç sıfır olur. Bundan yararlanarak biz algılayıcı bit katarımız1111111111011111 ile VE (AND) yapılırsa 5. algılayıcı kapatılır.Yukarıdaki (1<<5) işleminden 0000000000100000 elde edilir. Biz buişlemi terslersek (~(1<<5)) istenen sonuç 1111111111011111 sağlanır.İstenen aşağıdaki işlemdir;

sensors&=~(1<<5);

Kalıntı algılanan i inci algılayıcının yani sensor i nin yerini gösterenaçıklama;

Page 444: C++ 2 (Kitap - Selami KOÇLU)

unsigned short int detections=0;

Şimdi bu kadar ayrıntıyıverdikten sonra hemen belirtelim, aslında bizimelimizde bitset gibi bir SKK sınıfı bulunmaktadır. Yapmak istediğimizhertürlü bit işlemini onunla yapabiliriz. Bit vektörlerini bitset sınıfı ilekullanmak çok kolaydır.

Bitset üye işlevleri: Amacı: Örnek:----------------------- ------------------------- ------------------------ any( ) Herhangi bir bitin 1 sensors.any( ) olup olmadığını sınar count( ) 1 olan bit sayısını verir sensors.count( )flip( ) Mantıki tersler veya sensors.flip( ) bütün bitleri tersler flip(pos) Mantıki tersler veya sensors.flip(4) yeri verilen biti tersler none( ) Hiçbir bit 1 değilse true detections.none( ) yani doğru değer üretirreset( ) Bütün bitleri sıfır yapar detections.reset( )reset(pos) Yeri verilen biti sıfırlar detections.reset(5)set( ) Bütün bitleri 1 yapar detections.set( )set(pos) Yeri verilen biti 1 yapar detections.set(3)test(pos) Yeri verilen biti sınar detections.test(7)

Bitset sınıfı işleçlerin çoğuna bindirim yapar, o yüzden işlemler bildiğimizgibi yapılabilir.

İşte bitset sınıfı ile örnek;

//:B07:bityer.cpp//bitset sınıfının kullanımı#include <iostream>#include <bitset>using namespace std;int main( ){ bitset<16> sensors; bitset<16> detections; sensors.reset( ); detections.reset( ); //ayarı sına ve algılayıcıları aç

Page 445: C++ 2 (Kitap - Selami KOÇLU)

for(int i=1; i<=10; i++){ if(isCalibrated(i)){ sensors.set(i); } } //çalıştır, ortamı sına .... ... //kalıntı varmı bak for(int i=1; i<=10; i++){ if(detections.test(i)){ cout<<”BUNU GEC”<<endl; } } return 0;}///:~

Gördüğünüz gibi bitset sınıfı ile bu işler ne kadar basit.

Çıktı düzenlemeleri (Biçemler)

cout nesnesini kullanarak çıktıları istenilen biçimde düzenlemekmümkündür. Bunları gerçekleştirebilmek için, std::ios_base üye işlevlerinikullanarak ilgili bayraklar 1 yapılır. Örnek;

//B07:formatla.cpp//biçemleme örneği#include <iostream>int main( ){ std::cout.setf(std::ios_base::right, std::ios_base::basefield); std::cout.width(10); std::cout<<”Test”<<std::endl; return 0;}///:~//üretilen çıktı: Test

Page 446: C++ 2 (Kitap - Selami KOÇLU)

//

cout ile ayarlanabilen bayraklar:

skipws ---------- Girdide boşlukları atla left ---------- Değerin sağ tarafını boş bırakright ---------- Değerin sağ tarafını boş bırakinternal ---------- Değerle işaret arasını boş bırakboolalpha --------- Doğru ile yanlışı imge ile gösterdec ---------- 10 tabanlı gösterhex ---------- 16 tabanlı göster oct ----------- 8 tabanlı göster scientific ---------- Kayan noktalı göster d.ddddEddfixed ---------- d.ddshowbase -------- Tabanı önek şeklinde göster; 0 veya 0x gibishowpoint ------- Arkadan gelen 0 ları yazshowpos ------- Pozitif değerler için + yı gösteruppercase ------- e ve x yerine E ve X kullan adjustfield ------- Alan ayarı ile ilgili bayrak basefield -------- Doğal sayı tabanı ile ilgili bayrak floatfield -------- Kayan noktalı sayı ile ilgili bayrak unitbuf -------- Her işlemden sonra çıktıyı boşalt

Bu bayraklar aşağıdaki şekilde üye işlevlerle kullanılarak işlemlenirler;

flags(fmtflags f) // bayrakları 1 yap (Set flags)setf(fmtflags f) // bayrağı topla (Add flag)setf(fmtflags f, fmtflags mask ) // bayrağı ekle (Add flag)unsetf(fmtflags mask) // bayrakları sıfırla (Clear flags)width( ) // genişliği al (Get field width)width(streamsize wide) // alan genişliğini belirt (Set field width)fill( ) //yerleştirilecek karakteri al (Get filler character)fill(Ch ch ) //karakteri yerleştir (Set filler character)precision( ) //hassasiyeti al (Get precision)precision(streamsize n ) //hassasiyeti ayarla (Set precision)

Başka bir seçenek olarakta çıktı özelleştiricileri kullanılabilir;

Page 447: C++ 2 (Kitap - Selami KOÇLU)

//:B07:ciktiozel.cpp//manipulator kullanımı#include <iostream>#include <iomanip>int main( ){ std::cout<<std::setiosflags(std::ios_base::right) <<std::setw(10)<<”Test”<<std::endl; return 0;}///:~//üretilen çıktı: Test//

Özelleştiricileri kullanmak, bayrakları tek tek ayarlamaktan daha kolay,daha açık ve daha basittir. <ios> <iostream> ve <iomanip> te bulunan bazıözelleştiriciler şunlardır:

boolalpha // cout.setf ( ios_base::boolalpha )noboolalpha // cout.unsetf ( ios_base::boolalpha )showbase // cout.setf ( ios_base::showbase )noshowbase // cout.unsetf ( ios_base::showbase )showpoint // cout.setf ( ios_base::showpoint )noshowpoint // cout.unsetf ( ios_base::showpoint )showpos // cout.setf ( ios_base::showpos )noshowpos // cout.unsetf ( ios_base::showpos )skipws // cout.setf ( ios_base::skipws )noskipws // cout.unsetf ( ios_base::skipws )uppercase // cout.setf ( ios_base::uppercase )nouppercase // cout.unsetf ( ios_base::uppercase )left // cout.setf ( ios_base::left, ios_base::basefield )right // cout.setf ( ios_base::right, ios_base::basefield )internal // cout.setf ( ios_base::internal, ios_base::basefield )dec // cout.setf ( ios_base::dec, ios_base::basefield )hex // cout.setf ( ios_base::hex, ios_base::basefield )oct // cout.setf ( ios_base::oct, ios_base::basefield )fixed // cout.unsetf ( ios_base::fixed, ios_base::floatfield )scientific // cout.unsetf ( ios_base::scientific, ios_base::floatfield )endl // '\n' koy ve boşalt (put '\n' and flush)

Page 448: C++ 2 (Kitap - Selami KOÇLU)

ends // '\0' koy (put '\0')flush // akışı boşalt (flush stream)ws // boşluğu kaldır (eat whitespace)

resetiosflags(fmtflags f) //bayrakları (0) sıfırla (Clear flags)setiosflags(fmtflags f) //bayrakları (1) bir yap (Set flags)setbase(int base) //sayı tabanı (integer base)setfill(int ch) //boş kalan yerleri ch doldur (Fill padding with ch)setprecision(int n) // n digits of floating point precisionsetw(int n) //n alan genişliği (next field width is length n)

Bunlardan değişkenleri gösterilmeyenleri aşağıdaki gibi kullanabilirsiniz:

//:B07:almaz.cpp//örnek#include <iostream>#include <iomanip>int main( ){ int deg=123; std::cout<<deg<<” “<<std::oct<<deg<<” “ <<std::hex<<deg<<std::endl; return 0;}///:~//çıktısını sen bul ): hadi yazayım: oct=173 hex=7B

Programcı dilerse kendi özelleştiricilerini kendisi tanımlayabilir ve bunlarıprogramında gönlünce kullanabilir.

'mutable' anahtar kelimesi İlk kitabımızda mutable anahtar kelimesi ile ilgili biraz bilgi verilmişti. Buanahtar kelime eğer bilinçli kullanılmazsa oldukça sorunlar çıkarabilir. Buyüzden ayrıntı vermeyi uygun gördük.const üye işlevler ve nesneler hakkında sanıyoruz yeterli bilginiz vardır.C++ dili, bir şekilde ne const ne de const olmayan- anlam bakımından-nesnelerin inşa edilebilmesine imkan sağlamaktadır. Mutable anahtarkelimesi kullanılarak tanımlanmış olan veri üyeleri, const üye işlevler

Page 449: C++ 2 (Kitap - Selami KOÇLU)

tarafından değiştirilebilir. Mutable in kullanışlı olduğu duruma bir örnekolarak; const bir nesnenin kullanılma sayısını kaydetmek istemesiniverebiliriz. Mutable sınıfının const nesnesi için inşaata başlayabiliriz.Örneğe geçelim:

//:B07:sustur.cpp//mutable örneği#include <iostream>#include <memory>#include <string>class Mutable{ std::string d_name; mutable int d_count; //mutable anahtar kelimesi pubic: Mutable(std::string const& name) : d_name(name), d_count( ) {} void called( ) const{ std::cout<<”Calling”<<d_name<< “ (attempt “<<++d_count<<”)\n”; }};int main( ){ Mutable const x(“Mutable const nesne”); for(int idx=0; idx<4; idx++) x.called( ); //const nesneyi değiştir}//:~//üretilen çıktı:Calling Mutable const nesne (attempt 1)Calling Mutable const nesne (attempt 2)Calling Mutable const nesne (attempt 3)Calling Mutable const nesne (attempt 4)//

mutable anahtar kelimesi, örnek olarak; dayanç sayımlarını gerçekleştirensınıflarda çok faydalı olur. Metin string lerinde dayanç sayımınıgerçekleştiren bir sınıfı düşünelim. Dayanç sayımını yapan nesne, const birnesne olmalıdır, ama sınıf kopya yapıcı işlev tanımlamalıdır. Bildiğiniz

Page 450: C++ 2 (Kitap - Selami KOÇLU)

gibi const nesneler değiştirilemez, peki o zaman kopya yapıcı işlev dayançsayımını yaparken sayı artışını nasıl gerçekleştirecek?. İşte tam bu noktadamutable anahtar kelimesi devreye girer. Böylece nesne const olsa bile,sayı değeri azaltılıp çoğaltılabilir.En sonunda burada bizim söyleyeceğimiz; hangi veri üyelerinin değiştirilipdeğiştirilemeyeceğine programcının karar vereceğidir. Bu mutable anahtarkelimesinin sağladığı bir üstünlüktür. Ama mutable anahtar kelimesinin birde dezavantajı bulunmaktadır; programcıların const nesnelerle ilgili kesinkararlar almalarını engeller. Bundan dolayı const nesnelerin kararlıyapılarından uzaklaşılmış olur. Duruma göre bu bazen sorun olur, bazen deolmaz. Uygulamada mutable iç rezervasyonlarda yararlı olur; mutable veriüyelerinin değerlerini döndüren erişimciler const nesnelere sahip buerişimcileri kullanan müşterilere şaşırtıcı sonuçlar verirler. Bu durumlardadönen değerin tabiatı açık şekilde belgelenmesi lazımdır. Temel kural; Bukuralın dışına çıkan çok açık bir neden yoksa mutable anahtar kelimesinikullanmayın.

Page 451: C++ 2 (Kitap - Selami KOÇLU)

Burası boş bırakıldı

Page 452: C++ 2 (Kitap - Selami KOÇLU)

8. Bölüm

Öbür Kütüphaneler

C/C++ dillerinde standart kütüphanelerin dışında çok sayıda kütüphanedaha bulunmaktadır. Bu kütüphaneler genel amaçlara hizmet ettikleri gibibazıları da özel amaçlara hizmet ederler. Şimdi C++ dilindeki yaygınolarak kullanabilecek, henüz standart olmamış öbür kütüphanelerianlatalım. Bu kütüphaneler C++ standardını da genişletmektedir. HenüzC++ standardında yer almayan; ağlar (networks), program suratları (GUI),eş anlı programlar (concurrency).. vs konularındaki bu kütüphanelerikısaca inceleyelim. Zaten kısaca incelemek zorundayız, zira herbirininayrıntıları ciltlere sığmaz. Bu kütüphanelerin özellikle açık kaynak kodluolanlarını seçtik.

Sayılarla ilgili olanlar

Blitz++ ve Matrix Template Library (MTL) kütüphaneleri üst düzey

Page 453: C++ 2 (Kitap - Selami KOÇLU)

hesaplamalarda büyük verimlilikle çalışırlar. valarray in olması gerekendeğerini gösterir. En eski projelerden olan Blitz++, hesaplamalardakalıpların üstünlüklerinden olan, çalışma zamanı işlemlerini derlemezamanına kaydırmıştır. Blitz++ de güçlü dizi, matriks sınıfları, işleçler,işlevler ve altdiziler vs.. bulunur.Temel iyileştirilmelerinden biri; Blitz++ dizilerini içeren aritmetik işleçlerve matematik işlevler değeri hemen hesaplamazlar. Onun yerine nesneaçıklamalarını döndürürler. Bir nesne açıklaması bir diziye atandığızaman , açıklama hesaplanır, sonuçlar doğrudan atamanın hedefindesaklanır, böylece çok büyük geçici dizilere gerek kalmaz.MTL de verimlilikte iyileştirmeyi sağlamak için kalıpları kullanır.Yaklaşımı daha ziyade SKK (standart kalıp kütüphanesi) yı andıranbiçimde, kapları (Matrix, Vector vs..), yineleyicileri ve kalıpalgoritmalarını (transpose, dot vs) kullanır.

ACE

ACE (ADAPTIVE communication enviroment) Uyarlamalı iletişim ortamıolup ağ, iletişim ve eş anlılıkla ilgili imkanlar sağlar. ACE çok büyük birkütüphanedir, çok fazla iş yapar. ACE kütüphanesini anlatan ayrıntılıkitaplar bulunmaktadır (bakınız google). En önemli özelliği; eş anlılık veağ çalışmalarının birlikte bulunduğu tümleşik bir çatı oluşturmasıdır.CORBA desteği için de TAO (The ACE orb) vardır.En alt seviyede de işletim dizgesine arayüz yapan aktarılabilir bir API(application programming interface) bulundurur. Bu API nin üstünde iseC++ örtücü sınıfları vardır. Örtücüde uyum unsurları (semaphore vemutex), süreçlerarası iletişim unsurları (ACE_SOCK_Connector,ACE_SOCK_FIFO ve ACE_SOCK_Addres) ve eşanlılık unsurları (Futureve Servant tutak (thread) sınıfları) bulunur.Bir üst katman çatıyı oluşturur. İletiler ACE uyum sınıfları ile bütünleşikolarak olay yönelimli biçimde, Reactor ve Proactor sınıflarınca düzenlenir.Bu seviyede ACE Orb, CORBA için yapıya bütünleştirilir. Kaynak kodlarını Washington üniversitesinde aşağıdaki adrestenindirebilirsiniz.www.cs.wustl.edu/~doc

LOKİ

Page 454: C++ 2 (Kitap - Selami KOÇLU)

Andrei Alexandrescu tarafından geliştirilmiştir, sadece Windows a görekurgulanmıştır. Yani Unix sürümü bulunmamaktadır. Kalıp paradigmasınıen uç noktasına kadar (hatta daha ötesine kadar) kullanmakta olup üstprogramalamanın da özelliklerini ortaya çıkarmıştır. Kütüphaneyigeliştiren Andrei tarafından yazılmış olan Modern C++ design isimlikitapta, kütüphane hakkında çok ayrıntılı bilgi verilmiştir. Ama ben bukütüphaneyi kullanmayı kimseye tavsiye etmem.

BOOST

Eskiden gazinolarda sahneye en son assolistler çıkardı, evet burada da ensona en iyi kütüphane kaldı: BOOST. C++ standartlar komitesindebulunan bazı kişiler boost kütüphanesinin geliştirilmesinde bizzat yeraldılar. Bunun sebebi, C++ nın gelecekte gelişim doğrultusunubelirleyebilmekti. Böylece BOOST ortaya çıktı. Ve süreç hala devamediyor. Hatta şu an da bile Boost kütüphanesinin bazı bölümleristandartlara eklendi.Boost çok büyük bir kütüphane olup hemen hemen herşeyi kapsar.Bunların arasında tip biçimlendirmelerinden kuaterniyonlara (dörtboyutlularda sanal sayıları genelleştirmek), kodları bölmelemeden doğrusalcebire, basit bellek kullanımcısı tutaklar (threads) ve uyuma kadar herşeybulunur. Boost ayrıca C++ diline işlevik programlama paradigmasınıeklemiştir. İşlevik programlama program çalışırken programa işleveklenmesini sağlar. Aslında paradigmalar denizi olan C++ yeni birparadigma özelliği daha kazanmış olur. Böylece C++ sorun çözmeyeteneklerini daha da uç noktalara vardırır. Kalıp üst programlama iseBoost un C++ ya bahşişidir.

Boost u inşa etmek ve kurmak

Ağa girdiğinizde Boost adresine bakınca inşa ve yükleme bilgilerini hemengörürsünüz. Önce make e benzeyen Boost Jam i kurmalısınız. Bu sizeBoost u kurmakta çok büyük kolaylık sağlar. Daha sonra ağdaki adımlarıtakip ettiğinizde işte size Boost.

Page 455: C++ 2 (Kitap - Selami KOÇLU)

Kütüphaneler üzerine Kitabımızın başından beri gerek standart C/C++ kütüphaneleri gerekse(son bölümde) özel kütüphaneler hakkında bilgi verdik. 1. cildimizde debelirttiğimiz gibi kütüphaneler programcıya büyük kolaylık sağlar. Onlarınsayesinde programcı birçok kodu yazmaktan kurtulur. Böylece projelerkısa sürede gerçeklenir. Yani iyi bir programcı hangi kütüphanede neyi,nasıl bulacağını bilir, ve projesi ile ilgili olanları biraraya getirir. Busayede program maksimum verimle yazılmış olur. Edison lambayı bulmuşsizin de bulmanıza gerek yok.

Page 456: C++ 2 (Kitap - Selami KOÇLU)

Üst Programlama (metaprogramming) Belli bir ortama kolaylıkla uygun hale getirilebilen dizgelere giderek artantalep olmaktadır. Bu uygunluğa ek olarak, dizgelerin çalışma esnasındakendilerini özel durumlara ayarlayabilme ihtiyacı da vardır. (o an ki işyüküne göre, hazır (cache) bellek işlemlerini değiştirmek gibi). Butür dizgelere uyarlanmış dizgeler denir. Uyarlanmışlık sadece çalışmaesnası ile ilgili değildir. Derlenecek olan dizgeye yardımcı olan vekendiliğinden kodu uyarlayan (algoritmaları seçerek) kütüphanelerbulunur.Şimdi üst programlama terimlerini açıklayalım: Üst programlama; kendilerini (yansıma) veya öbür programları (örneğin;derleyiciler, yorumlayıcılar, ve program üreteçleri) temsil eden vedeğiştiren programları yazmaktır. Üst programlar kendilerini veya öbür programları temsil eder veyadeğiştirirler. Yansıma ise, bir program çalışırken programın durumunu belirten bazıverilerin programın kendisince değiştirilebilme yeteneğidir. İçebakış; bir programın gözlem yaparak kendi durum nedenini ortayaçıkarabilme yeteneği. Aracılık; bir programın kendi çalışma durumunu değiştirmesi veya, kendiyorum veya anlamına yeni anlam katmasıdır. Üst nesneler; yöntemlerin, çalışma yığıtlarının, süreç gerçekleştiricilerin,ve yaklaşık bütün dil ve onun çalışma ortamının öğeleri.Üst seviye mimari; seçilmiş dizge özelliklerini sağlayan üst seviyenin, veana seviye uygulama mantığını kapsarken, yazılımın kendi farkındalığınısağlayan mimaridir.Kısmi çalışma; bir dizgeyi iyileştirmek için kullanılan teknik olup, birkısım kodlar bir grup veri girdisi ile defalarca çalıştırılır, fakat burada girdikümesinin bir kısmı değişirken diğer kısmı sabit kalır. Bu durumda verininsabit kısmına göre, kodun önçalışması oldukça yararlıdır.

C++ da üst programlama

Page 457: C++ 2 (Kitap - Selami KOÇLU)

C++ standart kütüphanesi geliştirilirken, derleme esnasında üstbilgiyitemsil etme ihtiyacı ortaya çıktı. Bu sorunlar özgün kalıp deyimi ileçözüldü. Kalıp üst programlama ile ilgili ilk yazı 1995 yılında yazıldı.IF< > deyimi kalıpta ilk denetim yapısıydı.

Üst programlamanın alt yapısı

C++ da üst programlama başlangıçta planlanmış bir konu olmayıp, dahaziyade derleyicinin suistimali, yani hor kullanılması sonucu ortayaçıkmıştır. Üst programlamanın kodları oldukça özel ve anlaşılması güçtür.Ayrıca üst programların karmaşıklığı için derleyicilerin belirlediğisınırlandırmalar bulunur.

Yansıma

Yansımaya değişik diller değişik seviyelerde destek verir. C++ nın bukonudaki en önemli özelliği; çalışma sırasında tip saptamasıdır (run timetype identification=RTTI). Bundan başka C++ da yansıtıcı eklentileri debulunur (IBM system object model= IBM dizge nesne modeli).

İki seviyeli diller

İki seviyeli diller düşüncesi durağan üst programlamanın önemliunsurudur. İki seviyeli dilde, durağan kod ile derleme sırasında değerlersaptanır, dinamik kod ise derlenir ve değerleri daha sonra çalışırkensaptanır. ISO/ANSI C++ da değişkeni olan sınıf ve işlevleri tanımlamakiçin bir kalıp mekanizması belirlenmiştir. Buna tip ve bütün kalıpdeğişkenleri ve, kısmi ve tam kalıp özelleşmeleri dahildir.Kalıplar ve C++ nın öbür özellikleri ile beraber C++ nın derleme zamanıalt dili, tam Turing dili, oluşur. (tam Turing dilinden kastedilen; enaz birkoşul ve bir döngüden oluşan dil demektir. Bütün tam Turing dilleri çokgüçlüdür ve Turing makinesinin eşdeğeridirler). Bu yüzden C++ dili ikiseviyeli bir dildir. Alt dilin tam Turing olmasından dolayı, kuramsal olarakbununla herşey gerçekleştirilebilir. Tabii pratikte, derleyici koşullarındanötürü teknik sınırlar bulunur.Temel derleme zamanı koşulu kalıp özelleşmesidir; derleyici birçok

Page 458: C++ 2 (Kitap - Selami KOÇLU)

seçeneğin dışında uygun bir kalıbı seçmek zorundadır. Derleme zamanıdöngü yapısı kendini çağıran kalıptır. (yani tanımında sınıf kalıplarının birüyesi kullanılır). Derleyici böyle düzenekleri, kendi kendilerine çağırtarakgenişletmek zorundadır.

Örnekler

Durağan kodları kullanan en yaygın örneklerden biri faktöriyelhesaplamasıdır.

n!=1.2.3.4.5......(n-1)n

Bildik C++ faktöriyel işlevi aşağıdaki gibidir;

int factorial(int n){ return (n==0) ? 1: n*factorial(n-1); }

Buna denk gelen derleme sırasında faktöriyeli hesaplayan durağan kodaşağıda verilmiştir;

template<int n>struct Factorial{ enum{RET=Factorial<n-1>::RET*n};};//kalıp özelleşmesi template< >struct Factorial<0>{ enum{RET=1};};//dikkat RET return ün kısaltılmış halidir//kullanım:cout<<”Factorial(7)= “<<Factorial<7>::RET<<endl;

Kalıp üst programlama

Derleme esnasında hesapların yapılması o kadar çekici değildir, amadurağan kodları kullanarak dinamik kodları değiştirmek çok daha ilginçtir.

Page 459: C++ 2 (Kitap - Selami KOÇLU)

Bu bir çeşit durağan üst programlamadır ve kalıp üst programlamaya denkgelir.Kalıpla üst programlama birçok kategoriye ayrılabilir;** Hesaplama tipleri ve sayılar için üst işlevler yazmak.** Üye özgünlüğü, özgün sınıflar, özgün kalıplar ve yuvalı kalıplarkullanarak üst bilgiyi temsil etmek.** Durağan denetim yapıları kullanmak.** Kodlar üreten üst işlevler kullanmak.** Açıklama kalıpları kullanıp, tekleşik olarak belli bir alana özgü dillergeliştirmek.

Kalıp üst işlevler

Kalıp üst işlevleri dinamik kod öğelerinde (dinamik kodun üst seviyesindeyer alırlar) işlem yapan sınıf kalıplarıdır. (Factorial< > veya IF< > gibi).Üst işlevler diğer üst işlevleri çağırabilirler. (aynı, diğer işlevleriçağırabilen işlevler gibi). C++ da sayı ile ilgili üst işlevler, doğrudan doğal (yani int )sayılar üzerindeişlem yapabilirler. Zira kayan noktalı sayılar kalıp değişkeni veya derlemesırasında sabit başlatıcı olarak kullanılamaz. Bununla birlikte gerektiğindekayan noktalı aritmetik, tam sayıların üstünde işlemlenebilir.

Üst bilgi ve özgünlükler (traits)

Kalıp üst programlarında durağan kod (static code) dinamik kod üzerindeişlem yapar. Dinamik kod öğelerini değiştirmek için öğelerin bazıözelliklerinin bilinmesi gerekir. (sayı tipi ile beraber hassasiyetinikapsayan değer aralığı gibi). Özelliklere özgünlük demek daha yaygıntercihtir. Özgünlükler durağan koda tam sayı sabitleri veya tipleri olarakyerleştirilir. Özgünlükler ilgili oldukları tiplere üç şekilde bağlanır:** Üye özgünlükler: her özgünlük açıkladığı tipin sabiti veya üye tipiolarak tanımlanır.** Özgünlük sınıfları: ayrı bir sınıfta birçok özgünlük sarmalanır.** Özgünlük kalıpları: aile tiplerin özgünlükleri (numeric_limits< > gibi)sınıf kalıbında tanımlanır.

Page 460: C++ 2 (Kitap - Selami KOÇLU)

Yuvalı kalıplar

Temel veri yapılarına ilaveten, çok daha karmaşık derleme zamanı veriyapılarına da (listeler ve ağaçlar gibi) ihtiyaç duyulur. Bunlar da yuvalıkalıplar kullanılarak çok kolay bir şekilde temsil edilir. Yuvalı kalıplaraörnek;

Const<1, Const<2, Const<3, Const<9, End> > > >

Kod üretimi

Kalıp üst programları, derleme sırasında döngüleri açarken, kod parçalarınıkalıpları biraraya getirmek için kullanılabilir. Bu, kodları belirli bir ortamauygun hale getirirken, üst düzeyde iyileştirilmesine ve hale gelmesineyolaçar.Kalıpla üst programlama kod seçiminde önişlemleyici yönergelerine(#ifdef vs..) göre çok daha fazla denetim sağlar. Temel fark; durağan üstprogramlama, denetlenen C++ programında bulunan durağan C++ veriyiyorumlar ve sonra programda kullanılacak yeni durağan veriyi hesaplar.Örneğin derleme esnasında değişkenlere dayalı olarak kod parçalarıbiraraya getirilebilir:

IF<(MAX_NO<AlgorithmVariantA::MAX_ALLOWED>), AlgorithmVariantA, AlgorithmVariantB>::RET::execute( );

Kalıpları biraraya getirme

Derleme esnasında temel tipler ve sınıf hiyerarşileri, çok sayıda bayrağadayalı üretmek için, kalıplar biraraya getirilir. Örnek:

typedef IF<flag==listWithCounter, ListWithLengthCounter<List<ElementType> >, List<ElementType> >::RET ResultList;

Açıklama kalıpları

Page 461: C++ 2 (Kitap - Selami KOÇLU)

Açıklama kalıpları bir programlama tekniğidir. İşlev ve işleç çağrılarıiçeren C++ açıklamalarına uygun kodlar üretilmesini sağlarlar. Bunlarınsayesinde aşağıdakiler gerçekleştirilebilir;

** Derleme esnasında açıklamaların yapısını alana özgü sınamak. Busınamalar, C++ dizgelerinde bir şekilde yapılamayan sınamalardır. (örnek:C++ açıklamalarında 5 taneden fazla + yanyana bulunamaz).** Açıklamalar için, derleme esnasında iyileştirme dönüşümleri ve enuygun kod üretimi yapılabilir.

Kendiliğinden kod genişlemesi

Kalıpla programlama kodları kendiliğinden genişletmede de kullanılabilir.(örnek: döngü açma ve sınama kodu üretme). Örnek: derleme esnasındam**n nin n si bilinirse;

template<int n>inline int power(const int& m){ return power<n-1>(m)*m;}template< >inline int power<1>(const int& m){ return m;}template< >inline int power<0>(const int& m){ return 1;}///cout<<power<3>(m)<<endl;

Denetim yapıları

Kalıp üst programlarında denetim akışları, kendini çağırma (kendiniçağıran kalıplar), seçici uygunluk (kalıp özelleşmeleri) ve koşul işleci (?:)kullanılarak tanımlanır. C++ da bulunan if ve switch gibi seçim deyimleriüst işlevler kullanılarak eşdeğerleri elde edilebilir.

Page 462: C++ 2 (Kitap - Selami KOÇLU)

Kalıp üst programlamanın sorunları

Kalıp üst programlama özellikle derleyicilerden kaynaklanan çok sayıdasoruna sahiptir. Bu yüzden bu tür programları geliştirirken dikkatliolmalıdır.

** Hata ayıklama.** Kodun okunabilirliği.** Derleme süresi.** Derleyici sınırları.** Açıklama kalıplarının sınırları** Aktarılabilirlik.

Page 463: C++ 2 (Kitap - Selami KOÇLU)

İkinci cildimiz burada sonlanıyor. Bu kitap azami dikkatle yazılmış olup,gözden kaçan hatalar olmuşsa, yazara herhangi bir sorumlulukyüklenemez. Okur dikkatten kaçan hataları aşağıdaki adrese bildiririrsememnun oluruz.

[email protected]

Page 464: C++ 2 (Kitap - Selami KOÇLU)

2. cilde Sonsöz

1. cildimizde C/C++ nın bütün temel özellikleri anlatılmıştı. Bucildimizde ise, kütüphaneler konusu örnekler ile anlatılmaya çalışıldı.Aslında kütüphanelerin her konusu, ayrı bir kitap olacak kadar geniştir.Ama biz kitabı fazla şişirmemek için basit şekilde anlattık. 1. cildimizinönsözünde de belirttiğimiz gibi, 21. yüzyılda yazılımda asıl konu; çokçekirdekli işlemciler ve eş anlı programlama, dolayısı ile tutaklardır(threads). Tabii eş anlı programlama sadece tutaklarla çözülebilecek birsorun değildir. Bu konuda farklı ortamlar için farklı yaklaşımlarbulunmaktadır. Tek bir çözüm yoktur. Sanal makinalar bunun sihirli ilacıdeğildir. (JVM=java virtual machine nin kulakları çınlasın). İşin mimariyanı vardır, derleyici yanı vardır, işletim dizgesi yanı vardır, vehepsinden önemlisi çözülecek sorun yanı vardır. 21. yüzyıldayazılımcıların önündeki en önemli fırsat, eş anlı programlamadır. Yüzdeyüz verimi sağlayan ortam, yazılımcı arkadaşı yeni Bill Gates yapabilir.C/C++bu iş için en iyi yardımcısıdır.Burada anlattığımız kütüphaneler den birkaçı, eş anlı programlamadayeniden tanımlanmak zorundadır (örneğin string ler), ama hepsi değil.Boost kütüphanesi standart kütüphanenin bu sorununu kolaycaçözümlemektedir. 3. cildimizde bunlar çok ayrıntılı anlatılacak. Tercihlerise şimdilik programcıya bırakılacak. Zira eş anlı C++ standartlarıtahmini 2010 yılında yayınlanacak.Aklımıza gelmişken açıklayalım; 1 numaralı teknik raporun derleyicileregetirdiği yenilikleri, kitabımızın kapsama alanının dışında bıraktık. Zirabunlar şu an ki derleyicilerde bulunmayan özellikleri taşımaktadır. Buözellikler 2007 den sonra geliştirilecek olan derleyicilere eklenenözelliklerdir. (Bkz C++ TR.1)Umarız bu kitap size yararlı olur.

Selami KOÇLU

Page 465: C++ 2 (Kitap - Selami KOÇLU)

8-Aralık-2006

İÇİNDEKİLER

Standart C/C++ kütüphanesi ................................. 2String'ler ................................. 5Girdiler ve Çıktılar ................................. 30Kalıplar (Templates) ................................. 99 Kalıp Sınıflar (Template Classes) ........................ 133Standart Kalıp Kütüphanesi ............................... 202Algoritmalar ............................... 270İstisnalar (Exceptions) ............................... 377SKK Nesne İşlevler (functors) ............................ 409Bitset ............................ 434Öbür Kütüphaneler ............................ 452Üst Programlama (Metaprogramming) .............. 456

Page 466: C++ 2 (Kitap - Selami KOÇLU)
Page 467: C++ 2 (Kitap - Selami KOÇLU)
Page 468: C++ 2 (Kitap - Selami KOÇLU)
Page 469: C++ 2 (Kitap - Selami KOÇLU)
Page 470: C++ 2 (Kitap - Selami KOÇLU)
Page 471: C++ 2 (Kitap - Selami KOÇLU)