C/C++
Kütüphaneler
1.BÖLÜM
Standart C++ kütüphanesi
Standart C++ kütüphanesi standart C kütüphanesinin tamamını içerdiği gibi, 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üphanesi
standart 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 en iyi kaynak bizzat standardın kendisidir.
Standart C++ string sınıfını ele alacağız. string sınıfı, metin süreçlerini geliş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 üye iş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([]) de bindirime uğratılarak işlemler gerçeklenebilir. Ayrıca uluslararası
karakterleri desteklemek için "geniş" bir wstring sınıfı tasarlanmıştır. Hem
string 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_string ortak 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üzden strstream 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ı bildirir ve, <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ızda kullanabilirsiniz. !=, <=, >=, > işleçlerinin kalıp sürümleri bulunur (kısaltılmış tanımları engellemek için), tuple yapan kalıp işlevini
bulunduran pair kalıp sınıfı, Standart Kalıp İşlevlerini (STL) desteklemek iç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şim
mekanizmaları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 kapsar
Yineleyici 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ızda kullanı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
konuyu ayrıntıları ile irdeleyeceğiz. Zira kaynak kodları (değiştirerekte olsa) 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ıplar ile üst programlama da yapılabilmektedir. Üst programlama C++ da (C dilinde yok), derleyicinin yorumlayıcı gibi kullanıldığı bir uygulamadır. Biraz zor bir konu gibi görünse de, sağladığı olanaklar inanılacak gibi değildir. Üst program; programı programlayan program olup kod işlemler, uygulamada C++ yeni bir dil gibi davranır. Aslında bu konu bir kitap kaplayacak 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 üst
programlama da yapılabilmektedir. Bu da C++ dilinin, kullanmayı bildikten sonra rakipsiz olduğunu göstermektedir. Ama hiçbir zaman unutulmaması gereken; ışık vermek için yanmalı. Tabii siz.
String'ler
string C dilinde üzerinde en fazla vakit harcanan ve yanlış anlamalara yol
programlarının vazgeçilmezidirler. C string işlevlerini C++ da kullanmak iç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 string sı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ır sonlandırıcı olarak adlandırılır) olan karakter dizisidir. Karakter dizileri harf ve rakamlardan oluşur. C stringleri ile C++ stringleri arasında iki fark vardır; birincisi, karakter dizisi olan C++ string nesneleri, yönetmek ve işlemlemek için gereken işlevlere sahip string sınıfı tarafından
oluşturulurlar. Bir string, verilerin saklama alanı ve boyutu hakkında da bilgiler taşır. Özellikle, C++ string nesnesi bellekteki başlangıç yerini, içeriğini, karakter uzunluğunu, ve arttırılabilecek uzunluğu bilir. Bu da ikinci farkı ortaya çıkarır; C++ string nesneleri C string karakter dizileri gibi sıfır öğe ile sonlanmaz. Genellikle açıklamalarda; C char dizi, C++
string nesne kullanılır. C++ string nesneleri, C char dizilerinden
kaynaklanan hataları en aza indirir. C stringlerinde rastlanan en berbat hatalar; dizi üzerine yeniden yazma, diziye doğru değer atanmamış veya ilk 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ım yapması (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 string nesne verilerinin nerede hangi koşullarda tutulduğu hususunda, önceden tam bilgi sahibi olunamaz.
C dilinde string işlevlerini kullanmak için #include <string.h>, karakter iş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ı olarak bunları da kullanabilirsiniz. Ama bizim önerimiz C++ dilinin kendi
standart 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ılamaz hatırlatalım).
Şimdi C++ da kullanılabilecek C string ve karakter işlevlerini verelim;
int isalpha(int ch) ; cctype int isblank(int ch) ; cctype int iscntrl(int ch) ; cctype int isdigit(int ch) ; cctype int isgraph(int ch) ; cctype int islower(int ch) ; cctype int isprint(int ch) ; cctype int ispunct(int ch) ; cctype int isspace(int ch) ; cctype int isupper(int ch) ; cctype int isxdigit(int ch) ; cctype
void* memchr(const void* buffer, int ch, size_t count) ; cstring int memcmp(const void* buf1, const void* buf2, size_t count) ; cstring void* memcpy(void* to, const void* from, size_t count) ; cstring void* memmove(void* to, const void* from, size_t count) ; cstring void* memset(void* buf, int ch, size_t count) ; cstring
char* strcat(char* str1, const char* str2) ; cstring char* strchr(const char* str, int ch) ; cstring
int strcmp(const char* str1, const char* str2) ; cstring int strcoll(const char* str1, const char* str2) ; cstring char* strcpy(char* str1, const char* str2) ; cstring size_t strcspn(const char* str1, const char* str2) ; cstring char* strerror(int errnum) ; cstring
size_t strlen(const char* str) ; cstring
char* strncat(char* str1, const char* str2, size_t count) ; cstring int strncmp(const char* str1, const char* str2, size_t count) ; cstring char* strncpy(char* str1, const char* str2, size_t count) ; cstring char* strpbrk(const char* str1, const char* str2) ; cstring
char* strrchr(const char* str, int ch) ; cstring size_t strspn(const char* str1, const char* str2) ; cstring char* strstr(const char* str1, const char* str2) ; cstring char* strtok(char* str1, const char* str2) ; cstring
size_t strxfrm(char* str1, const char* str2, size_t count) ; cstring int tolower(int ch) ; cctype
int toupper(int ch) ; cctype
Burada cstring ve cctype açıklamalarını, ilgili işlevi C++ dilinde kullanırken, yazılacak başlık biçimini hatırlatmak için verdik. Örnek;
#include <cstring> #include <cctype>
İşlevlerin kullanım ayrıntılarını, C standartlarının bulunduğu herhangi bir kitapta 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 yerine
tercih edilebilmektedir. O nedenle bu tür bellek yerleşim işlevlerinin ayrıntılarını öğrenmeniz sizin yararınızadır. Burada C ile ilgili daha fazla ayrı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 bir sı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ın
iç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şebilme
yö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ın nesnesi oluşturulurken en önemli şeyi dikkate alalım; sınıfla ilgili seçenekler. String sınıfına ait 6 adet yapıcı işlev aşağıda ayrıntıları ile verilmiş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
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, baş-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şturur
5-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ürer
6-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 string nesnesini göstertir, ve [] bindirilmiş işleci stringin herhangi bir üyesine erişimi sağlar. Şimdi yukarıda anlatılan yapıcı işlevler ve işleçlerin kullanı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 ;
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 hersey
iyi, 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ğer atamaları içinde geçerlidir.
string sınıf girdileri
seçeneklerdir. C biçimi string çağrımları için üç seçenek bulunur;
char bilgi[100] ;
cin>>bilgi ; //bir kelime oku
cin.getline(bilgi, 100) ; //bir satır oku, gözardı et\n
cin.get(bilgi, 100) ; //bir satır oku, kuyrukta bırak\n
string nesnesinin girdi seçenekleri neler?. String sınıfı önce >> işlecini
bindirime uğratır. İlk nesne (cin) string nesnesi olmadığı için, >> işleci
string sınıfı yöntemi değildir. Onun yerine, istream nesnesini (cin) ilk
değişkeni olarak, string nesnesini de ikinci değişkeni olarak alan genel bir iş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ığında girişi sonlandırır, dosya sonunu algılar, veya string nesnesine saklanabilen en fazla sayıdaki karaktere ulaşabilir. İşlev öncelikle hedef stringin
içeriğini siler ve daha sonra her seferinde bir karakter olmak üzere okur ve yazar. String nesnesine izin verilen karakter sayısından daha az sayıda giriş yapılırsa, operator>>(istream&, string&) işlevi string nesnesinin boyutunu otomatik olarak girişe uydurur. Özet olarak şunu söyleyebiliriz; >> işlecini C biçiminde olduğu gibi C++ da da aynen kullanabiliriz. Ayrıca dizi 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ğerini
koymak (operator>>( ) işlevinden farklı) biraz daha az şeffaftır, onun yerine ü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 yeni
bir üye işlev eklenmesi gerekir. Tabii bu akıllıca olmazdı. Onun yerine
string kütüphanesinde üye olmayan getline( ) işlevi tanımlanır. Bu işlev
ilk değişken olarak istream nesnesi, ikinci değişken olarakta string nesnesi alır. Böylece girişten string nesnesine bir satır okumak için ;
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 string nesnenin kapasite sınırına kadar sürer. Eğer yeni satır (\n) karakteri
algılanırsa, bu karakter okunur fakat string nesnesine eklenmez. Burada hemen bir şeyi belirtelim; istream sürümüne benzemeyen biçimde string sürümde, okunacak karakterlerin maksimum sayısını veren boyut
değişkeni bulunmaz. Bunun nedeni; string nesnesinin boyutu otomatik olarak ayarlamasıdır. Aşağıdaki örnek iki türlü giriş seçeneğini
gö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ı onundu Paraların hepsi istenilenmi
tam bir satir gir: bu örnek programı herkes begenirdi degilmi Satir: bu örnek programı herkes begenirdi degilmi
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, ve string nesnelerini birbirine eklemek bunların arasındadır. Başka neler
yapı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ğerinden
makine metin karşılaştırmasında daha önce yer alırsa, ilki diğerinden daha küçü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ük
karakterlerde küçük karakterlerden küçük demektir. Her ilişkinlik işleci üç türlü bindirime uğrayabilir; ya bir string nesnesini başka bir string nesnesi ile karşılaştırırsınız, ya bir string nesnesini C benzeri string ile
karşılaştırırsınız, ya da C benzeri stringi bir string nesnesi ile karşı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ğmen neden ikisi de var. Bunun nedeni; length( ) işlevinin string sınıfının ilk sürümlerinde bulunması, size( ) işlevinin standart kalıp kütüphanesi uyumluluğu sağlamak için oluşturulmasıdır.
Bir string içinde alt stringler veya karakterleri değişik yollarla
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 lur. Bulduğu zaman altstring ilk karakter sırasını geri 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, ka-size_type n)const rakterini pos noktasından maya başlayarak bulur.
Bulduğunda altstring ilk 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 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.
find_first_of( ) değişkende yer alan karakterlerden herhangi birinin stringte 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 rastlanan
karakterin “r” olmasıdır. find_last_of( ) yöntemi de benzer fakat ters biç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 geri
döndürür. find_first_not_of( ) yöntemi ise değişkende olmayan stringin ilk 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ın ayrıntılarını verelim;
//string işlemleri:
const charT* c_str( ) const; const charT* data( ) const;
allocator_type get_allocator( ) const ;
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, size_type n2) const;
basic_string& append(const basic_string& str);
basic_string& append(const basic_string& str, size_type pos, size_type n);
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_type n);
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_type pos2,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, const basic_string& str);
basic_string& replace(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2) ;
basic_string& replace(size_type pos, size_type n1, const charT* s, size_type n2) ;
basic_string& replace(size_type pos, size_type n1, const charT* s) ; basic_string& replace(size_type pos, size_type n1, size_type n2, charT c) ;
basic_string& replace(iterator i1, iterator i2, const basic_string& str) ; basic_string& replace(iterator i1, iterator i2, const charT* s, size_type n) ;
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 ciltte
verilmiş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. npos değ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östermede
kullanılabilir.
string nesne kullanımında C işlev bindirimi
string nesnelerini karşılaştırırken bindirilmiş == işleci kullanılır. Fakat
büyük harf küçük harf ayırma özelliği nedeni ile, bu işleç bazı durumlarda sorunlara neden olur. Yani büyük harf küçük harf ayrımı istenmediği zaman başka çözüm bulunması gerekir. Durumu bir örnekle açıklayalım;
#include <string> //string nesnesi için
...
string strA ;
cin>>strA ; //kullanıcı selami adını girsin string strB=”Selami” ; //belleğe konan sabit if(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 ne yapmalı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ırlatma yapalım; bu işlevler C standardında bulunmamaktadır. O yüzden bu
işlevlerin size ait bindirilmiş sürümlerini yazmanız, ve onları programınıza eklemeniz gerekebilir. Şimdi yukarıdaki örneğin benzerini büyük harf küçü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 edelim
string strB=”Selami” ; //bellekteki sabit inline 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,
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 ile aynı 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 [] ile
at( ) işlevi arasında büyük bir fark bulunmaktadır. at( ) işlevi dizi
sınırlarının dışında bir öğeye erişilmek istendiği zaman istisna
fırlatılmasını sağlar, [] (köşeli ayraçları) ise sizi bu durumda bir başınıza bı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ğerleri içerirler. Bu değerler aslında istenmeyenler olup, kullanılmaya kalkıldığı zaman programın dengesini alt üst edecek sonuçlara yolaçabilirler. O nedenle istisna yönetimi birinci kitapta da belirttiğimiz gibi önemli bir konudur. Burada da at( ) gibi istisna fırlatmayı sağlayacak işlevi, [] yerine kullanmak oldukça yararlıdır. İstisnai durum ortaya çıktığı zaman, istisna yöneticisi, programın düzenini sürdürmek için, sonraki eylemleri ona göre dü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ş harfli kelimelerden oluşan string nesneler dizisine sahiptir, ve gelişigüzel olarak bunların içinden birini seçer. Sizde bu kelimeyi tahmin etmeye çalışırsınız.
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 ;
}
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
kümesinin sağladığı kolaylıklar, yukarıdakilerle sınırlı değildir. Stringin bir kısmını veya tamamını silen, bir string ile diğer bir stringin bir
parçasını veya tamamını yerdeğiştiren, stringe başka unsurlar ekleyen veya 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şlevler
bulunmaktadır. Bir işlevde, bir stringin bir parçasını diğer bir stringe kopyalamaktadır. Ayrıca string içeriklerini değiş tokuş eden bir işlevde bulunmaktadır. Bu işlevler string nesneleri ile çalıştıkları kadar, bindirime uğratılarak C benzeri stringlerle de, aynı biçimde çalışırlar. Burada birçok kı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ına yaslanarak 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 belli koşulları yerine getirip karakter benzeri unsurlar için bir sınıf geliştirebilir ve basic_string kalıp sınıfını onunla kullanabilirsiniz. traits sınıfı seçilen karakterlerin öne çıkan özelliklerini tanımlayan bir sınıftır, örnek olarak değerlerin karşılaştırma biçimini açıklaması gibi. char ve wchar_t tipleri için, char_traits kalıbının önceden tanımlanmış özellikleri bulunur. Ve bunlar traits kalıbının varsayılanlarıdır. Allocator sınıfı, bellek
yerleşimini yöneten sınıftır. char ve wchar_t tipleri için allocator kalıbının önceden tanımlanmış özellikleri vardır. Bunlar da allocator varsayılanlarıdır. Genellikle new ve delete işleçleri bellek yerleşiminde kullanılır. Ama isterseniz belleğin bir yerini kendi bellek yönetim
biçimleriniz için ayırıp kullanabilirsiniz. Bu da sizin yeteneklerinize bağlı. Aslında herşey sizin paradigmaları kavrayıp onları uygulama becerilerinize kalmıştır. Burada kurallar anlatılır birkaç örnekle uygulama gösterilir, geri kalan sahneleri ve oyunları siz oynamalısınız. Programcılık kuralları ilgili dil tarafından konmuş zeka sanatıdır. Notaları herkes öğrenebilir ama, tutulan besteyi herkes yapamaz.
Ö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 okur
int 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; }
// Yılı okur int 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ı okur int 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 okur int 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 Nov Dec
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 bir
C-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;
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 etkilenmiyor
string 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 etkilenmiyor
string 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 stringe yerleştirme.
#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;
}///:~
// öğ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şlemlerini iki 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;
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. Yani
standart kütüphanenin tamamını içeren “using namespace std;” isim alanı yerine, tek tek ilgili nesnelerin standart isim alanları seçilmiştir. Böylece derleyicinin isimlerle ilgili kapsama alanı daraltılmıştır. Program daha verimli kılınmıştır. using std::cout ve using std::cerr; vs benzeri
açı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önergesini kullanmak daha elverişlidir.
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 bilgi
demektir.Giren bilgi yani girdi, çevresel birimden gelebildiği gibi
programın içindeki bir başka dosyadan da olabilir. Çıktı ise programdan herhangi 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ında
genel bir sorunla karşılaşılır. Zira bir yandan, uygulamaların en basitinde bile girdi/çıktı işlemlerinin olması, ve dil öğrenme sürecinde karşılaşılan ilk 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 özellikler arasında sınıflar, türetilmiş sınıflar, işlev bindirimi, sanal işlevler, çoklu kalıtım ve kalıplar yer alır. Bundan dolayı C++ girdi/çıktı işlemlerini tam anlamı ile kavrayabilmek için, çokca C++ bilmek gereklidir. Başlangıç olarak, girdilerde kullanılan cin nesnesinin istream sınıfı, çıktılarda
kullanılan cout nesnesinin ise, ostream sınıf nesnesi olduğunu belirtelim.
C++ dilinde C dilinden farklı olarak, girdi/çıktı işlemleri sınıflara
yaslanarak geliştirilmiştir.
Artık girdi/çıktı sınıflarını uzun uzun incelemenin zamanı geldi. Böylece bu sınıfların tasarım ve çıktı biçemlerini öğreneceğiz.
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ört kapı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 sonra aynı 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 eylem farklı olduğu için girdi çıktı tanımları farklılaşıyor. Siz eski odadan yeni bir odaya geçtiğinizde artık yeni odanın girdisisiniz. Yani bu durumda birinci odanın çıktısı ikinci odanın girdisi oluyorsunuz. Aslında
bilgisayarlardaki 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çine
koyarlar. Bu tür dillerde print veya write benzeri deyimler, doğrudan dilin anahtar kelimeleri arasında yer alır. C/C++ dilleri böyle değildir. Gerek C gerekse C++ dillerinin anahtar kelimelerine bakarsanız, if, for vs.. gibi kelimeler bulunmasına rağmen, girdi/çıktı işlemleri ile ilgili bir anahtar kelimeye rastlayamazsınız. C, aslında girdi/çıktı işlemlerinin yerine
getirilmesini, derleyiciyi kullanana bırakır. Bunun bir nedeni; hedeflenen bilgisayarı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ında
UNIX 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 kendi
bünyesinde de kullanılmasına izin verir. Böylece C dilindeki stdio.h da yeralan işlevler hakkında bilgi sahibi iseniz, bunları C++ programlarının içinde kullanabilirsiniz. C++ da bunları kullanmak için başlık dosyasına
cstdio 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ık
dosyaları ile tanımlanan sınıflar kullanılır. Sınıf kütüphanesi C++ resmi dil tanımının bir parçası değildir. Sadece nesne yönelimli özelliğinden
kaynaklanan ve kabul edilmiş standart kütüphanedir. C dili ile C++ dilini birbirinden ayıran temel özellik budur; C de sorun dile uyar, C++ da ise dil soruna uyar. C++ da cin ve istream anahtar kelimeler değildir.
Hepsinden öteye C++ bilgisayar programlama dili, sınıflar ve benzeri unsurların nasıl oluşturulacağının kurallarını koyar. Bu kuralların ardından
nelerin yapılması lazım geldiği ile uğraşmaz. C işlevlerin standart kü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şturup inceleyelim.
Akışlar (streams) ve Tamponlar (buffers)
C++ programlarında girdi ve çıktılar, baytların akışıdır. Girdide, program baytları girdi akışından elde eder. Çıktıda ise program, baytları çıktı
akışına yerleştirir. Metine yönelik bir programda, her bayt bir karakteri temsil eder. Daha genel bir deyişle baytlar, karakter veya sayısal verinin iki tabanlı sayılarla gösterimini sağlar. Girdi akışındaki baytlar klavyeden gelebildiği gibi, herhangi bir bellek aracından (hard disk gibi), veya başka bir programdan da gelebilir. (girdi çıktı anlatımında yukarıdaki odalar benzetimini hatırlayın). Benzer şekilde, çıktı akışındaki baytlar da ekrana gittiği gibi, yazıcıya, bir bellek aracına veya başka bir programa da
gidebilir. Akış, programla akışın kaynağı, veya akışın varış yeri arasında arabulucu gibi davranır. Bu yaklaşım C++ programlarının, klavyeden giren bilgi ile, dosyadan giren bilgiye de aynı şekilde davranmasını sağlar. Yani C++ 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ğı yere bakmaksı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ıya gerek 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çta olabilir, ö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 daha etkili olmaktadır. Tampon büyük bir bellek parçası olup, bilginin bir araçtan programa veya programdan bir araca aktarımı sırasında kolaylık sağlayan, geçici olarak kullanılan saklama yeridir. Tipik olarak disk sürücüsü gibi araçlar, bilgileri 512 baytlık (veya daha fazla) büyüklükler halinde aktarırlar. Programların bilgiyi işlemleme büyüklüğü ise, her
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 karakter okur işlemler, dosyadan bir sonraki karakteri okur işlemler, ve bu böyle sürer gider. Hard diskte yer alan bu karakter için her seferinde dosyadan bir 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, daha sonra bu okunan alan tampon da saklanır, ve sonra tampon her seferinde bir karakter olmak üzere okunur. Bu tercihin nedeni; tampon bellekten okuma 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 hard diskten bir sonraki bellek alanını okur. Tekrar tampon belleğe atar, böyle sürer. Bu, suyu büyük bir depoda (tampon) toplayıp, daha sonra hızla damlatarak (baytları tek tek okuma) akıtmaya benzer.
Çıktılarda da benzer şekilde program tamponu önce doldurur, daha sonra tampondaki verinin tamamını hard diske aktarır, çıktının bir sonraki verisini aktarabilmek için tampon silinir, bir sonraki veri tampona yerleş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 tampon gerekmez. Zira farklı veri aktarım hızları bulunmamaktadır. Bununla
birlikte 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, bu nedenle bilgisayarlarda klavyeden girdi yazarken enter tuşuna basıncaya kadar programda işlemleme yapılmaz. Ekrana çıktı gönderebilmek için de, C++ programı tamponda yeni satır karakterini (\) görmeyi bekler. Tabii bunu sizin göndermeniz gerekir. Gelmekte olan girdi için program uygulamaya bağlı olarak önceki girdiyi boşaltabilir. Başka bir deyişle programa 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üzel
uygulamaları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 program geliş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.
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ıza
gerçekleyen sınıflar kullanılır. C++ dilinin son Girdi/Çıktı (I/O) sürümleri
char ve wchar_t verilerini destekleyen sınıf kalıpları tanımlamıştır. Bu
kalıpların özel char uygulamaları içinde, typedef sayesinde C++ klasik kalıpsız girdi/çıktı işlemleri gerçeklenebilir. Bunlarla ilgili sınıfların bazıları aşağıda verilmiştir;
**streambuf sınıfı tamponlar için bellek yeri temin eder. streambuf sınıf
yöntemleri ile (yani yapıcı işlevleri ile) tamponlar doldurulur, tampon iç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çin
açı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. ios
sı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ından türetilmiş ve yeteneklerini ondan almıştır (dolayısı ile ios_base sınıfından).
ios sınıfının önemli bir işlevi de akışlar tarafından kullanılan tamponla
iletişimi tanımlamaktır. Tampon streambuf nesnesidir (veya streambuf sı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 kendileri gerçeklemez demektir. Anılan işlemler ilgili tampon nesnelerine bı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 (yani
yapıcı işlevlerini) bulundurur.
**istream sınıfı da ios sınıfından türetilmiştir. Girdi yöntemlerini
bulundurur.
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 nesneleri
oluşturulmalıdır. Örnek olarak; çıktıyı yönetmek için ostream sınıf nesnesi
cout kullanılması gibi. Böyle bir nesne oluşumu ile akış açılır, otomatik
olarak tampon oluşur ve onu akışla ilişkilendirir. Ayrıca sınıf üye işlevleri de hizmet vermeye hazırdır.
Örnek:
cout<<”Okullarda”<<talebe<<”bulunmakta\n” ;
Yukarıdaki örnekte derleyici, “talebe” değişkeninin tipini saptar ve değ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 tipi arasında uyumsuzluk olduğu takdirde, derleyici sizi uyarmaktan başka bir iş 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ştan engellenmiştir.
iostream kütüphanesinde akış nesneleri sınırlı bir role sahiptir. Bunlar bir
yandan, girdi veya çıktı nesneleri arasında arayüz oluştururken, öte yandan
streambuf, araçlara asıl girdi ve çıktılar sorumlusu olarak ilk adımda streambuf nesnesini oluşturmuştu. Bu yaklaşım yeni bir araç çeşidi için
yeni bir çeşit streambuf oluşturmak amacı ile streambuf eski iyi dostlar
istream veya ostream sınıfları ile birlikte kullanılır. Burada kavranması
gereken en önemli şey; iostream nesnelerinin biçemleme yeteneği ile, bir araç 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 nesnesi gerekmez. Kaplayıcı (wrapper class)bir sınıf istream veya ostream
sınıfları etrafında inşa edilebilir, ama sadece belli bir araca kolaylıkla erişebilmek amacı ile. Stringstream sınıflarının inşa edilme biçimleri bunlardı.
ios_base
ios
istream
ostream
ifstream
istringstream
ofstream
ostringstream
iostream
streambuf*
filebuf*
stringbuf*
GİRDİ
rdbuf( )
streambuf*
ÇIKTI
Ö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ıza eklenebilir.
**#include <iosfwd> : iostream sınıfları için önceden bildirim
gerektiği zaman bu önişlemleyici yönergesi kullanılmalıdır. Örnek olarak bir işlev ostream e bir dayanç değişkeni tanımlarsa, işlev kendini
bildirirken, derleyici ostream in ne olduğunu tam olarak bilmesi gerekmez. Başlık dosyasında böyle bir işlevin bildiriminde, ostream sınıfının sadece bildirimi gerekir. Ama aşağıdaki yazımı kullanmazsınız;
class ostream ; //hatalı bildirim void birfunc(ostream &str) ;
Bunların yerine;
#include <iosfwd> //doğru bildirilmiş ostream void birfunc(ostream &str) ;
kullanılmalıdır.
**#include <streambuf> : streambuf ve filebuf sınıfları kullanılırken bu önişlemleyici yönergesi yazılır.
**#include <istream> : istream sınıfları kullanırken veya hem girdi hem 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çekleyen sı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.
**#include <sstream> : string akış sınıfları kullanılırken gereken önişlemleyici yönergesi.
**#include <iomanip> : değerleri belirlenmiş özelleştiricilerin kullanı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, onun tanımladıkları arasında yer alır. G/Ç kütüphanesinin her akış sınıfı, ios sı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 resmi parç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 de fakto 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çin
yardı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 nesnesini doğrudan inşa etmek mümkün değildir. ios_base nesneleri ios_base
sınıfından türetilen sınıflar aracılığı ile inşa edilir. (ios_base::ios_base yapı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ıf kolaylı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 hemen
hiç 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ından yönetilen streambuf nesnelerinin yönetimini sağlamaktır. Sağlanan