• Tidak ada hasil yang ditemukan

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

N/A
N/A
Protected

Academic year: 2021

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

Copied!
471
0
0

Teks penuh

(1)

C/C++

Kütüphaneler

(2)

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.

(3)

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

(4)

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.

(5)

String'ler

string C dilinde üzerinde en fazla vakit harcanan ve yanlış anlamalara yol

(6)

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;

(7)

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;

(8)

#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

(9)

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 ;

(10)

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

(11)

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 ;

(12)

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

(13)

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

(14)

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.

(15)

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 ;

(16)

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;

(17)

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) ;

(18)

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;

(19)

#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,

(20)

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.

(21)

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 ;

}

(22)

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

(23)

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.

(24)

Ö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; }

(25)

// 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;

(26)

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.

(27)

#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;

}///:~

(28)

// öğ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;

(29)

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.

(30)

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.

(31)

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

(32)

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

(33)

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.

(34)

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.

(35)

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ı.

(36)

ios_base

ios

istream

ostream

ifstream

istringstream

ofstream

ostringstream

iostream

streambuf*

filebuf*

stringbuf*

GİRDİ

rdbuf( )

streambuf*

ÇIKTI

(37)

Ö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.

(38)

**#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

Referensi

Dokumen terkait

Tujuan umum asuhan keluarga adalah ditingkatkannya kemampuan keluarga dalam mengatasi masalah kesehatannya secara mandiri dalam mengenal masalah kesehatan keluarga,

Dalam novel air mata terakhir bunda penulis ingin melakukan penelitian terhadap novel tersebut bagaimana model komunikasi antarpribadi yang dilakukan seorang ibu

Seorang perawat harus mampu mengenali perasaan klien untuk dapat menciptakan hubungan terapeutik yang baik dan efektif dengan klien.. Dengan  bersikap sensitive

• Sebaliknya, pertambahan pertumbuhan diameter batang pohon gmelina lebih tinggi 25% apabila pemangkasan cabang dilakukan pada 30–40% dari total tinggi pohon dibandingkan

&#34;leh karena itu dapat disimpulkan bahwa Manajemen Strategis adalah sebuah proses yang dilakukan suatu perusahaan atau organisasi untuk  mempertahankan

Klien datang dengan perdarahan pasca coitus dan terdapat keputihan yang berbau tetapi tidak gatal. Perlu ditanyakan pada pasien atau keluarga tentang tindakan yang dilakukan

Dan dari hasil evaluasi diperoleh bahwa konsumsi rata-rata energi listrik pelanggan PLN Kota Medan untuk pelanggan 450 VA sebesar 106,3 kWh dengan varians 31,3 lebih tinggi

Reksa dana saham adalah reksa dana yang portofolio investasinya pada instrumen berbentuk saham (equity) dengan jumlah sekurang-kurangnya 80% (delapan puluh persen) dari total