Professional Documents
Culture Documents
C Necatiergin
C Necatiergin
indekiler
C Dili ile C++ Dili Arasndaki Farkllklar (3)
Referanslar (21)
Parametre Deikenlerine Varsaylan Deerlerin Aktarlmas (37)
lev Yklemesi (43)
Snflar (53)
le Yklemesi (133)
sim Alanlar (155)
Tretme (171)
Sanal levler ve ok Biimlilik (197)
lev ablonlar (221)
alma Zaman Hatalarnn Yakalanmas ve lenmesi(247)
alma Zamannda Tr Belirlenmesi (279)
new ve delete lelerinin Yklenmesi (291)
oklu Tretme (303)
C DL LE C++ DL ARASINDAK
FARKLILIKLAR
Bu blmde ANSI C dili (C89 ISO/IEC 9889:1990) ile C++ dili (ISO/IEC 14882:1998)
arasndaki szdizim (sentaks) farkllklarn ele alacaz.
Bir C++ derleyicisi ile ayn zamanda C'de yazlm kaynak kodlar da derlenebilir. C++
derleyicisinin ayarlar (settings) deitirilerek yazlacak kaynak kodlarn dorudan C
kaynak dosyas olarak ele alnmas salanabilir. Ayrca derleyicilerin hemen hepsi,
yaratlan kaynak dosyann uzantsna bakarak -rnein uzantnn .c ya da .cpp olmasna
gre- kaynak kodu hangi dilin kurallarna gre derleyeceklerini anlar.
C++ dilinin temel szdizimsel yaps C dili szdiziminin zerine birtakm eklemelerin
yaplmasyla oluturulmutur. Bu durumda C dili szdizimsel adan C++ dilinin bir alt
kmesi olarak grlebilir. Baz noktalarda da C ve C++ dilleri szdizim kurallar asndan
farkllklar gsterir.
C ile C++ arasndaki szdizimsel farkllklar iki ana grupta ele alnabilir:
1. C alt programlama tekniine destek veren bir dilken, C++ birden fazla programlama
tekniine destek verir (multiparadigm). ki dil arasndaki farkllklarn nemli ksm, C++
n dier programlama tekniklerine destek verebilmek iin ekledii yeni aralara ilikindir.
2. Dier farkllklarn dorudan programlama teknikleriyle ilgisi yoktur. Bu farklar C++'
daha iyi bir C yapma amacna ynelik yaplan deiiklikler ve eklemeleri kapsar.
Cde yazlan bir dosya C++ diline, C++'da yazlan bir dosya da C diline tanmak
istenebilir. ANSI C dilinin kurallarna gre yazlan ve geerli olan bir kaynak dosya C++
diline tandnda, C++ dilinin kurallarna gre geersiz durumlar oluabilir.
phesiz bunun tersi de sz konusudur. Yani C++da bir szdizim hatas iermeyen bir
kod paras C dilinin kurallarna gre geersiz olabilir.
Baz durumlarda da yazlan kod her iki dilin kurallarna gre geerli olarak deerlendirilse
de, yazlan kod iki dilin kurallarna gre farkl anlamlara sahip olabilir. Bir kaynak dosyann
bir dilden dierine tanmasnda kural deiiklikleri yznden uyumsuzluk sorunlar ile
karlalabilir.
C ve C++ dilleri arasndaki temel szdizime ilikin farkllklar ayr balk altnda ele
alacaz:
C'de geerli C++'da geersiz olan durumlar
C++'da geerli C'de geersiz olan durumlar
C ile C++ arasndaki kural farkllklar
/*nilemcikomutuyla*/
/*typedefbildirimiyle*/
/*numaralandrmatrolarak*/
Oysa C++'da bool doal bir trn ismidir ve bool bir anahtar szcktr. Bu trden
deerleri gsteren true ve false szckleri de C++'n anahtar szck kmesine
eklenmitir.
C++'da bool trden bir nesne true ve false deerlerini alabilir.
bool trden bir nesneye baka bir trden deer atanmas durumunda bu deerler atama
ncesinde otomatik olarak true ya da false deerlerine dntrlr. Yani dier doal
C++ Kitabi (11/330)
/*anesnesine10de?eriatand.
/*bnesnesine20de?eriatanmad!*/
//anesnesine10de?eriatand.
//bnesnesine20de?eriatand.
//Geersiz
return0;
}
Dng deikeninin bilinirlik alanna ilikin bu deiiklik tehlikeli bceklerin kayna
olabilir.
C++ Kitabi (14/330)
#include<cstdio>
inti=0;
intmain()
{
for(inti=0;i<100;++i)
printf("%d\n",i);
i=100;
//globalde?ikenolani'yeatamayaplyor.
//...
}
Yukarda programda
i=100;
atama deyimiyle, hangi i deikenine atama yaplmaktadr? Eski derleyicilere gre for
deyimi ayrac iinde tanmlanan i deikenine atama yaplmaktadr. nk dar bilinirlik
alannda olan ayn isimli deiken geni bilinirlik alanndaki ayn isimli deikeni maskeler!
Oysa standart ISO C++ dilinin kurallarna gre yalnzca global deiken olan i'ye
eriilebildii iin global deiken olan i'ye atama yaplr.
Dier denetim deyimlerinin ayralar iinde deiken tanmlanmas uygulamada bir fayda
getirir mi? Ana tema ayra iinde tanmlanan deikene bir ilev arsnn rettii geri
dn deerinin atanmas ve bu deikenin yalnzca denetim deyiminin gvdesinde
kullanlmasdr:
if(intx=get_value()){
//...
}
8. C++'da statik mrl deikenlere deimez ifadesi ile ilk deer vermek zorunlu
deildir.
C'de statik mrl deikenlere, yani global deikenler ve static anahtar szc ile
tanmlanm yerel deikenlere ilk deer verilmesi durumunda, ilk deer veren ifadenin
(initializer) deimez ifadesi olmas gerekir. Yani ilkdeer veren ifade iinde daha nce
tanmlanm bir deiken yer alamaz. Bunun nedeni C'de, statik mrl nesnelerin ama
kod iine ilk deerleriyle birlikte yazlmasdr. Bunun mmkn olabilmesi iin verilen
ilkdeerlerin derleme aamasnda belirlenmi olmas gerekir. Derleme aamasnda
saptanabilmesi iin ifadenin deimez ifadesi olmaldr. Oysa C++'da statik mrl
nesnelere her trden bir ifadeyle ilk deer verilebilir. Bu deikenlere ilk deer verilmemi
olsa da, bu deikenler 0 deeriyle ama kod iine yazlr. Programn alma zaman
srasnda ve main ilevinden nce ilk deerlerini alrlar.
9. C++ da iki karakterlik ayra atomlar tanmlanmtr. Derleyici ve nilemci program
bu kakarakter iftelerini grd yerde bunlara edeer karakterlerinin bulunduunu
varsayar:
<:
:>
<%
%>
%:
[
]
{
}
#
C++ Kitabi (15/330)
++x=10;
printf("x=%d\n",x);
return0;
}
Yukardaki programda yaplan
++x=10;
atamas C++'da geerli, C'de geerli deildir.
11. C++'da koul ileci ile oluturulmu bir ifade, koul ilecinin ikinci ve/veya nc
terimleri nesne ise, bir sol taraf deeridir. C'de koul ileciyle yazlan bir ifade hi bir
zaman sol taraf deeri deildir:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
intmain()
{
intx,y;
x=y=0;
srand((unsignedint)time(0));
(rand()%2?x:y)=1;
printf("x=%d\n",x);
printf("y=%d\n",y);
return0;
}
return0;
}
Yukardaki programda yazlan
(x,y)=30;
deyimi C++'da geerli iken C'de geersizdir.
13. C++'da bir typedef bildiriminin zde olarak yinelenmesi geerlidir. C'de bir typedef
bildiriminin ikinci kez yazlmas bir szdizim hatasdr.
typedefdoubledollar;
typedefdoubledollar;
Yukardaki bildirimler C++'da geerli, C'de geersizdir.
14. C++'da tr dntrme ilecinin ilevsel biimi vardr. ilecin byle kullanm C'de
geerli deildir.
voidfoo()
{
doubled=4.5;
intx;
x=int(d);
/***/
}
Yukardaki rnekte double trnden d deikeninin deeri atama ncesinde tr
dntrme ilecinin ilevsel biimi kullanlarak int trne dntrlyor.
Yukardaki kod C++'da geerli olmakla birlikte C'de geerli deildir.
15. C++'da geri dn deeri retmeyen bir ilevin return deyiminde return anahtar
szcn void trnden bir ifade izleyebilir. C'de byle ilevlerde return anahtar
szcn bir ifade izleyemez:
//sizeC++'daiba?lantyasahip.
//valueC++'dadba?lantyasahip.
C++ Kitabi (19/330)
REFERANSLAR
Referanslar yazlmsal baz amalara salamak iin kullanlan dzeyi ykseltilmi
gstericiler olarak dnlebilir.
cout<<"a="<<a<<endl;
r+=2;
cout<<"a="<<a<<endl;
r=r;
cout<<"a="<<a<<endl;
return0;
}
r+=2
deyimiyle a deikeninin deeri 2 artrlyor. Artk a'nn yeni deeri 12 olur.
r=r;
deyimiyle a deikeninin deeri -12 yaplyor.
Bir referansn ilevini iyi anlayabilmek iin, kodun gsterici deikenlerle oluturulmu
edeer karlklar gz nne alnabilir. rnein yukardaki program parasnn gsterici
deiken ile oluturulmu edeer karl aadaki gibidir:
#include<iostream>
usingnamespacestd;
intmain()
{
inta=10;
C++ Kitabi (22/330)
cout<<"a="<<a<<endl;
*ptr+=2;
cout<<"a="<<a<<endl;
*ptr=*ptr;
cout<<"a="<<a<<endl;
return0;
}
//Geersiz!
intmain()
{
intx=10;
inty=20;
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
swap(x,y);
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
return0;
}
Burada swap isimli ilev iki deikenin deerini takas eder. lev
swap(x,y);
biiminde arlyor. swap ilevinin parametre deikenleri olan referanslar x ve y
nesnelerinin yerine geer. phesiz, ileve aslnda x ve y deikenlerinin adresleri
aktarlmaktadr. lev iinde kullanlan a aslnda x in, b ise ynin yerine geer. Yukardaki
programn gsterici deikenlerle oluturulmu edeer C karl da yledir:
#include<iostream>
usingnamespacestd;
voidswap(int*a,int*b)
{
inttemp;
temp=*a;
*a=*b;
*b=temp;
}
intmain()
{
intx=10;
inty=20;
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
swap(&x,&y);
cout<<"x="<<x<<endl;
cout<<"y="<<y<<endl;
return0;
C++ Kitabi (26/330)
#include<iostream>
usingnamespacestd;
structPerson{
charname[30];
intno;
};
voiddisplay_person(constPerson&r)
{
cout<<r.name<<'\n'<<r.no<<endl;
}
intmain()
{
Personper={"NecatiErgin",123};
display_person(per);
return0;
}
*p=10;
++p;
*p=20;
std::cout<<a[0]<<""<<a[1];
return0;
}
imdi de aadaki kodu inceleyelim:
#include<iostream>
usingnamespacestd;
intmain()
{
inta[10]={1,2,3,4,5,6,7,8,9,10};
int&r=a[0];
r=10;
++r;
r=20;
cout<<a[0]<<""<<a[1];
return0;
}
Referanslar tek bir elemann adresine ilikindir. Bir referansa adres ilkdeer verilirken
yerletirilir. Daha sonra bu adres bilgisi deitirilemez. rnein:
inta=10;
intx=20;
int&r=a;
r=x;
ilemleri yapldnda, x deeri referansa deil, referansn yerine getii nesneye yani aya
aktarlr. Referanslar arka planda kendisi const olan gsterici deikenlere karlk gelir.
for(k=0;k<10;++k)
cout<<r[k]<<"";
cout<<endl;
}
intmain()
{
inta[10]={1,2,3,4,5,6,7,8,9,10};
display(a);
return0;
}
Referanslar daha ok, tek bir nesneyi adres yntemiyle ileve geirmek amacyla
kullanlabilir. rnein tek bir int deer ya da tek bir yap deikeni referans yoluyla ileve
geirilebilir. Ancak int trden bir dizi ya da bir yap dizisi bu yntemle ileve doal bir
biimde geirilemez.
Szdizimsel adan referanslarn gstericilere gre kullanm alanlar daha dardr. Gsterici
dizileri olur ama referans dizileri olamaz. Gstericileri gsteren gstericiler olabilir ama
referanslarn yerine geen referanslar olamaz.
Ancak phesiz bir gstericinin yerine geen bir referans olabilir. Aadaki kodu
inceleyin:
#include <iostream>
int main()
{
int x = 10;
int *ptr = &x;
int *&r = ptr;
*r = 20;
std::cout << "x = " << x << std::endl;
}
return 0;
//Geerli
Bu durumda nce const referansa balanan farkl trden nesnenin deeri, referansn
trnden yaratlacak geici bir nesneye atanr. Referans da bu geici nesneye balanr.
Yani derleyici aadaki gibi bir kod retir:
intmain()
{
doubled=10.5;
inttemp=(int)d;
constint&r=temp;
return0;
}
//Geersiz!
//Geerli
//x=10 y=20
// x=100 y=20
// x=100 y=200
return0;
}
foo ilevinin bildiriminde varsaylan argmanlar kullanlyor. main ilevi iinde yaplan ilk
arda foo ilevine hi bir argman gnderilmiyor. Bu durumda parametre deikenleri
olan x ve yye varsaylan deerler olan 10 ve 20 deerleri aktarlr. main ilevi iinde
yaplan ikinci arda, foo ilevine yalnzca 100 deeri gnderiliyor. Bu durumda birinci
parametre deikenine 100 deeri kopyalanrken, ikinci parametre deikenine varsaylan
deer olan 20 deeri kopyalanr. foo ilevine yaplan nc arda ise ileve 100 ve 200
deerleri gnderiliyor. Bu durumda, parametre deikenlerinden hibiri varsaylan bir
deer almaz. Grld gibi varsaylan deerler ilev arlrken ileve gnderilmeyen
deerlerdir.
Daha soldaki parametre deikenlerine ilev arsyla argman gnderilip, daha sadaki
dier argmanlar yazlmadan ilev arlrsa varsaylan deerler kullanlabilir. Ancak
bunun tersi geerli deildir. Aadaki ar biimi her durumda geersizdir:
foo(,10);
Bir parametre deikeni iin varsaylan bir deer belirlenmise, bu parametre
deikeninin daha sanda bulunan parametre deikenlerinin hepsi varsaylan deerler
almak zorundadr:
voidfunc(intx=10,inty);
voidfoo(intx,inty=20);
//Geersiz!
//Geerli
Varsaylan deer almam olan btn parametre deikenlerine ar ifadesi ile gereken
argmanlar gnderilmek zorundadr.
C++ Kitabi (38/330)
Bir ilevin parametre deikeni olan gsterici de varsaylan deer alabilir. Aadaki
rnei inceleyin:
#include<iostream>
voidput_message(constchar*p="Success!")
{
std::cout<<p;
}
intmain()
{
put_message("Failed!");
put_message();
return0;
}
Yukardaki programda put_message isimli ilevin gsterici olan parametre deikeni p iin
varsaylan argman olarak "Success!" dizgesi geiliyor. main ilevi iinde yaplan ilk
arda, ileve "Failed!" dizgesi argman olarak geilirken, ikinci arda ileve herhangi
bir argman gnderilmiyor. Yaplan ikinci aryla ekrana "Success!" yazs yazdrlr.
Varsaylan argman olarak belirlenen ifade, bir deimez ifadesi olmak zorunda deildir.
Deiken ieren ifadeler de varsaylan argman olarak kullanlabilir. Varsaylan argman
olarak kullanlan ifadelerde daha nce bildirimi yaplm global deikenler
kullanlabilecei gibi ilev arlar da yer alabilir.
intfunc1();
intfunc2(int);
intfunc3(double=3.14);
intg=10;
intfunc4(inta=func1(),intb=func2(g),intc=func3());
Yukarda yaplan tm ilev bildirimleri geerlidir. func4 ilevinin her parametresi de
varsaylan argman alyor. Birinci parametre olan a deikenine, ilev ar ifadesi ile bir
deer atanmaz ise, func1 ilevinin geri dn deeri atanr. kinci parametre deikeni
olan b'ye bir deer geilmez ise, func2 ilevinin geri dn deeri atanr. Bu arada
arlan func2 ilevine, global deiken olan g deikeninin deeri geilir. Son parametre
deikenine deer geilmedii zaman ise, bu parametre deikenine func3 ilevinin geri
dn deeri atanr ki, arlacak func3 ilevi de kendisine argman gnderilmedii iin
varsaylan deer olarak belirlenen 3.14 deerini alr.
Varsaylan argmanlara ilikin ifadelerin deerlendirilmesi ilevin arld noktada
gerekleir. Yani func4 ilevi eer arlmaz ise func1, func2 ve func3 ilevleri de
arlmaz.
Varsaylan argman olarak belirlenen deer yalnzca bir kez yazlmaldr. Ayn kaynak
dosyada varsaylan argmann ikinci bir bildirimde yeniden yazlmas geersizdir.
Aadaki rnei inceleyin:
///////file1.h
intfunc(inta,intb,intc=0);
///////file2.h
#includefile1.h"
intfunc(inta,intb,intc=0);
//Geersiz
//Geersiz.
//
Geersiz!
Derleyici burada *= karakterlerini tek bir atom olarak ele alp ilemli atama ileci olarak
deerlendirir (En uzun atom kural - maximum munch). Bildirim geersizdir. Bu durumda
varsaylan argmana ilikin bu iki karakter bitiik yazlmamaldr:
voidfunc(char*="Ahmet");
Gsterici parametre deikenleri varsaylan argman alabildii gibi, referans parametre
deikenleri de varsaylan deerler alabilir:
#include<iostream>
intg=20;
voidfunc(int&r=g);
intmain()
{
inty=30;
func();
func(y);
return0;
}
C++ Kitabi (40/330)
voidfunc(int&r)
{
std::cout<<r<<std::endl;
}
itoa ilevi ounlukla bir tamsayy onluk say sistemine gre oluturulmu bir yazya
dntrmek iin kullanldndan, ilevin nc parametresine ounlukla 10 deeri
geilir. imdi ayn ii yapan ancak varsaylan argman alan itoa_d isimli bir ilev yazalm:
#include<iostream>
#include<cstdlib>
char*itoa_d(intn,char*str,intbase=10)
{
returnitoa(n,str,base);
}
intmain()
{
chars[100];
itoa_d(123,s);
std::cout<<s;
return0;
}
Yazlan itoa_d isimli ilevin base isimli parametre deikeninin varsaylan argman alarak
10 deeri aldn gryorsunuz. lev iki argmanla arlrsa nc parametre olan
base isimli parametre deikenine 10 deeri geilir. levin kendi iinde itoa ilevini
ardn gryorsunuz. itoa ilevi bu durumda varsaylan argman alan itoa_d isimli
ilevi tarafndan sarmalanmtr.
Varsaylan argmanlar "Hibir deer almayacana bari u deeri alsn" fikriyle
kullanlmamaldr. Bylesi kullanmlar kaynak kodu inceleyen kiiyi yanltr.
Bazen parametre deikenine verilen varsaylan deerin zel bir anlam olmaz. Bu
varsaylan deer yalnzca ilevin varsaylan argmanla arlp arlmadn saptamak
C++ Kitabi (41/330)
LEV YKLEMES
C++ dilinde, bir kaynak dosyada ayn isimli birden fazla ilev tanmlanabilir. Nesne
ynelimli programlama tekniinin uygulanmasn kolaylatran bu ara ngilizce'de
"function overloading" (ilev yklemesi) olarak isimlendirilir. u sorulara yant aramakla
balayalm:
Neden iki ya da daha fazla sayda ilevin isimleri ayn olsun? ki ayr ileve ayn isim
vermenin nasl bir faydas olabilir? Bir rnekle balayalm:
//Geersiz!
intmain()
{
foo();
return0;
}
Yukardaki her iki bildirim de geerli olsayd, hangi foo ilevinin arld nasl anlalrd?
C++ Kitabi (44/330)
levlerin parametre deikenlerinin says ile, her bir parametre deikeninin trn
kapsayan bilgiye ilevin imzas denir. levin geri dn deerinin tr bilgisi, ilevin
imzasnn bir paras deildir.
Ayn bilinirlik alannda ayn isimli iki ya da daha fazla sayda ileve ilikin bildirim varsa,
aadaki durumdan biri sz konusu olur:
i. Hem ilevlerin imzalar tamamen ayn hem de ilevlerin geri dn deerlerinin trleri
ayn ise, derleyici bu durumu, ayn ilevin bildiriminin birden fazla kez yapld biiminde
yorumlar. C'de olduu gibi C++'ta da, bir ilevin bildirimi zde olmas kouluyla birden
fazla yaplabilir:
//ilevbildirimiikikezyaplyor(functionredeclaration)
intfunc(int);
intfunc(int);
ii. Ayn isimli ilevlerin parametik yaplar birbirinden farkl ise, yani ilevlerin imzalar
farkl ise, derleyici bu durumu, ayn isimli fakat farkl ilevlerin bulunduu (function
overloading) biiminde yorumlar. Bu durumda ilevlerin geri dn trlerinin bir nemi
yoktur:
//functionoverloading(?levyklenmesi)
intfunc(int);
intfunc(int,int);
Ayn isimli iki ilevin parametrik yaps, yalnzca ilevin birinde varsaylan argman
kullanlmas biiminde farkllk gsteriyorsa, bu durum yine ilev bildiriminin yinelenmesi
olarak ele alnr:
//ilevbildirimiyineleniyor(functionredeclaration)
intmax(int*ptr,intsize);
intmax(int*,int=10);
iii. levlerin parametrik yaplar tamamen ayn, fakat ilevlerin bildirilen geri dn
deerlerinin trleri farkl ise, bu durumda derleyici, ilev bildiriminin zde olmayan
biimde yinelendii sonucunu kartr. Bu durumu bir szdizim hatas olarak belirler.
//ilevbildirimielikilibiimdeyineleniyor
intfunc(int)
doublefunc(int);
//Geersiz!
typedef bildirimi ile yeni bir tr yaratlm olmaz. typedef bildirimiyle var olan bir tre yeni
bir isim verilebilir. Eer ayn isimli iki ilevde, parametre deikenlerinin trlerinin
yazlmasnda birinde typedef ismi kullanlrken, dierinde trn gerek ismi kullanlm ise
bu durum "ilev yklemesi" olarak kabul edilmez. Aadaki rnei inceleyin:
typedefunsignedcharBYTE;
BYTEfoo(BYTE);
intfoo(unsignedchar);
Yukardaki iki bildirimin bir arada bulunmas geersizdir. Bildirilen her iki ilevin de imzas
ayn, geri dn deerlerinin trleri farkldr.
Peki C++'da, parametrik yaplar birbirlerinden farkl, ayn isimli ilevler
tanmlanabildiine gre, derleyici ayn isimli ilevlerden hangisinin arldn nasl anlar?
C++ Kitabi (45/330)
//1
//2
//3
//4
voidfunc()
{
foo(5);
}
func ilevi iinde foo isimli ileve yaplan arnn, ilevlerden hangisine ait olduunun
saptanmas aamalarn inceleyelim:
1. aama
levin arld noktada, tm ilev bildirimleri grlebilir (visible) olduundan bu
aamada, drt ilev de aday olarak belirlenir, parametrik yaplar hakknda bilgi edinilir.
2. aama
1 numaral ilevin parametre deikeni says (0) ile, ilev arsndaki argman says (1)
birbirine eit olmadndan, bu ilev uygun (viable function) olarak ele alnmaz.
2 numaral ilev uygundur. Parametre deikeni ile argman says uyumludur,
argmandan parametre deikenine geerli bir dnm sz konusudur.
3 numaral ilev uygundur. levin iki parametre deikeni vardr. Ancak ikinci parametre
deikeni varsaylan argman ald iin, ilev tek bir argman ile arlabilir. Argman
olan ifade int trden, bu argmann kopyalanaca parametre deikeni double trdendir.
Ancak atama ncesinde int tr, geerli olarak double trne dntrlebilir.
//ykseltme
iii. Bir numaralandrma trnden, o numaralandrma trne baz olan (underlying type)
tre yaplan dnm de, ykseltme olarak deerlendirilir. Aadaki rnei inceleyin:
enumPOS{OFF,ON,HOLD,STAND_BY};
intfunc(int);
intmain()
{
POSposition=OFF;
func(position);
func(STAND_BY);
//...
}
//Numaralandrmatrndeninttrneykseltme)
//Numaralandrmatrndeninttrneykseltme)
3. Standart dnmler
Standart dnm (standard conversions) bal altnda toplanan, 5 grup dnm sz
konusudur:
i. Tamsay trlerine ilikin dnmler
Bir tamsay trnden ya da bir numaralandrma trnden, baka bir tamsay trne
yaplan dnmler.
C++ Kitabi (48/330)
//standartdnm(inttrdenlongtrne)
//standartdnm(chartrdenlongtrne)
//standartdnm(unsignedinttrndenint)
//standartdnm(doubletrdenfloattrne)
//standartdnm(0de?erininbirgstericiyeatanmas
//standartdnm(int*trdenvoid*trne)
}
4. Programcnn tanmlad dnmler.
Bu durumu snflara giri yaptktan sonra ele alacaz. Ancak imdilik u kadarn
syleyebiliriz: Bu dnmler, bir snf trnden nesnenin baka bir snf trne ya da
doal bir veri trne dntrlmesine ilikindir. Derleyicinin bu tr dnmleri
gerekletirebilmesi iin, programcnn zel dnm ilevleri tanmlam olmas gerekir.
simleri "Dntrme kurucu ilevleri" ya da "tr dntrme ilevleri" olan bu ilevleri
ilerde ayrntl bir ekilde inceleyeceiz.
Hangi ilevin arlm olduunun saptanmas konusunda rnekler verelim:
//1
//2
//3
//4
//5
//6
//7
voidfunc()
{
foo(10);
foo(3.4F);
foo((double*)0x1FC0);
foo(6U);
}
func ilevi iinde yaplan ilev arlarn teker teker ele alalm:
foo(10);
ars iin
1, 2, 3, 4, 5, 6, 7 numaral ilevler adaydr.
1, 2, 3, 4 numaral ilevler uygundur.
Tam uyum salad iin, 1 numaral ilev en uygun olandr.
foo(3.4F)
ars iin
1, 2, 3, 4, 5, 6, 7 numaral ilevler adaydr.
1, 2, 3, 4 numaral ilevler uygundur.
Ykseltme durumu olarak deerlendirildiinden, 2 numaral ilev en uygun olandr.
foo((double*)0x1FC0)
ars iin
1, 2, 3, 4, 5, 6, 7 numaral ilevler aday ilevlerdir.
Uygun ilev yoktur (no match).
lev ars geersizdir.
foo(6U)
ars iin
1, 2, 3, 4, 5, 6, 7 numaral ilevler adaydr.
1, 2, 3, 4 numaral ilevler uygundur.
1, 2, 3 ve 4 numaral ilevler iin standart dnm uygulanabilir. ift anlamllk hatas
(ambiguity) sz konusudur. lev ars geersizdir.
const Yklemesi
Bir ilevin parametre deikeni gsterdii yer const olan bir gsterici iken, ayn isimli bir
baka ilevin parametre deikeni, gsterdii yer const olmayan bir gsterici olabilir:
voidfoo(constint*);
voidfoo(int*);
Bu durum da ilev yklemesi olarak ele alnr. Yani iki ayr ilev sz konusudur. foo
ilevine int trden bir nesne ile ar yapldnda, hangi ilev arlm olur?
cout<<"voidfoo(int)"<<endl;
}
intmain()
{
inta=20;
//foo(a);iftanlamllkhatas
foo(5);//voidfoo(int);
return0;
}
Yukardaki programda, ilk tanmlanan foo ilevinin parametre deikeni int trden bir
referans iken, ikinci foo ilevinin parametre deikeni int trden bir nesnedir. Bunlar farkl
ilevdir. main ilevi iinde, yorum satr iine alnm birinci ilev ars ift anlamllk
hatasna neden olur. Yani bu durumda, deerle arma(call by value) ya da adresle
arma (call by reference) birbirine gre ncelikli deildir. Ancak ikinci ilev arsnda,
argman olan ifade bir deimezdir. Bir referansa bir deimez ifadesi ile ilk deer
verilemeyeceine gre, arlabilecek tek bir ilev vardr. unu da ekleyelim: Birinci
ilevin parametre deikeni const referans olsayd, ikinci ilev ars da ift anlamllk
hatas durumuna derdi.
Hangi ilevin arlm olduunu saptamak, derleme zamannda yaplan bir ilemdir. Yani
kaynak kod derlenip hedef dosya haline getirildiinde, artk hangi ilevin arlm olduu
bilinir. nk arlan ilevin kimlii bir ekilde hedef koda yazlm olur. Baka bir deyile
"ilev yklemesi" aracnn, programn alma zaman asndan bakldnda bir ek
maliyeti sz konusu deildir. Ancak byle bir ara derleyici zerindeki yk de artrr.
Derleyici programn boyutunun bymesine neden olur. Kk bir dil olarak tasarlanan C
dilinde, bu aracn bulunmamasnn nemli bir nedeni de budur.
Ayn isimli ilevler gereksiz yere tanmlanmamaldr. lev yklemesinin ana amac,
alacak kodlar gerekte birbirinden farkl olan ilemleri, ayn isim altnda soyutlamaktr.
Farkl iler gren ilevlerin, ayn ismi tamas okunabilirlii bozar. Birden fazla ilevin ayn
ismi tamas, kullanc kodlarn iini kolaylatrmaya yneliktir.
SINIFLAR
Snflar, nesne ynelimli programlama tekniinin temel yap tadr. Nesne ynelimli
programlama tekniine "snflar kullanarak program yazma" teknii diyebiliriz. Bu
blmde snflara bir giri yapacak, snflara ilikin temel kavramlar aklayacaz. Bundan
sonraki blmlerde ise arlkl olarak snflarn kullanlmas zerinde duracaz.
Snf Nedir
Snflar ncelikle szdizim asndan ele alacaz. Snf C++ dilinin, programcnn yeni bir
tr yaratmasna olanak veren bir aracdr. C'de, programcnn yeni bir tr yaratmas, yap
(struct), birlik (union) ve enum aralaryla mmkn oluyordu. C++ dilinde bu aralara bir
de snf (class) eklenmitir.
Snf (class) nesne ynelimli programlama tekniinin uygulanmasna olanak salayan, C
dilinde olmayan yeni bir yazlmsal birimdir. Snflar, Cdeki yaplara benzetilebilir. Ancak
Cdeki yaplar yalnzca eleman (member) ierirken, C++da snflar yaplardan fazla olarak
hem veri eleman hem de ye ilevleri (member function) ierir. Snflar, yaplara gre ek
bir ok zellie sahiptir. Bu zelliklerin ou Nesne Ynelimli Programlama Tekniini
destekleme amacyla eklenmitir.
Nasl bir "yap" tr programc tarafndan tanmlanm bir tr (user defined type) ise,
snflar da programcnn tanmlam olduu trdr. Programc, nce yeni bir tr
derleyiciye tantr, daha sonra bu yeni trden nesne, gsterici, referans tanmlayabilir.
Snflar kullanabilmek iin ilk yaplmas gereken ilem, bir snfn tanmn yapmaktr. Bir
snfn tanmn yapmak, bu snf hakknda derleyiciye bilgi vermek anlamna gelir.
Derleyici ald bilginin sonucunda, bu snf trnden bir nesne tanmlanmas durumunda,
hem bellekte ne kadar yer ayracan bilir, hem de programcnn yazm olduu koda
ilikin baz kontrol ilemlerini yapma olanana kavuur.
Snflar yaplara gre temel olarak iki nemli farklla sahiptir:
i. Snf bildirimi iinde snflarn elemanlar, ismine public, protected ya da private denilen
ayr blgede yer alabilir.
ii. Snflar yalnzca veri elemanlar deil ilevler de ierir. Bu ilevlere snfn ye ilevleri
(member functions) denir.
nce bu iki yeni zellik stnde ayrntl bir biimde duracaz:
Snf Tanm
Bir snfn tanm, yani derleyiciye tantlmas zel bir szdizim ile olur:
Snf bildiriminin genel biimi yledir:
class[snf_ismi]{
[private:]
//
[protected:]
//
[public:]
//
};
class bir anahtar szcktr. Trke "snf" anlamna gelir. Genel biimdeki snf_ ismi(class
tag), bildirimi yaplan snfn ismidir. Snf ismi, isimlendirme kurallarna uygun herhangi
bir isim olabilir.
C++ Kitabi (53/330)
classA{
longb;
inta;
public:
doublefunc3();
protected:
intfunc2(int);
public:
doublec;
private:
voidfunc1();
};
Yukardaki rnekte, A isimli snfn a ve b elemanlar snfn private blmnde bildiriliyor.
imdi de aadaki snf bildirimini inceleyin:
classDate{
intday,mon,year;
boolverify_date();
public:
voidset_date(int,int,int);
voiddisplay_date();
};
Yukarda, ismi Date olan bir snf tanmlanyor. Yukardaki tanmla, Date isimli yeni bir veri
tr yaratlm olur. C ile C++n bu noktadaki farkn da hatrlayn: C++da bu veri
trnn ismi hem class Date hem de Date'dir. Yani bir typedef bildirimi yaplmakszn
Date ismi bu trn ismi olarak kullanlabilir. Oysa C dilinde yaplar sz konusu olduunda,
bir yap ismini (structure tag) bir tr ismi olarak kullanmak iin bir typedef bildirimi
yapmak gerekir.
Bir snf ismi (class tag), isimlendirme kurallarna uygun olmak kouluyla, istenildii gibi
seilebilir. Ancak programclarn ou, yalnzca ilk harfi byk dier harfleri kk olan
isimleri seerler.
Date isimli snfn tanmn incelemeyi srdrelim:
int trden day, mon, year isimli elemanlar snfn private blmnde bildirilmi. Bir eriim
belirteci kullanlmad zaman snfn private blmnn anlald sylenmiti. Baka bir
deyile, int, mon ve year, Date snfnn private elemanlardr. Date snfnn bildirimi
iinde, verify_date, set_date ve display_date isimli ilevin bildiriminin yapldn
gryoruz. Bu ilevler, Date snfnn ye ilevidir. verify_date isimli ilevin bildirimi snfn
private blmnde yaplm iken, set_date ve display_date ilevlerinin bildirimleri Date
snfnn public blmnde yaplm. yle de sylenebilirdi: set_date ve display_date,
Date snfnn public ye ilevleridir. verify_date Date snfnn private ye ilevidir. Peki,
snfn ye ilevleri ile bizim daha nceden bildiimiz ilevler arasnda bir fark var m?
Snflarn ye ilevleri ne ie yaryor, nasl tanmlanyor? Btn bu konular ayrntl bir
ekilde ele alacaz.
Mantksal olarak bir snf ile ilikilendirilmi ilevlere ye ilevler (member functions)
denir. Her ye ilev, snfn elemanlarna dorudan eriebilir. Snfn elemanlar, ye
ilevler tarafndan ortaklaa kullanlan deikenlerdir. Snfn ye ilevleri, bir konuya
ilikin eitli alt ilemleri yapar. Bu ilemleri yaparken de snfn elemanlarn ortaklaa
olarak kullanrlar. Bir ii gerekletiren bir dizi ilevin snf ad altnda ele alnmas,
alglamay ve tasarm kolaylatrr, derleyicinin baz denetimleri yapmasna olanak verir.
Yaplarla olduu gibi, snflarla da almak iin nce snf tanmn yapmak, sonra da bu
snf trnden nesneler tanmlamak gerekir.
C++ Kitabi (55/330)
ye levlerin Tanmlanmas
Bir snfn ye ilevi, belirlenmi bir szdizim ile snfn dnda tanmlanr:
[geridnde?erinintr]<snf_ismi>::<ilev_ismi>([parametre
de?ikenleri])
{
//
}
Szdizimi inceleyelim:
nce ilevin geri dn deerinin trnn yazldn gryorsunuz. Sonra snfn ismi
yazlyor. Daha sonra gelen :: atomunu, ilevin ismi izliyor. ki tane "iki nokta st ste"nin
yan yana getirilmesiyle oluturulan :: atomuna, bundan sonra "znrlk atomu"
diyeceiz. Bu atom bir ile olarak kullanldnda "znrlk ileci" (scope resolution
operator) ismini alr.
biiminde ama kod iine yazlr. Derleyiciler, ayn isimli global ilevlerle ye ilevleri ar
biimlerine bakarak ayrabilir. rnein:
a.func();
gibi bir ar, snf nesnesi ile yapldna gre, derleyici a hangi snf trnden bir nesne
ise func isimli ilevi de o snfn bir ye ilevi olacak biimde arayacaktr.. Oysa ar
func();
biiminde yaplrsa, derleyici global olan func ilevinin arldn anlard.
C++da ilevlerin ama kod iine yazlmalarnda herhangi bir standart sz konusu deildir.
Her derleyici ye ilevlerin isimlerini snf ismiyle ve parametre trleriyle kombine ederek
ama kod iine yazar. Ancak bu yazma ilemine ilikin kesin bir notasyon(yazm ekli)
standart olarak belirlenmemitir. Bu nedenle farkl C++ derleyicileriyle derlenmi
modllerin birletirilmesinde sorunlar kabilir.
Peki, bir snf nesnesinin elemanlar bellee ne ekilde yerletirilir?
Bir snf nesnesi tanmlandnda derleyici yalnzca snfn elemanlar iin bellekte yer
ayrr. Snfn ye ilevleri snf nesnesi iinde herhangi bir yer kaplamaz. ye ilevler
yalnzca mantksal bakmdan snf ile ilikilendirilmitir. Snfn iki blm belirten anahtar
szc arasna yazlan veri elemanlar, ilk yazlan dk adreste olacak biimde bitiik
yerletirilir. Blmlerin birbirlerine gre yerleim biimleri standart olarak
belirlenmemitir, derleyiciden derleyiciye deiebilir. Aadaki rnei inceleyin.
classA{
private:
inta;
intb;
public:
intc;
intd;
protected:
inte;
intf;
private:
intg;
inth;
};
Burada A snf trnden bir snf nesnesinin bellekte kaplad alan 8 * sizeof(int) kadar
olur. Nesne iinde, a ile b, c ile d, e ile f ve g ile h veri elemanlar bitiik olarak yerletirilir.
Ancak bu gruplarn kendi aralarndaki yerleimleri derleyiciden derleyiciye deiebilir.
Blmler arasndaki yerleim standart bir biimde belirlenmemi olsa da derleyicilerin
hemen hepsi daha yukar yazlan blmleri dk adrese yerletirir. Yani yukardaki
rnekte derleyicilerin byk blm veri elemanlarn srasyla dk adresten balayarak
yukardan aaya doru yerletirir. Blmler aras yerleim standart olmadna gre,
nesnenin elemanlarnn yerleimine ilikin yazlan kodlarda tanabilirlik sorunlar ortaya
kabilir.
Nesnenin bellekte kaplad alan, derleyicinin hizalama (alignment) ilemleri etkin hale
getirilmise veri elemanlarnn toplam uzunluundan fazla olabilir.
//Geersiz!
if(!date.verify_date()){
//Geersiz!
cout<<"Geersiztarih"<<endl;
exit(EXIT_FAILURE);
}
date.set_date(10,10,1997);
date.display_date();
//Geerli!
//Geerli!
return0;
}
Bir snf nesnesi referans ya da gstericisi yoluyla snfn yalnzca public blmndeki veri
elemanlar ile ilevlerine dorudan eriilebilir: Yukardaki rnekte date.day veri eriimi ile
date.verify_date() ye ilevinin ars bu nedenle geerli deildir. Fakat snfn public
blmnde bulunan set_date ve display_date ye ilevlerine yaplan arlar geerlidir.
Bu ilev arlar derleme zamannda herhangi bir soruna yol amaz. Snflardaki eriim
kuralna uyulmamasndan dolay oluan hatalara ilikin iletiler Microsoft derleyicilerinde,
xxxcannotaccessprivatememberdeclaredinClassYYY
Borland derleyicilerinde ise,
YYY::xxxnotaccessible
biimindedir.
Bir ye ilevin baka bir ye ilevi armas durumunda, arlan ye ilev aran ye
ilev ile ayn elemanlar kullanr. rnein A snfnn set ye ilevi display ye ilevini
dorudan arm olsun:
Burada p, yerel bir gsterici deiken olduundan iinde bir p deer vardr. Bu durumda
func ilevine this adresi olarak bir p deer geilir. Yani func ilevi rastgele bir bellek
blgesini snf nesnesi gibi kullanr. Byle bir durum bir gsterici hatasna yol aar.
imdi yle bir soru soralm: Madem bir snfn ye ilevi iinde snf nesnesinin
elemanlarna dorudan eriiliyor, o zaman neden this diye bir anahtar szck var? ye
ilev iinde snf nesnesinin adresinin elde edilebilmesinin ne gibi faydalar olabilir? this
adresi bir ye ilev iinde aadaki amalar iin kullanlabilir:
1. ye ilev hangi snf nesnesi iin arlmsa, o snf nesnesinin adresi, ye ilev tanm
iinde bir baka ileve argman olarak gnderilebilir:
classA{
public:
//...
voida_func();
private:
//...
};
voidg_func(A*);
voidA::a_func()
{
//...
g_func(this);
}
intmain()
{
Aa;
a.a_func();
return0;
}
Yukardaki programda, A snfnn a_func isimli ye ilevi iinde arlan g_func isimli
global ileve, this adresi argman olarak gnderiliyor. main ilevi iinde tanmlanan A
snf trnden a nesnesi ile a_func ye ilevi arldnda, a nesnesinin adresi gizlice
a_func ilevine geirilir. Bu ilev iinden de, bu adres global g_func ilevine gnderilir.
2. ye ilev iinde, ye ilevi aran snf nesnesinin kendisine eriilebilir. Madem ki this
adresi ye ilevi aran snf nesnesinin adresidir, bu durumda
*this
ifadesi bu nesnenin kendisidir, deil mi? Aadaki kodu inceleyin:
a_func1 ye ilevi hangi snf nesnesi iin arlmsa, o snf nesnesinin deerini geri
dndrr.
a_func2 ye ilevi hangi snf nesnesi iin arlmsa, o snf nesnesinin adresini geri
dndrr.
a_func3 ye ilevi hangi snf nesnesi iin arlmsa, o snf nesnesinin kendisini geri
dndrr.
4. this gstericisi, snf elemanlarnn yerel deikenler tarafndan maskelenmesi
durumunda, snf elemanlarna erimek amacyla da kullanlr. Bu durum "snf bilinirlik
alan" bal altnda incelenecek.
biimindedir. imdi de ayn isimli deikenlerin durumuna bir gz atalm: Ayn isimli
deikenler konusunda u kural anmsatalm: Cde(C dilinde) olduu gibi C++da ayn
bilinirlik alanna ilikin ayn isimli birden fazla deiken tanmlanamaz. Fakat farkl
bilinirlik alanna ilikin ayn isimli birden fazla deiken tanmlanabilir. Bir blok iinde ayn
isimli birden fazla deiken etkinlik gsteriyorsa, o blok iinde dar bilinirlik alanna sahip
olana eriilebilir.
Aadaki rnei inceleyin:
//ilevbildirimi
inta=50;
//globalde?iken
classX{
public:
voidfoo(int);
voidfunc();
private:
inta;
};
usingnamespacestd;
voidX::func()
{
cout<<"Xsnfnnfuncisimliyeilevi"<<endl;
}
voidfunc()
{
cout<<"Globalfuncisimliilev"<<endl;
}
Bu rnekte hem a isimli global bir deiken tanmlanyor, hem de X snfnn a isimli bir
eleman var. Snfn foo ye ilevi de yle tanmlanm olsun:
voidX::foo(inta)
{
cout<<a<<endl;
//Parametrede?ikeniolana
{
inta=30;
cout<<a<<endl;
//Blokiindekia
}
func();
//yeilevolanfunc
}
imdi drt tane a sz konusu. Global olan a, snfn eleman olan a, parametre deikeni
olan a ve i blokta tanmlanm olan a. Dar bilinirlik alanna sahip olana erime kuralna
gre, i blokta kullanlan a o blokta tanmlanan a deikenidir. D bloktaki ise ilevin
parametre deikenidir. arlan ilev ye ilev olan func isimli ilevdir. nk ilev
isimleri de deiken gibi ele alnr, ayn bilinirlik alan kuralnda uyar. Global ilevler dosya
bilinirlik alanna, ye ilevler ise snf bilinirlik alanna sahiptir. imdi foo ilevinin
parametre deikeninin ismini deitirelim:
voidX::foo(intx)
{
a=x;
{
inta=30;
cout<<a<<endl;
}
func();
}
//Snfnelemanolana
//Blokiindekia
//yeilevolanfunc
znrlk leci
znrlk ileci iki : karakterinin :: biiminde yan yana getirilmesiyle elde edilir.
znrlk ilecinin tek terimli nek (unary prefix) ve iki terimli araek biiminde (binary
infix) iki kullanm vardr. nce tek terimli nek biim zerinde duracaz:
//Globala
//Yerela
//Globala
//Yerela
//Globala
return0;
}
znrlk ileci, ayn isimli hem yerel hem de global bir deikenin tanml olduu
durumda global olana erimek amacyla kullanlabilir. Yukardaki rnekte blok ilerinde a
deikeninin :: ileci ile kullanlmasyla global olan a deikenine eriilir.
Burada bir de uyar yapalm: :: ileci bir st blokta tanml olana eriimi deil, her zaman
global olana eriimi salar. Bir yukardaki bloa erimenin ciddi bir faydas yoktur. Oysa
global deikene eriim pek ok durumda gerekebilir.
Tek terimli nek kullanm sklkla bir snfn ye ilevleri iinde snfn elemanlar ile ayn
isimli global deikenlerin bulunmas durumunda, global olana eriimi salamak iin
kullanlr. Aadaki rnei inceleyelim:
//Globalilev
classX{
public:
voidfunc();
voidfoo();
};
usingnamespacestd;
voidfunc()
{
cout<<"Globalfuncisimliilev"<<endl;
}
voidX::func()
{
cout<<"Xsnfnnfuncisimliilevi"<<endl;
}
voidX::foo()
{
cout<<"Xsnfnnfooisimliilevi"<<endl;
func();
//Xsnfnnyeileviolana?rlyor
::func();
//Globalolana?rlyor
}
foo ye ilevinde,
func()
Biiminde normal olarak arlan dar bilinirlik alan kuralna gre X snfna ilikin olan
func ilevidir. Oysa
::func();
biiminde arlm olan global func ilevidir.
Baz programclar global olanla ayn isimli bir ye ilev olmasa bile, ye ilevler iinde
global ilevleri okunabilirlii artrmak iin :: ileciyle arrlar. rnein,
CMyDialog::CMyDialog()
{
hProcess=::GetProcessHeap();
}
GetProcessHeap ilevin CMyDialog snfnn ye ilevi olmadn dnelim. Bu durumda
ilevin :: ileci ile arlmasna gerek yoktur, deil mi? nk bilinirlik alanlarnn
akmas sz konusu olmad iin nasl olsa GetProcessHeap dendiinde global olan
anlalr. Ancak programc kodu inceleyen kiiye yardmc olmak iin durumu vurgulamak
istemi olabilir. Snflarn youn olarak kullanld ktphanelerde bu tr vurgulamalarla
olduka sk karlaabilirsiniz.
Date::year=year;
}
Parametre deikenlerinin isimleriyle snfn elemanlarnn isimlerinin ayn olduuna dikkat
edin. Bu durumda ye ilev iinde snfn elemanlarna erimek iin iki terimli znrlk
ileci kullanlyor. Kullanm biimini inceleyin:
Date::month=month;
Atama ilecinin sol tarafnda kullanlan month ismi snfn elemanna ilikin iken, atama
ilecinin sa tarafnda bulunan month ismi parametre deikenine ilikindir.
Grld gibi znrlk ilecinin iki terimli ara ek biimi, snfn elemanlar ile ayn
isimli yerel deikenler ya da parametre deikenlerinin olmas durumunda snfn
elemanlarna eriilmesi amacyla kullanlr. Bunun dnda znrlk ilecinin snflarn
tretilmesi ilemlerinde de benzer amalarla kullanldn greceksiniz.
Date();
biiminde bildirilen ilev snfn kurucu ilevidir. Bu ilevin isminin snf ismiyle ayn
olduuna dikkat edin. Bildirimde geri dn deeri yerine hibir ey yazlmyor. Bu durum
C++ Kitabi (71/330)
//Kurucuilev
//Geersiz
a, X trnden bir snf nesnesi ise bu biimde ilkdeer(ilk deer) verilemez. Aslnda
istisna olarak zel baz snflara kme ayralar ile ilk deer verilebilir. Ancak bu konuyu
ileride ele alacaz.
Aadaki snf bildirimini inceleyin, izleyen rnei yazarak altrn:
#include<iostream>
#include<ctime>
classDate{
public:
Date();
Date(int,int,int);
voiddisplay();
private:
intday,month,year;
};
usingnamespacestd;
Date::Date()
{
time_ttimer=time(0);
tm*tptr=localtime(&timer);
day=tptr>tm_mday;
month=tptr>tm_mon+1;
year=tptr>tm_year+1900;
}
Date::Date(intd,intm,inty)
{
day=d;
month=m;
year=y;
}
voidDate::display()
{
cout<<day<<'/'<<month<<'/'<<year;
}
intmain()
{
C++ Kitabi (74/330)
return 0;
rneimizde g snf nesnesine ilikin kurucu ilev main ilevinden nce arlr. Ekranda
unlar grmeniz gerekir:
Xsnfnnkurucuilevi
mainilevibalad
20
Eer birden fazla global snf nesnesi tanmlanmsa kurucu ilevlerin arlma sras
yukardan aaya dorudur. Yani daha yukarda tanmlanm snf nesnesinin kurucu
ilevi daha nce arlr.
Kurucu ilevler de varsaylan argmana sahip olabilir. Aada rnekte verilen Complex
snfn inceleyin:
classComplex{
doublereal,imag;
public:
Complex(double,double=0.);
voiddisplay();
};
Complex::Complex(doubler,doublei)
C++ Kitabi (76/330)
voidComplex::display()
{
cout<<real;
if(imag!=0)
cout<<"+"<<imag;
cout<<'\n';
}
intmain()
{
Complexc1(10);
c1.display();
//Complexx(10,0)
Complexc2(10,20);
c2.display();
return0;
}
Bir kurucu ilevin tm parametre deikenleri varsaylan deer alyorsa o kurucu ilev
varsaylan kurucu ilev olarak da kullanlabilir. rnein, Complex snfnn kurucu ilevi
aadaki gibi bildirilmi olsun:
classComplex{
public:
Complex(double=0,double=0);
//
};
Aadaki tanmlamalarn hepsi geerlidir:
Complexx;
Complexy(10);
Complexz(10,20);
// Complex(0,0)ileaynanlamda
// Complex(10,0)ileaynanlamda
Bir snf iin hi bir kurucu ilev yazlmayabilir. Bu durumda derleyici varsaylan kurucu
ilevi kendisi yazar. Yani bir snf iin hibir kurucu ilev yazlmasa da, varsaylan kurucu
ilev varm gibi nesne tanmlanabilir. rnein:
classPoint{
public:
voidset(int,int);
voiddisplay();
private:
intx,y;
};
Grld gibi, Point snfnn hi kurucu ilevi yok. Bu durumda varsaylan kurucu ilev
varm gibi nesne tanmlanmas, herhangi bir hataya yol amaz.
//Geerli
Peki derleyicinin yazd varsaylan kurucu ilev tam olarak ne i yapar? Bu konuyu
ileride ele alacaz. imdilik yle dnebiliriz: Bir kurucu ilevin yapmas gereken baz
isel ilemler sz konusudur. Bu ilemlerin her durumda yaplmas gerekir. Derleyici bu
isel ilemler iin bir kod yazar. Derleyicinin yazd bu kod, programcnn yazd her
kurucu ilevin en bana eklenir. Kurucu ilev programc tarafndan tanmlanmaz ise,
derleyicinin yazd varsaylan kurucu ilev, yalnzca derleyicinin yazd kod parasndan
oluur.
Snfn en az bir kurucu ilevi varsa, ama varsaylan kurucu ilevi yoksa bu durumda
varsaylan kurucu ilev arlacak biimde nesne tanmlanmas geersizdir. nk bu
durumda artk derleyici otomatik olarak varsaylan kurucu ilevi yazmaz. rnein:
classPoint{
public:
Point(int,int);
//...
private:
intx,y;
};
gibi bir snf bildiriminden sonra,
Pointpt;
//Geersiz!
voidA::foo()
{
Aa;
//Geerli
}
leride, kurucu ilevin private ya da protected blmde bildirilmesinden fayda
salanabilecek baz kod kalplar da inceleyeceiz.
Kurucu ilevleri dier ye ilevlerden ayran bir baka zellik de, bu ilevlerin programc
tarafndan dorudan arlamamasdr:
classX{
public:
X();
};
Voidfoo()
{
Xobject;
X.X();
}
//Geersiz!
//Kurucuilev
//Sonlandrcilev
#include <iostream>
#include <cstdio>
#include <cstdlib>
class File {
public:
File(const char *);
void type();
~File();
private:
FILE *fp;
};
using namespace std;
File::File(const char *fname)
{
if ((fp = fopen(fname, "r")) == NULL) {
cout << "Cannot open file..." << endl;
exit(EXIT_FAILURE);
}
}
void File::type()
{
int ch;
fseek(fp, 0L, SEEK_SET);
while ((ch = fgetc(fp)) != EOF)
putchar(ch);
}
File::~File ()
{
fclose(fp);
}
int main()
{
File file = "c:\\autoexec.bat";
{
File file = "c:\\config.sys";
file.type();
}
file.type();
}
return 0;
Verilen rnekte File snfnn kurucu ilevi ismini ald dosyay ayor. Alan dosyaya
ilikin FILE trnden adres snfn fp isimli elemannda saklanyor. Sonlandrc ilev de
alm olan dosyay kapatyor. Dosyann almas ve kapatlmas ilemlerinin kurucu ve
sonlandrc ilevler tarafndan otomatik olarak yapldn gryorsunuz. rneimizdeki
kurucu ve sonlandrc ilevlerin arlma yerlerine dikkat edin. Yerel snf nesneleri iin
sonlandrc ilevler tanmlandklar bloklarn sonlarnda arlr.
C++da kurucu ve sonlandrc ilevlere ilikin her zaman geerli olan yle bir kural
vardr: Sonlandrc ilevler kurucu ilevler ile ters srada arlr. Yani kurucu ilevi daha
nce altrlan snf nesnesinin sonlandrc ilevi deha sonra altrlr. rnein a
nesnesinin kurucu ilevi b nesnesinin kurucu ilevinden daha nce arlmsa a
nesnesinin sonlandrc ilevi de b nesnesinin sonlandrc ilevinden daha sonra arlr.
Yukardaki rnekte grdnz gibi, x nesnesinin kurucu ilevi ak dikkate alndnda y
nesnesinin kurucu ilevinden daha nce arlmtr.
Bu zellik ayn bilinirlik alan iindeki snf nesneleri iin daha nemlidir. rnein,
{
Date date1, date2;
//
}
Burada kurucu ilevler nce date1 sonra date2 srasyla, sonlandrc ilevler ise ters
srada yani nce date2 sonra date1 srasyla arlr.
Global snf nesnelerine ilikin sonlandrc ilevler main ilevinden sonra arlr. Yukarda
verilen kurala gre, birden fazla global snf nesnesi varsa kurucu ilevlerin arlma
srasna ters srada sonlandrc ilevler arlr. Yani kaynak kod iinde en aada
tanmlanan nesnenin sonlandrc ilevi en nce arlr.
Aadaki programda kurucu ve sonlandrc ilevler hangi srada arlr?
classX{
//
};
Xa;
Xb;
intmain()
{
Xc;
Xd;
//
{
Xe;
//..
}
}
Burada programn ak dikkate alndnda kurucu ilevlerin arlma sras a, b, c, d, e
biiminde, sonlandrc ilevlerin arlma sras ise e, d, c, b, a biiminde olur.
Bir ilev return deyimi ile sonlandrlrsa, ilev sonlandrlmadan nce, o noktaya kadar
tanmlanm btn yerel snf nesneleri iin sonlandrc ilevler arlr. X bir snf olmak
zere rnein:
#include <iostream>
#include <cstdlib>
class Array {
public:
Array(int);
void display();
int get_item(int);
void set_item(int,int);
int get_max();
~Array();
private:
int *pArray;
int size;
};
using namespace std;
Array::Array(int length)
{
pArray = new int[size = length];
}
Array::~Array()
{
delete [] pArray;
}
void Array::display()
{
cout << '(';
for (int i = 0; i < size; ++i)
cout << pArray[i] << ' ';
cout << ')';
}
int Array::get_item(int index)
{
if (index < 0 || index >= size) {
cout << "Geersiz index: " <<
exit(1);
}
return pArray[index];
}
//Datesnfnnverielemanlariinyerayrlyor.
Date*ptr;
ptr=&date;
Artk ptr adresinden balayan bilgiler date snf nesnesinin elemanlar olarak yorumlanr.
Bu durumda *ptr ifadesi ile date ayn nesneleri belirtir.
Snf trnden bir gsterici yoluyla snfn elemanlarna ve ye ilevlerine -> ileci ile
eriilebilir. rnein artk:
ptr>display()
ile display ye ilevi arlabilir. Bu durumda display ye ilevi ptr adresindeki yani ptrnin
deeri olan adresten balayan elemanlar kullanr. Daha ak bir anlatmla,
date.display();
gibi bir arda display ye ilevi date nesnesinin veri elemanlarn kullanyor. Yani display
ilevi iinde kullanlan day, month, year deikenleri Date snfnn elemanlardr. te,
ptr>display();
arsyla da display ye ilevi p adresindeki nesnenin elemanlarn kullanr. ptr adresinde
ptr=&date
atamas ile date nesnesinin adresi olduuna gre, display ilevi de datein elemanlarn
kullanr, deil mi? Yani iki ilem edeerdir.
Tabii -> ileci yerine ncelik ayrac ve nokta ilelerini de kullanlabilir:
(*ptr).display();
ile
ptr>display();
edeerdir. Snf gstericisi yoluyla snfn elemanlarna yine -> ileci ile eriilebilir. Ancak
phesiz elemanlarn eriilebilir olmas yani snfn public blmnde bildirilmi olmalar
gerekir. rnein:
ptr>day=10;
gibi bir ifadenin geerli olmas iin day elemannn snfn public blmnde olmas gerekir.
Aadaki kodu yazarak snayn:
intmain()
{
Datex;
Date*p;
p=&x;
p>display();
return0;
}
//Kurucuileva?rlr,nknesneyaratlm!
//Kurucuileva?rlmaz,yalnzcagstericiyaratlm!
return0;
}
Bu rnekte func ilevi arsnda kullanlan date deikeni func ilevinin parametre
deikeni olan a deikenine ilkdeerini verir. Bu durumda date deikeninin tm
elemanlar bire bir dnin elemanlarna kopyalanr. Ancak byle bir ar, nesnenin btn
elemanlarnn kopyalanmasn gerektirdiinden ounlukla tercih edilmez. Bu durum
aktarm srasnda gereksiz bir zaman kayb oluturur. arlan ilev yerel bir snf nesnesi
zerinde deiiklik yapamaz.
return0;
}
phesiz, bir snf nesnesinin adres yoluyla ilevlere geirilmesi, snfn elemanlarnn
bellekte bitiik bir biimde bulunmas ile mmkn olur. Bu yntemle ilev parametre
olarak ald adresteki nesneyi de deitirebilir.
Snfn kodlarn yazan kii byle bir durumda snfn elemanlarna eriimi salamak
istiyorsa snfn private elemanlarnn deerlerini alan ve onlara deer yerletiren bir grup
ye ilev yazabilir: Bu ye ilevler ngilizce genellikle GetXXX ya da SetXXX biiminde
isimlendirilir. rnein:
//Geersiz!
//Geersiz!
gibi ilemler eriim kurallarna gre geerli deildir. Ancak bu ilemler get_year ve
set_year ye ilevleri arlarak gerekletirilebilir:
x=date.get_year();
date.set_year(2000);
ounlukla bir snf baka kod paralarna hizmet verir. Snf kodlarndan hizmet alan
kodlar (client codes) snfn public arayzne gre yazlr. Zaten daha nce de akland
gibi snftan hizmet alan bir kod parasnn snfn private blmne erimesi mmkn
deildir. Snfn public blm ya da snfn public arayz, snf bildiriminde public eriim
belirteci altndan bildirilen blmdr.
Snfn elemanlarnn private blme yerletirilmesinin en nemli faydas snfn
elemanlarnn genel yaps deitirildiinde, snftan hizmet alan kodlarn bundan
etkilenmesinin engellenmesidir. Yani snfn public arayzne bal kalmak kouluyla
snfn private ksmnda yaplacak deimeler sonucunda, snftan hizmet alan kodlarda bir
deiiklik yaplmas gerekmez. Aslnda yaplan i, snfn arayzyle (interface) snfn
kendi kodlarnn (implementation) birbirinden ayrlmas olarak tanmlanabilir. Snfn
arayznde bir deiiklik yapmadan snfn kendi kodlarn deitirmek snf kullanan
kodlarda bir deiiklik yaplmasn engeller. Byk projeler sz konu olduunda kodlarda
bir deiiklik yaplmasnn maliyeti ok azaltlm olur. Aadaki Date snfnn kodlarn ve
onu kullanan rnek bir kodu inceleyin:
#include <iostream>
#include <ctime>
using namespace std;
Date::Date()
{
time_t timer = time(0);;
tm *tptr = localtime(&timer);
C++ Kitabi (90/330)
classDate{
public:
//
private:
chardate[11];
};
usingnamespacestd;
Date::Date()
{
time_ttimer=time(NULL);
structtm*tptr=localtime(&timer);
sprintf(date,"%02d/%02d/%04d",tptr>tm_mday,tptr>tm_mon+1,tptr
>tm_year+1900);
}
Date::Date(intd,intm,inty)
{
sprintf(date,"%02d/%02d/%04d",d,m,y);
}
voidDate::display()
{
puts(date);
}
intDate::get_day()
{
returnatoi(date);
}
intDate::get_month()
{
returnatoi(date+3);
}
intDate::get_year()
{
returnatoi(date+6);
}
voidDate::set_date(intd,intm,inty)
{
day=d;
month=m;
year=y;
}
voidDate::set_day(intd)
{
sprintf(date,"%02d",d);
date[2]='/';
}
voidDate::set_month(intm)
C++ Kitabi (93/330)
//Kurucuileva?rlr
ilemiyle Date trnden dinamik bir nesne yaratlyor. Yaratlan bu dinamik nesne iin de
kurucu ilev arlr. Yani yukardaki ilemde srasyla unlar yaplr:
return 0;
Dinamik bir snf dizisi iin ayrlan alann boaltlmas durumunda [] unutulmamaldr. Eer
[] unutulursa, sorunlu durumlarla karlalabilir. Bu durumu new ve delete ile
ilevlerinin ayrntl olarak anlatld blmde ele alacaz.
//xnesnesininde?eride?itirilemez.
//Geersiz
Deikenin bir gsterici olmas durumunda ise, const anahtar szcnn verdii anlam
anahtar szcn kullanld yere gre deiiyordu:
intx=5;
constint*ptr=&x;
*ptr=1; //Geersiz!
Yukardaki rnekte ptr gsterdii yer const olan bir gsterici deikendir. ptr deikenine
baka bir adres atanabilir ama *ptr, yani ptrnin gsterdii nesne atama yoluyla
deitirilemez.
intx=5,y=10;
int*constptr=&x;
ptr=&y; //Geersiz!
Yukardaki rnekte ptr kendisi const olan bir gstericidir. ptr gstericisinin gsterdii
nesneye, yani *ptr ifadesine atama yaplabilir. Ancak ptr nesnesine bir atama yaplamaz.
Yani ptr gstericisinin bir baka nesneyi gstermesi salanamaz.
intx=5,y=10;
constint*constptr=&x;
*ptr=1;
//Geersiz!
ptr=&y;
//Geersiz!
Yukardaki rnekte ise const anahtar szcnn her iki anlam birletiriliyor. Yani ptr
hem kendisi hem de gsterdii yer const olan bir gsterici deikendir. Ne ptr nesnesine
ne de ptrnin gsterdii nesneye, yani *ptr nesnesine atama yaplabilir.
const anahtar szc bir ilev bildiriminde ya da tanmnda ilevin geri dn deerinin
tr bilgisinden nce de yazlabilir :
constchar*func();
Yukardaki bildirimden func ilevin geri dn deeri olan adresteki nesnenin
deitirilemeyecei sonucu kar:
*func()='m'; //Geersiz!
phesiz yukardaki kurallarn hepsi referanslar iin de geerlidir.
intx=5;
constint&r=x;
r=20;
//Geersiz!
constchar&func();
func()=5; //Geersiz!
voidA::foo()const
{
x=10;
//Geersiz!
}
imdi de bir ye ilevin tanm iinde snfn baka bir ye ilevinin arlmas durumunu
ele alalm. Bu duruma derleyici asndan bakalm. aran ye ilev dardan gizlice
ald adresi, yani this gstericisinin deerini, ard ileve gizlice argman olarak
geer. Yani aran ye ilevin this gstericisinin arlan ye ilevin this gstericisine
atanmas, kopyalama yoluyla aktarlmas sz konusudur.
C++'da gsterdii yer const olmayan bir gstericiye gsterdii yer const olan bir
gstericinin deerinin atanmas geerli deildir:
constint*cptr;
int*p;
p=ptr;
//Geersiz!
birth_date.day=10;
gibi bir atamayla elemann deitirilmesi mmkn olmazd.
Bir snf nesnesinin elemanlar, snfn ye ilevleri tarafndan da deitirilebilir. O halde
const bir snf nesnesinin, snfn elemanlarn deitirebilecek bir ye ilevi de
aramamas gerekir! rnein:
constDatebirth_date(4,3,1964);
birth_date.set(6,1,1966);//Geersiz.
gibi bir ilemde set ye ilevi, birth_date nesnesinin elemanlarn deitirir. Gvenlik ve
okunabilirlik alarndan bu durumun da engellenmesi gerekir. Peki, ama derleyici set
ilevinin snfn elemanlarn deitirip deitirmeyeceini nasl anlar? levin koduna
bakarak bu bilgiyi almas mmkn olmayabilir. nk program birka modl halinde
yazlm olabilir. lgili ilev o modlde bulunmayabilir. Dilin kurallarna gre, const bir snf
nesnesi ile ancak snfn const ye ilevlerini arlabilir. nk const ye ilevlerin snfn
elemanlarn deitirmeyecei baka bir kontrolle zaten gvence altna alnmtr.
rnein:
constAa;
a.func();
Yukardaki kod parasnda derleyici A snfna ilikin func ilevinin bildirimine bakarak
onun const bir ye ilev olup olmadn saptar. Eer func const bir ye ilev ise ar
geerlidir, deilse ar geersizdir. Durum bir hata iletisiyle bildirilir.
Derleyici asndan bakldnda durum son derece aktr:
a.func();
arsyla const a nesnesinin adresi gizlice func ilevinin parametre deikenine atanr. a
nesnesinin adresi
(constA*)
trndendir. Bu adres ancak gsterdii yer const olan bir gstericiye atanabilir. Oysa func
ilevi const ye ilev deil ise, ilevin gizli parametre deikeni
A*
trndendir. Dolaysyla ilev ars geerli deildir.
Gsterdii yer const olan bir snf gstericisi ya da const bir snf referans da sz konusu
olabilir. Byle bir gsterici ya da referans yoluyla bir snf nesnenin elemanlar
deitirilemez. Aadaki rnei inceleyin:
voiddisplay_totaldays(constDate*pDate)
{
cout<<pDate>totaldays();
//totaldaysileviconstde?ilse
geersiz!
}
intmain()
{
Datedate
display_totaldays(&date);
return0;
}
C++ Kitabi (105/330)
Burada display_totaldays ilevinin parametre deikeni, gsterdii yer const bir snf
gstericisidir. Bu gstericinin gsterdii snf nesnesinin elemanlar deitirilemez ve bu
nesne iin snfn const olmayan bir ye ilevi arlamaz. Ayn durum const referanslar
iin de sz konusudur. rneimizi aadaki biime dntryoruz:
voiddisplay_totaldays(constDate&r)
{
cout<<r.totaldays();
//totaldaysileviconstde?ilsegeersiz!
}
intmain()
{
Datedate;
display_totaldays(date);
return0;
}
const anahtar szcnn, genel olarak okunabilirlii artrmak amacyla kullanldn
anmsayalm. Hizmet alaca snfn arayznde const bir snf gstericisini ya da
referansn gren programc, bu gsterici ya da referans yoluyla snfn elemanlarnn
deitirilemeyeceini anlayarak daha fazla bilgi edinir. Bir ilevin adresini ald snf
nesnesinin elemanlarn deitirmeyecek ise, yani bir set ilevi deil ise, ilevin parametre
deikeni gsterdii yer const olan bir gsterici ya da referans olmaldr.
const Yklemesi
Bir snf ayn isimli ve ayn parametre yapsna sahip biri const biri const olmayan iki ye
ileve sahip olabilir. Bu duruma const yklemesi (const overloading) denir.
ye ilevlerin ama kod iine yazlmasnda snf isimleri ve parametre trlerinin yan sra
const anahtar szc de bir ekilde kodlanr. Baka bir deyile const ilevin imzasnn bir
parasdr. Byle bir durumda ayn simli ye ilev arldnda gerekte hangi ye ilevin
arlm olduu, ar ifadesinde kullanlan nesnenin const olup olmadna baklarak
saptanr. Eer ar ilemi const bir snf nesnesi ile yaplmsa const olan ye ilevin,
const olmayan bir snf nesnesi ile yaplmsa const olmayan ye ilevin arld anlalr.
Aadaki kodu derleyerek altrn:
classA{
public:
A(){}
voidfunc();
voidfunc()const;
};
#include<iostream>
usingnamespacestd;
voidA::func()
{
cout<<"A::func()"<<endl;
}
voidA::func()const
{
cout<<"A::func()const"<<endl;
}
intmain()
{
Aa;
constAca;
a.func();
ca.func();
return0;
}
//a1nesnesiiinvarsaylankurucuileva?rlyor.
//a2nesnesiilkde?erinia1nesnesindenalarakyaratlyor.
return0;
}
C++da bir snf nesnesinin yaratld her durumda bir kurucu ilevin otomatik olarak
arldn hatrlayalm. Bir snf nesnesi ilkdeerini baka bir snf nesnesinden alarak
yaratld zaman da bir kurucu ilev arlr. Bu ilev "kopyalayan kurucu ilev" olarak
isimlendirilir. Kopyalayan kurucu ilev, programc tarafndan tanmlanmaz ise derleyici
tarafndan otomatik olarak tanmlanr.
Derleyicinin otomatik olarak yazaca kopyalayan kurucu ilev, snfn public blmnde
bildirildii kabul edilen inline bir ilevdir ve parametrik yaps aadaki gibidir:
classA{
//
public:
A(constA&);
};
//Kopyalayankurucuilev
108/330
109/330
110/330
111/330
112/330
113/330
114/330
atamas ile derleyicinin yazd atama ilevi arlyor: Bu ilev, atama ilecinin terimi olan
nesnelerin elemanlarn karlkl olarak birbirine atad iin name2 nesnesinin m_p
elemanna name1 nesnesinin m_p elemannn deeri atanr. Bu durumda her iki
nesnesinin m_p eleman da ayn dinamik blou yani "Yusuf" yazsnn bulunduu dinamik
blou gsterir. Bu durumda artk "Deniz" yazsnn bulunduu blokla iliki kaybedilmi
olur. Ama asl byk hata isel bloun sonlanmasyla oluur.
sel bloun sonunda name2 nesnesi iin sonlandrc ilev arlr. arlan sonlandrc
ilevin nesnenin m_p elemannn gsterdii blou free storea geri verdiini biliyorsunuz.
Oysa isel bloun kapanmasndan sonra name1 nesnesi halen hayattadr ama bu
nesnenin m_p gstericisi artk geri verilmi olan bir blou gsterir.
name1.print();
arsyla, geri verilen bir dinamik bloa eriilir. Bu da bir gsterici hatasdr. Bu kadar
hatann zerine son hata da name1 nesnenin mr sona erdiinde oluur. Bu kez name1
nesnesi iin arlan sonlandrc ilev, daha nce geri verilen dinamik blou ikinci kez geri
vermeye alr. Bu da tanmsz davrantr.
115/330
116/330
117/330
//Dntrenkurucuilevi
118/330
main ilevinde yer alan birinci deyimle, Account trnden a1 nesnesi 200. deeriyle
ilkdeerini alarak yaratlyor. Bu durumda 200 deeri dntren kurucu ilev yoluyla
Account snf trnden bir geici nesneye dntrlr. Geici nesne de kopyalayan
kurucu ilevle a1 nesnesine ilkdeerini verir. Yani derleyici asndan byle bir deyim
Accounta1=Account(200.);
ya da
Accounta1=(Account)200;
deyimlerine edeer kabul edilebilir. C++ tr dntrme ilecinin ilevsel biiminin de
geerli olduunu anmsayalm.
C++ derleyicilerinin ou eniyileme (optimizasyon) amacyla, nce bir geici nesne
yaratp geici nesneyle kopyalayan kurucu ilevi armak yerine bu durumda dorudan
a1 nesnesi iin snfn kurucu ilevi arr. Bu durumu ileride yeniden inceleyeceiz.
Gelelim ikinci deyime:
a1=300.;
C'den bildiimiz gibi bir yap nesnesine ancak o yap trnden bir baka nesne atanabilir.
Yani C dilinde baka bir trden ifade hibir zaman bir yap trne otomatik olarak
dntrlmez. Ancak C++ da bir snf nesnesine baka trden bir ifade atandn gren
derleyici, atamann mmkn olup olmadn anlamak iin bir dntrme kurucu ilevinin
var olup olmadn sorgular. Byle bir kurucu ilev varsa otomatik tr dnm yaplr.
Byle bir ilevin olmamas durumunda dnm mmkn olmad iin, derleme
zamannda hata oluur. Yukardaki atama deyimi derleyici tarafndan aadaki gibi ele
alnr:
a1.operator=(Account(300.));
Yani nce 300 deeri dntrme kurucu ilevine argman olarak geilerek Account snf
trnden bir geici nesne yaratlr. Yaratlan geici nesnesin deeri a1 nesnesi iin arlan
atama ile ilevine argman olarak geilir.
func(200)
ars iin de benzer durum sz konusudur. func ilevine gnderilen argman olan 200.
ifadesi nce dntrme kurucu ileviyle Account snf trnden bir geici nesneye
dntrlr. func ilevinin parametre deikeni bu geici nesne ile kopyalayan kurucu
ilev araclyla ilkdeerini alr. Yani derleyici asndan kodun anlam
func(Account(200));
gibi bir deyime edeerdir.
imdi de foo ilevinin kodunu inceleyelim. foo ilevinin return ifadesi, ilevin geri dn
deerini iinde tayacak geici nesneye ilkdeerini verir. return ifadesi olan double trden
x, otomatik olarak Account snf trnden bir geici nesnenin yaratlmasna neden olur.
levin geri dn deerini iinde tayacak geici nesne kopyalayan kurucu ilev
araclyla ilkdeerini bu geici nesneden ilk deerini alr. Bu durumlarda yine derleyici
eniyileme amacyla daha ksa kodlar retebilir, ama imdilik bu konuya girmeyeceiz.
119/330
//Accounta1=(Account)200;
// a1=(Account)300.;
//(Account)500.;
return0;
}
Aklama satrlarnda tr dntrme ileleri C biimiyle kullanld.
lerdeki derslerimizde C'de olmayan C++'n yeni tr dntrme ilelerini reneceiz.
Yukardaki tr dnmleri iin C++'n yeni tr dntrme ileci olan static_cast tr
dntrme ilecinin de kullanlabileceini imdiden syleyelim.
Otomatik ya da bilinli dnmn gerekletirilebilmesi iin dntrme kurucu ilevin
parametresiyle, geici snf nesnesine dntrlecek ifadenin trnn tamamen ayn
olmas gerekmez. Dntren kurucu ilevinin arlmasndan nce doal veri trlerine
ilikin otomatik dnmler yaplabilir. Yukarda bildirilen func ilevinin aadaki gibi
arldn dnelim:
func(10);
func ilevine argman olarak int trden bir ifade gnderiliyor. func ilevinin parametresi
Account snf trndendir. Account snfnn
Account(double);
ilevinin arlabilmesi iin nce int trden 10 deeri otomatik tr dnm ile double
trne, daha sonra da dntren kurucu ilev ile Account snf trnden geici bir
nesneye dntrlr. Yani derleyici iin
func(10)
gibi bir ar
func(Account(double(10))
arsna edeerdir.
Snf trnden olmayan bir ifadenin otomatik olarak bir snf trne dntrlmesi her
zaman istenmeyebilir. Baz durumlarda byle bir dnm program yazarken oluan bir
kodlama hatasnn derleyici tarafndan bulunmasna engel olur. Aadaki koda inceleyin:
intmain()
{
inta;
Accountac(400.);
func(a);
//...
return0;
}
120/330
//Geersiz.otomatikdnmyaplamaz!
//Geersiz.otomatikdnmyaplamaz!
//Geersiz.otomatikdnmyaplamaz!
return0;
}
Accountfoo()
{
doublex;
//...
returnx;
}
//Geersiz.otomatikdnmyaplamaz!
121/330
Arkadalik Bildirimleri
Snfn ye ilevi olmayan herhangi bir ileve, o snfn private ve protected blmlerine
eriim hakk verilebilir. Bir ilev bir snfn arkada ilevi olarak bildirilebilir. Bir ilevi bir
snfn arkada ilevi olarak bildirmek iin ilev bildiriminin nne friend anahtar szc
yazlr. Aadaki snf bildirimini inceleyin:
classAccount{
intaccount_no;
doublebalance;
public;
//...
frienddoublebalance_dif(constAccount&,constAccount&);
//...
};
Bu rnekte balance_dif global bir ilevdir. Ancak Account snf iinde friend anahtar
szcyle arkada ilev olarak bildiriliyor. Arkada ilevler yalnzca eriim bakmndan
ayrcalkl olan ilevlerdir. Bir arkada ilev iinde arkada olunan snfa ilikin bir nesne,
gsterici ya da referans tanmlanrsa, o nesne gsterici ya da referans yoluyla snfn her
blmne eriilebilir. Yukardaki rnekte balance_dif ilevi Account snfnn bir arkada
ilevi olarak bildirilmitir. Bu yzden balance_dif ilevi iinde tanmlanan Account snfna
ilikin bir snf nesnesi ile snfn her blmne eriilebilir.
classA{
intx,inty;
public:
A(int,int);
friendvoidfunc();
};
Yukardaki rnei inceleyelim. A snfnn x ve y isminde iki private eleman var. func isimli
global ilev snfn arkada ilevi olarak bildiriliyor. func global bir ilev olmasna karn
func ilevi iinde tanmlanan A snf trnden nesnelerin private elemanlar iin eriim
snrlamas ortadan kalkar:
voidfunc()
{
Aa;
a.x=10;
a.y=20;
//...
}
//privateblmeeriiliyoramageerli
//privateblmeeriiliyoramageerli
Eer func normal bir ilev olsayd, yani A snfnn arkada ilevi olmasayd a.x ve a.y
eriimleri geersiz olurdu. friend anahtar szcnn yalnzca ilevin bildiriminde
kullanlmaldr. levin tanmnda friend anahtar szcnn yazlmas bir szdizim
hatasdr.
Arkada ilevler snfn ye ilevleri deildir. Arkada ilevlerin dier global ilevlerden tek
fark eriim ayrcalna sahip olmalardr. Arkada ilevlere this gstericisi geirilmez. Bu
nedenle snfn elemanlarna arkada ilevler iinde dorudan eriilemez.
Uygulamalarda genellikle arkada ilevlerin parametre deikeni arkada olunan snfa
ilikin bir gsterici ya da referans olur. lk rnekteki Account snfna balance_dif ilevini
inceleyelim:
122/330
123/330
voidfunc()
{
//
f();
}
//Geersiz!?levbildirimibubloktatannamaz!
Burada f arkada ilevinin snf iindeki arkadalk bildirimi ayn zamanda ilevin prototip
bildirimi olarak da kabul edilir. Ancak bu bildiriminin bilinirlik alan X snfnn bilinirlik alan
ile ayndr. Yani bu bildirim yalnzca main ilevi iinde bilinir. Bu yzden func ilevi iinde f
ilevi arldnda, daha nce bildirimi yaplmam bir ilev arlm olur.
Arkada Snflar
Bir baka snfn bir ye ilevine arkadalk verilebileceini belirtmitik. Bir snf bir baka
snfn tm ye ilevlerine arkadalk verebilir. Arkada snf bir snfn tm ye ilevlerinin
baka bir snfn arkada ilevi olmas durumudur. Bir snf arkada snf yapmak iin,
arkada olunan snf iinde,
friendclass<snfismi>;
bildirimini yapmak gerekir. Bu bildirimin snfn hangi blmnde yaplm olduunun bir
nemi yoktur. Bu durumda arkada olarak belirtilen snfn tm ye ilevleri ieri ilgili snf
trnden bir nesne, gsterici ya da referans yoluyla snfn her blmne eriilebilir.
Aadaki rnei inceleyin:
classNode{
intval;
Node*next;
friendclassLList;
};
classLList{
public:
voidadd(intval);
voiddelete(size_tn);
Node*get_head();
//
private:
Node*phead;
Node*pnode;
size_tsize;
};
Yukardaki rnekte LList snf Node isimli snfn arkada snfdr. LList snfnn tm ye
ilevleri iinde Node snfna ilikin nesne, gsterici ya da referans yoluyla, Node snfnn
124/330
voidfunc();
};
classB{
intb;
//...
};
voidA::func()
{
Bobject;
object.b=1;//Geersiz!
}
Yukardaki rnekte A snf B snfna arkadalk veriyor. Bu B snfnn da A snfna
arkadalk verdii sonucunu dourmaz. A snfnn ye ilevi olan func ilevi iinde
tanmlanan B snf trnden object isimli nesnenin private elemanna ulama giriimi
geersizdir, derleme zamannda hata oluturur.
125/330
126/330
127/330
128/330
//1
//2
return0;
}
Burada x.count ile eriilen count deikeni ile y.count ile eriilen count deikeni ayn
deikenlerdir. Snfn statik bir elemanna hangi snf nesnesi ile eriildiinin bir nemi
yoktur. Bu yzden statik elemanlara snf nesnesi olmadan da snf ismi ve znrlk
ileci ile eriilebilir. Byle bir eriimin geerli olmas iin bildirimin public blmde yaplm
olmas gerekir. Aadaki main ilevini inceleyin:
intmain()
{
Personx("AliSere");
cout<<Person::count<<endl;
Persony("AhmetAltntart");
cout<<Person::count<<endl;
//1
//2
return0;
}
rnekte eriim Person::count biiminde hi snf nesnesi kullanlmadan yaplyor. count
isimli eleman, snfn public blmnde bildirildii in eriim geerlidir. static elemanlara
ye ilevler iinde normal elemanlar gibi dorudan eriilebilir. rnein Person snfnn
kurucu ilevi iinde eriim bu biimde salanyor:
Person::Person(constchar*nm,intn)
{
name=newchar[strlen(nm)+1];
strcpy(name,nm);
no=n;
++count;
}
Snfn statik eleman bir dizi ya da gsterici olabilir. Eer dizi ise bildirim srasnda dizinin
boyutu belirtilmeyebilir. Ancak dizinin tanmlamas srasnda dizinin boyutu belirtilmelidir.
Snfa ilikin statik bir diziye tanmlama srasnda kme ayralar iinde ilkdeer de
verilebilir. Bu durumda dizi boyutu belirtilmeyebilir. rnein:
129/330
//Geerli
//Geerli
//Geersiz
Bir snfn statik bir eleman, ayn snfn bir ye ilevinde varsaylan argman olarak
kullanlabilir. Ancak snfn statik olmayan bir eleman bu biimde kullanlmaz:
130/330
//Geersiz
//Geerli
131/330
132/330
133/330
biiminde bir tanmlama geersiz olur, deil mi? Ancak bu durumda new ileciyle de
nesne yaratlamaz. Hatrlayacanz gibi new ileciyle bir snf nesnesi yaratldnda nce
snf nesnesinin kaplayaca yer kadar dinamik bir alan ayrlyor, daha sonra bu alann
balang adresi snfn kurucu ilevine this adresi olarak geiriliyordu. Kurucu ilev snfn
private blmde olduuna gre nesne yaratlmas yine mmkn olmaz, deil mi?
Ancak snfn kurucu ilevi private blmde ise, ve snfn bir ye ilevi, iinde snfn
baka bir private bir ye ilevi arlabildiine gre, DynamicOnly snfnn bir ye ilevi
iinde dinamik bir nesne yaratlabilir. Peki ye ilevi bir snf nesnesi ile armak
gerektiine gre, byle bir snf nesnesi nasl elde edilecek? te bu noktada static bir ye
ilev kullanlabilir: Dinamik bir snf nesnesinin adresi ile geri dnen statik bir ye ilev:
classDynamicOnly{
DynamicOnly();
//balangileviprivateblmde.
public:
staticDynamicOnly*create_object();
};
DynamicOnly*DynamicOnly::create_object()
{
returnnewDynamicOnly;
//Geerlinkyeileviindebakabirprivateileva?rlabilir.
}
#include<iostream>
usingnamespacestd;
intmain()
{
//DynamicOnlyd; Geersiz!
//DynamicOnly*ptr=newDynamicOnly;
Geersiz!
DynamicOnly*ptr=DynamicOnly::create_object();
//Geerlinkcreate_objectilevipublicblmde
deleteptr;
//Yaratlandinamiknesneyokediliyor.
return0;
}
Yukardaki rneimizde DynamicOnly snfnn kurucu ilevi private blmde bildiriliyor.
Bylece global bir ilev iinde DynamicOnly snf trnden bir nesne yaratlmas
engellenmi oluyor. Ancak bir ye ilev iinde nesne yaratlabilir. Snfn public blmne
yerletirilmi olan create_object isimli statik ye ilev, DynamicOnly snf trnden bir
adresle geri dnyor.
create_object ilevi new ileci ile yaratlm bir nesnenin adresi ile geri dnyor. levimiz
ye ilev statsnde olduu iin, ilevimiz iinde DynamicOnly snfnn private kurucu
ilevinin arlmas geerlidir.
imdi de main ilevine bir gz atalm. main ilevi iinde, statik ye ilev olan
create_object ilevi
DynamicOnly::create_object()
biiminde arlabilir. Bylece ilevin geri dn deeri olan dinamik nesnenin adresi ile
yaratlan dinamik nesne istenildii biimde kullanlabilir.
135/330
LE YKLEMES
C dilinde iki yap nesnesi birbiriyle aritmetik ilemlere sokulamaz, karlatrma ileleriyle
yap deikenleri arasnda karlatrma ilemi yaplamaz. Cde bir yap nesnesi yalnzca
ilecin terimi olabilir:
Nokta ileciyle bir yap nesnesinin elemanna eriilebilir.
Adres ileciyle bir yap nesnesinin adresi alnabilir. Bu durumda elde edilen adres yap
trnden bir adrestir.
Yap nesnesi sizeof ilecinin terimi olabilir. Bu durmda retilen deer sz konusu yapnn
bellekte kaplad byte saysdr.
Ayn trden iki yap deikeni birbirine atanabilir.
Eer yap deikenleri zerinde eitli ilemlerin yaplmas isteniyorsa ileme girecek yap
deikenlerini argman olarak alan zel ilevler yazmak gerekir. rnein Cde tarih
ilemlerini gerekletirmek iin, nce tarih bilgilerini bir yap olarak belirlemek ardndan
da bu yapya ilikin bir grup ilev yazmak gerekir.
C++da bir snf nesnesi bir ilecin terimi olabilir. Bu durumda derleyici, ilecin kullanld
ifadeyi bir ilev arsna dntrebilmek iin, programc tarafndan tanmlanan uygun
bir ilevin bulunup bulunmadn aratrr. Derleyici byle bir ilev bulursa, ilecin
bulunduu ifadeyi, bir ileve yaplan arya dntrr. le ifadesinin dntrlecei
uygun bir ilev yoksa, durum derleme zamannda hata olarak deerlendirilir.
Snf tasarlayan programc, tasarlad snfa ilikin nesnelerin belirli ilelerin terimi
olmasn istiyorsa, ilevler tanmlayarak bu amacn salayabilir. C++n var olan
ilelerine bylece tanmlanan snflara ilikin ek anlamlar yklenebilir. Bu araca ile
yklemesi (operator overloading) denir.
le yklemesi, kullanc kodlarn iini kolaylatrr, daha kolay ve daha iyi bir soyutlama
yapmasn salar. le yklemesi ile, var olan ilelerin verdii gl armdan snf
nesneleri iin de faydalanlr. Snf nesneleri sanki doal veri trlerinden nesnelermi gibi
ilelerle birlikte kullanlabilir.
Bir snf nesnesi bir ilecin terimi olmusa aadaki olaslklar sz konusudur.
a) fade derleyici tarafndan bir snfn ye ilevine yaplan arya dntrlr.
b) fade global bir ileve yaplan arya dntrlr.
c) fade derleme zaman hatas olarak deerlendirilir.
Bir ile snfn bir ye ilevi tarafndan yklenebilecei global ileve de yklenebilir.
136/330
137/330
138/330
139/330
140/330
boolMyint::operator!=(constMyint&r)const
{
returnr<*this;
}
boolMyint::operator<(constMyint&r)const
{
return!(*this<r);
}
boolMyint::operator<=(constMyint&r)const
{
return!(*this>r);
}
boolMyint::operator>(constMyint&r)const
{
return*this<r||r<*this;
}
boolMyint::operator>=(constMyint&r)const
{
return!(*this!=r);
}
Bir ye ilevin baka bir ye ilevi dorudan arlabileceini biliyorsunuz. leci ykleyen
ilev iinde de baka bir ye ile ykleyen ilev dorudan arlabilir. Myint snf nce <
ilevini tanmlyor. Dier karlatrma ilevleri grevlerini bu ilevi ararak
gerekletiriyor. Bylelikle ileride snfnn isel yapsnda bir deiiklik olmas durumunda,
daha az ye ilevin kodunda deiiklik yaplmas salanm olur.
Myint snfnn int trden parametresi olan bir dntren kurucu ileve sahip olduunu
gryorsunuz. leleri ykleyen ilevlerin sa terimleri doal veri trlerinden olursa
arlan ilevlere gnderilen argmanlar otomatik tr dnm ile Myint snfna
dntrlr. ye ilevlerin parametre deikenleri Myint snf trnden const referans
olduuna gre, rnein
intmain()
{
Myinti(25);
if(i==25)
cout<<"do?ru"<<endl;
return0;
}
gibi bir kod paras geerlidir. nk
i==25
141/330
142/330
143/330
Doal trler sz konusu olduunda, atama ilelerinin rettii deer nesneye atanan
deerdir. Ayn arayz salayabilmek iin bu ileleri ykleyen ilevlerin geri dn
deerinin Myint snf trnden referans yapldn gryorsunuz. Bylece bir ifade iinde
birden fazla atama ilecinin kullanlmas olana getiriliyor. lemli atama ilecinin sa
terimi olan nesne byle bir ilemden etkilenmeyecei iin ilevin parametre deikeni
const Myint & trnden yaplmal. levlerin tanmn aada yapyoruz:
Myint&Myint::operator+=(constMyint&r)
{
m_val+=r.m_val;
return*this;
}
Myint&Myint::operator=(constMyint&r)
{
m_val=r.m_val;
return*this;
}
Myint&Myint::operator*=(constMyint&r)
{
m_val*=r.m_val
return*this;
}
Myint&Myint::operator/=(constMyint&r)
{
m_val/=r.m_val;
return*this;
}
Myint&Myint::operator%=(constMyint&r)
{
m_val%=r.m_val;
return*this;
}
++ ve -- lelerinin Yklenmesi
++ ve - ilelerinin hem nek hem de sonek konumunda kullanldklarn biliyorsunuz.
Bu ileler global ilevler ile de yklenebilir. Ancak bu ileleri ykleyen ilevler
ounlukla snf nesnesi zerinde deiiklik yapt iin ye ilevler olarak yazlmalar
tercih edilir.
nek ve sonek konumunda bulunabilen bu ilevlerin ayr ayr yklenebilmesi iin
ilevlerin imzalarnn farkl olmas gerekir. te bu nedenle bu ileleri ykleyen ilevler
iin yle bir kural getirilmitir: Bu ileler ye ilevler ile yklendiklerinde, nek
konumunda olanlar iin ilevin parametre deikeni olmaz. Ancak sonek konumundaki
ileci ykleyen ilevin int trden bir parametre deikeni olur. Bu parametre deikeninin
144/330
145/330
returnret_val;
}
levin int trden isimlendirilmi bir parametre deikenine sahip olduunu gryorsunuz.
C dilinde szdizimi hatas olan bu durum C++ dilinde geerlidir! C++ da bir ilevin
parametre deikenine isim verme zorunluluu yoktur. Daha nce de sylendii gibi bu
parametre deikeninin tek amac ilevin imzasn nek konumunda olan operator++
ilevinden farkl klmaktr. nek konumunda olan ++ ilevi terimi olan nesnenin kendi
deerini rettii iin, ilev de benzer biimde nesnenin kendi deerini geri dndrmelidir.
Bu yzden nesnenin deeri, daha sonra geri dn deeri olarak kullanmak zere ret_val
isimli bir nesnede saklanyor. Daha nce tanmlanan ++ ilevi ile m_val elemannn deeri
1 artrdktan sonra nesnenin arttrlmadan nceki deerini tutan ret_val nesnesinin deeri
ile geri dnlyor.
rnekler ++ ilevleri iin verildi ancak - ileler iin de tamamen benzer kodlar sz
konusudur.
Yazlan ilevleri aadaki main ileviyle snayabiliriz:
#include<iostream>
usingnamespacestd;
intmain()
{
Myintx1(10),x2(20);
Myinty1(++x1),y2(x2++);
cout<<"y1="<<y1<<endl;
cout<<"y2="<<y2<<endl;
cout<<"x1="<<x1<<endl;
cout<<"x1="<<x1<<endl;
return0;
}
146/330
147/330
148/330
149/330
a[3]=5;
//ca[2]=10;//Geersiz
Std::cout<<ca[5]<<std::endl;
return0;
}
Array snfnn kurucu ilevi, istenen sayda elemann saca byklkte bir blok elde
ediyor. Sonlandrc ilev ise ayrlan dinamik blou free storea geri veriyor.
Snfn public blmnde iki ayr operator[] ilevinin bildirildiini gryorsunuz. levlerin
imzalarndaki tek farkllk, birinin const ye ilev iken dierinin const olmayan bir ye
ilev olmas. Byle ilev yklemesine const yklemesi (const overloading) dendiini
anmsayn. Acaba neden iki ayr ilev tanmland?
nce const olmayan operator[] ilevini inceleyelim. levimiz elde edilen dinamik dizinin
istenen indisli elemann, referans yoluyla dndryor. Bylece main ilevi iinde
a[3]=5;
gibi bir atama ile, dinamik dizi iindeki 3 indisli elemana atama yaplm olur. Bu atama
deyimi derleyici tarafndan aadaki biime dntrlr:
a.operator[](3)=5;
const bir dizinin elemanlarna atama ileci ile atama yaplamaz, deil mi? Benzer biimde
const bir Array nesnesinin tuttuu elemanlara da atama yaplamamas gerekir. te bu
nedenle iki ayr ilev tanmladk.
Array snf trnden const bir nesne olan a nesnesi ile operator[] ilevi arldnda,
snfn const ye ilevi arlr. const ye ilevin geri dn deeri bir referans olmad
iin
ca[4]=10;
gibi bir atama geersizdir.
150/330
151/330
152/330
153/330
154/330
155/330
while(totaldays>ms_yeartabs[is_leap(m_y)])
totaldays=ms_yeartabs[is_leap(m_y++)];
m_m=1;
while(totaldays>ms_daytabs[is_leap(m_y)][m_m])
totaldays=ms_daytabs[is_leap(m_y)][m_m++];
m_d=totaldays;
return*this;
}
///////////////////////////////////////////////////////////////////////////
Date::Date(intd,intm,inty)
{
set(d,m,y);
}
///////////////////////////////////////////////////////////////////////////
Date::Date()
{
time_ttimer=time(0);
tm*tp=localtime(&timer);
set(tp>tm_mday,tp>tm_mon+1,tp>tm_year+1900);
}
///////////////////////////////////////////////////////////////////////////
intDate::get_year_day()const
{
intyearday=m_d;
for(intk=1;k<m_m;++k)
yearday+=ms_daytabs[is_leap(m_y)][k];
returnyearday;
}
///////////////////////////////////////////////////////////////////////////
intDate::get_week_day()const
{
return(m_totaldays+msc_factor)%7;
}
///////////////////////////////////////////////////////////////////////////
Date&Date::operator+=(intn)
{
returnrevdate(m_totaldays+n);
}
///////////////////////////////////////////////////////////////////////////
Date&Date::operator=(intn)
{
returnrevdate(m_totaldaysn);
}
///////////////////////////////////////////////////////////////////////////
Date&Date::operator++()
{
return*this+=1;
}
///////////////////////////////////////////////////////////////////////////
DateDate::operator++(int)
{
Dateretval(*this);
++*this;
156/330
returnis;
}
///////////////////////////////////////////////////////////////////////////
booloperator<(constDate&d1,constDate&d2)
{
returnd1.m_totaldays<d2.m_totaldays;
}
///////////////////////////////////////////////////////////////////////////
booloperator>(constDate&d1,constDate&d2)
{
returnd2<d1;
}
///////////////////////////////////////////////////////////////////////////
booloperator>=(constDate&d1,constDate&d2)
{
return!(d1<d2);
}
///////////////////////////////////////////////////////////////////////////
157/330
while(today<=next_year_today)
cout<<today++<<endl;
return0;
}
158/330
SM ALANLARI
Global olarak bildirilmi olan varlklar iin seilmi isimlerin birbirinden farkl olmas
gerekir.
Global blgede programc tarafndan bildirilen trler, nesneler, ilevler, ablonlar, global
varlklardr. rnein bir ilevin ismiyle, global bir nesnenin ismi ayn olamaz. stelik bu
durum yalnzca tek bir kaynak dosya iin deil, projeyi oluturan dier kaynak dosyalar
iin de geerlidir. Ayn kaynak dosyada ayn ismin global dzeyde kullanlmas derleme
zamannda hataya neden olur. Ayn ismin global dzeyde farkl dosyalarda d balantya
ait olacak biimde kullanlmas, balama (linking) zamannda hataya neden olur.
Bu durumun programc iin uygulamadaki nemi udur: Projede baka ktphaneler ya
da modller kullanlyorsa, kullanlan modllerdeki global varlklarn isimleri, programcnn
yazmakta olduu kaynak dosyadaki global isimler ile ayn olmamaldr. Yani global
dzeydeki isimler akmamaldr.
sim akmasn engellemek her zaman kolay deildir. nk kullanlan modller ya da
ktphaneler farkl kiiler ya da firmalar tarafndan retilmi olabilir. zellikle byk
programlar sz konusu olduunda global isimlerin akma riski ok fazladr. Farkl
kiilerin gelitirdii ktphanelerin birletirilmesi durumunda isimlerin akmas nasl
engellenebilir? Global isimlere ynelik bu akma problemi "global isim alannn
kirlenmesi problemi" olarak (global namespace pollution) bilinir.
C++ dilinin standartlatrlma dnemi ncesinde, programclar global isim alannn
kirlenmesini, global varlklara ok uzun isimler vererek engellemeye alyorlard. Bu
isimler de ounlukla nceden belirlenen nek szcklerle oluturuluyordu. Bir rnek
verelim: Dinamik bir diziyi soyutlayan bir snfn tanmlandn dnelim. Snfn ismi
Array olarak seilirse isim akmas olasl artar. Kullanlan modller iinde bu isim bir
baka snfa verilmi olabilir. Olas bir isim akmasn engellemek iin snfa daha uzun
bir isim verilebilir:
classC_ve_Sistem_Programcilari_Array{
//
};
voiddisplay(constC_ve_Sistem_Programcilari_Array&);
Bunun iyi bir zm olduu sylenemez. C++ dilinde yazlm bir program, btn
program iinde grlebilen ok sayda snf, ilev ya da ablon ierebilir. Srekli uzun
isimler yazmak, hem programc iin zahmetli bir itir, hem de byle isimler programn
okunabilirliini azaltr.
sim alanlar (Namespaces) global isim alan kirlenmesi problemini byk lde zen,
C++ dilinin nemli bir aracdr.
Bilgisayarmzn hard diskinin sadece bir kk dizin ierdiini dnelim. Bu dizin iinde
ayn isimli iki dosya olamaz, deil mi? Kk dizin altnda eitli alt dizinler oluturabiliriz.
Bu alt dizinler iinde isimleri ayn olan dosyalar bulunabilir. rnein ismi necati.txt olan
bir dosya, hem kk dizinde hem de kk dizinin altndaki "notlar", "mektuplar", "odevler"
isimli dizinlerde bulunabilir.
Kaynak dosyann global dzeyi, yani global isim alan, yukardaki rnekteki kk dizine
benzetilebilir. Nasl kk dizin iinde iki dosyann ismi ayn olamyorsa kaynak dosyada da
global iki varln ismi ayn olamaz. te kk dizin iinde farkl isimli dizinler yaratlmas
gibi, kaynak dosyann global alannda farkl isim blgeleri yaratlabilir. Bu durumda bu
farkl blgeler iinde ayn isimler kullanlabilir.
Bir programda belirli isimleri, global alandan gizlemek iin isim alanlar tanmlanabilir:
159/330
160/330
//Geersiz
//Geerli
Yukardaki rnekte, CDernek isim alan iinde tanmlanan String snf, CDernek isim alan
dnda nitelenmi isim olarak kullanlyor. Global dzeyde bildirilen func ilevinin
parametre deikeni
CDernek::CDString&
trndendir. Bir isim alan iinde bildirilen isim, sz konusu isim alan iinde gizlenir. Bu
ismi kullanmak iin derleyiciye, sz konusu ismi hangi isim alannda aramas gerektii
bildirilmelidir. Derleyiciye bu bilgi verilmez ise, derleyici bu ismi nce bulunulan bilinirlik
alan iinde arar. Orada bulamazsa aramay bilinirlik alann kapsayan dier bilinirlik
alanlarnda srdrr. Yukardaki rnei deitirelim:
classCDString{
//
};
namespaceCDernek{
classCDString{
//
};
}
161/330
Yukarda bildirilen foo ilevinin paramesi, global isim alannda bildirilen CDString snf
trnden iken, func ilevinin parametresi CDernek isim alannda bildirilen CDString
trndendir.
sim alannda bildirilen bir ismin, nitelenmeden dorudan kullanlmasn salayan aralar
da vardr. Bu aralar "using bildirimi", "using namespace komutu" ve "argumana bal
isim arama"dr. Bu aralardan birinin kullanmyla, isim alannn bir elemanna znrlk
ileci olmakszn dorudan eriilebilir.
//globalisimalanndakimax
namespaceCDernek{
constintmax=500;
};
//Cdernekisimalanndakimax
intmain()
{
intmax=100;
//blokbilinirlikalanndakimax
std::cout<<max<<std::endl
//blokbilinirlikalanndakimax
std::cout<<::max<<std::endl;
//globalisimalanndakimax
std::cout<<Cdernek::max<<std::endl;//Cdernekisimalanndakimax
return0;
}
162/330
163/330
164/330
//...
returnresult;
}
levin geri dn deerinin trnn yazld yer, Cdernek::CDStringLib isim alan ile
belirlenen bilinirlik alan dndadr. Bu yzden ilevin geri dn deerinin tr
nitelendirilmi isim olarak yazlmaldr. Bu yzden global isim alannda bu tr
Cdernek::CDStringLib::CDString
biiminde yazlr. levin tanmnda ilevin ismi de nitelenmi isim olarak yazlr:
Cdernek::StringLib::operator+
Ancak ilevin parametre ayrac ii ile ilevin ana blou Cdernek::StringLib isim alannn
bilinirlik alanndadr. Bu yzden ilevin parametre deikenleri olan r1 ve r2 ile ilev
iindeki yerel result deikeninin tr bilgileri dorudan
CDString
olarak yazlabilir. Zira isim alan elemanlar, isim alan bilinirlik alan iinde nitelenmemi
isimleriyle dorudan kullanlabilir.
Bir isim alan elemann tanm, isim alan dnda her yerde yaplamaz. Tanm isim alannn
dnda yaplacaksa, ancak bildirimin yapld isim alann kapsayan isim alanlarndan
birinde yaplabilir. Yukardaki rnekte operator+ ilevinin tanm ya Cdernek isim alan
iinde ya da global isim alan iinde yaplmaldr. levin tanm CDernek isim alan iinde
aadaki biimde yaplabilirdi:
namespaceCdernek{
//...
namespaceCDStringLib{
classCDString{
intsize;
//...
public:
intget_size()const{returnsize;}
//...
};
CDStringoperator+(constCDString&,constCDString&);
}
CDStringLib::CDString
CDStringLib::operator+(constCDString&r1,constCDString&r2)
{
//...
}
}
Bu kez ilevin tanm CDStringLib isim alannn dnda yaplmasna karn, Cdernek isim
alan iinde yaplyor. levin geri dn deerinin trnn ve ilev isminin
String_Lib::String
String_Lib::operator+
165/330
using Bildirimi
Bir isim alan elemanna ulamak iin her defasnda bu elemann ismini nitelenmi ismi
yazmak programc asndan ok zahmetlidir. using bildirimiyle isim alan elemanna
nitelenmemi ismi ile dorudan eriilebilir.
using bir anahtar szcktr. Bu anahtar szc isim alan elemannn nitelendirilmi ismi
izler. Bildirim sonlandrc atomla sonlanr.
usingCDernek::CDString;
Yukardaki bildirimden sonra, CDernek isim alan iinde tanmlanan CDString snf
CDernek::CDString
biiminin yansra, dorudan
CDString
biiminde kullanlabilir. Yani using bildiriminden sonra CDString ismi kullanldnda
derleyici bu ismi
CDernek::CDString
olarak ele alr.
using bildirimi ile bildirilen isim, bildirimin yapld bilinirlik alanna eklenir.
using bildirimi de dier bildirimlerin ortak zelliklerini tar. Bu bildirimin de bir bilinirlik
alan vardr. using bildirimi yerel olarak yaplabilecei gibi, global isim alan iinde ya da
baka bir isim alan iinde de yaplabilir. Dier tm bildirimlerde olduu gibi using
bildirimiyle bilinirlik alanna eklenen isim
1. Eklendii bilinirlik alannda tek olmaldr.
2. Daha geni bilinirlik alanndaki ayn ismi maskeler.
3. Daha dar bilinirlik alanndaki ayn isim tarafndan maskelenir.
Aadaki rnei dikkatle inceleyin:
166/330
//Geersiz!
voidfunc()
{
inty=a;
}
main ilevi iinde yaplan
usingMynamespace::b
bildiriminden sonra yaplan
b=10
atamasyla eriilen b, Mynamespace isim alan iinde tanmlanan b olur. Yani main
ilevinin blok bilinirlik alanna, using bildirimiyle eklenen b ismi, global deiken olan b
deikenini maskeler. main ilevi iinde c isimli bir yerel deikenin tanmlanmasndan
sonra yaplan
usingMynamespace::c
bildiriminin hata oluturduunu gryorsunuz. Ayn bilinirlik alannda ayn ismin
kullanlamayacan hatrlayn. using bildiriminden nce, blok bilinirlik alannda zaten c
isimli bir yerel deiken vardr. Bu durumda bilinirlik alanna ikinci bir c isminin sokulmaya
allmas geersizdir. Son olarak main ilevinden sonra tanmlanan func ilevine bir gz
atalm. lev iinde tanmlanan bir yerel deiken olan y isimli deikene a deikeninin
deeri atanm. Ancak a isimli bir nesne bu noktada grnr deil. using bildiriminin de
bir bilinirlik alan olduunu hatrlayalm.
usingMynamespace::a;
bildirimi yerel dzeyde, yani main ilevinin iinde yapldndan yalnzca main ilevi iinde
nitelenmeden kullanlabilir. func ilevinin ana blou iinde a ismi nitelenmi isim olarak
yani Mynamespace::a biiminde kullanlmaldr.
Ancak using bildirimi yerel dzeyde deil de global dzeyde yaplsayd, bu eriim de
geerli olurdu. Aadaki kodu inceleyin:
167/330
intmain()
{
usingnamespaceMynamespace;
a=10;
168/330
return0;
}
voidfunc()
{
//inty=a;
}
main ilevinin ana blounun banda using namespace komutu yer alyor. Komutun
grlr olduu kaynak kod aralnda, Mynamespace isim alan iindeki tm isimler adeta
isim alannn dnda, yani global blgede tanmlanm gibi grlebilir duruma gelir.
a=20;
atamas geerlidir. Bu atamayla Mynamespace isim alan iinde tanmlanan a deikenine
deer atanr.
b=20;
atamas derleme zamannda ift anlamllk hatas oluturur. Zira bu noktada hem global b
deikeni hem de using namespace komutunun etkisiyle, Mynamespace isim alan
iindeki b deikeni grlr durumdadr. Hata, yalnzca b nesnesinin nitelenmemi isim
olarak kullanlmas durumunda oluur. main ilevinin ana blou iinde b nesnesi hi
kullanlmaz ise ya da b nesnesi nitelenmile kullanlrsa, ift anlamllk hatas olumaz:
//...
Mynamespace::b=100;
::b=178;
main ilevi iinde c deikeninin tanmlanmas geerlidir. Zira isim alannn iinde
tanmlanan c ile, main blou iinde tanmlanan c deikenlerinin bilinirlik alanlar farkldr.
c=30
atamas yerel c deikenine yaplm olur.
using namespace komutunun da bir bilinirlik alan vardr. Yukardaki rnekte komut main
ilevi iinde verildiinden komutun bilinirlik alan yalnzca main ilevini kapsar. main
ilevini izleyen foo ilevi iinde, a isminin nitelenmeden kullanlmas geersizdir.
using namespace komutunun kullanm son derece kolaydr. Tek bir komutun
kullanlmasyla, isim alan iindeki tm isimler dorudan kullanlabilir hale gelir. Bu kolay
bir zm gibi grnse de, gereksiz ya da ar kullanm da baka sorunlara yol aabilir.
Birden fazla ktphane kullanlyorsa ktphanelerdeki isimleri ksa biimleriyle
kullanmak iin her bir isim alan iin using namespace komutu kullanldnda, global isim
kirlenmesi sorunu yine ortaya kar.
169/330
171/330
172/330
return0;
}
173/330
174/330
175/330
TRETME
Tretme (inheritance) nesne ynelimli programlama tekniinin en nemli aralarndandr.
Tretme yoluyla nceden tanmlanm olan bir snfn zerinde deiiklik yapmadan,snfn
ilevleri geniletilebilir. nceden tanmlanm olan snflar tretme ilemi ile
gereksinimlere gre biimlendirebilir. Tretme yoluyla bir snf baka bir snfn var olan
zelliklerini alarak, o snf trnden bir nesneymi gibi kullanlabilir. Tretme, nesne
ynelimli programlamann olmazsa olmaz aralarndandr.
D dnyadaki nesneler arasnda baz ilikiler kurmamz, o nesneleri soyutlayarak
alglamamz kolaylatrr. Bu ilikilerden birincisi ngilizcede "has a" ilikisi diye bilinen
ilikidir. Bir nesne baka bir nesneye sahiptir. "Bilgisayarn monitr var", "Arabann 4
tekerlei var" gibi cmlelerde, bilgisayarn ya da arabann nelere sahip olduunu grmek,
nesneleri temel bileenlerine ayrmak, bu nesneleri daha iyi soyutlamamza yardmc olur.
phesiz araba ya da bilgisayar belki yzlerce farkl paraya sahiptir. Ancak zaten
soyutlama(abstraction), baz ayrntlar grmezden gelip temel nitelikler stnde
odaklanarak, genel kavrama derecesini artrmaya dayanr.
Soyutlamada ve alglamada kullandmz ikinci teknik ise ingilizcede "is a" ilikisi diye
bilinir. "Aslan bir hayvandr." Burada verilen ana bilgi udur. "Her aslan bir hayvandr."
Her aslan hayvanlarn genel zelliklerini tar. Ancak aslan hayvanlarn genel zellikleri
dnda baz baka zelliklere de sahiptir. Aslan yrtc bir hayvandr. Aslan etobur bir
hayvandr. Aslan hayvan kavramnn daha zelletirilmi bir yesidir. phesiz bunun tersi
doru deildir. Yani her hayvan aslanlarn genel zelliklerini tamaz. Her hayvan yrtc
deildir. Her hayvan etobur deildir. Ancak znesi hayvan olan bir cmle iinde zneyi
deitirip aslan yaparsak, cmle yanl olmaz. rnein, "Hayvanlar su ier." cmlesi
doru olduu gibi "Aslanlar su ier." cmlesi de dorudur.
"is a" ilikisi bir ana tr-yan tr ilikisidir. Bu iliki alglamay glendirmek iin
kademelendirilebilir. Bylece bir tr aac oluturulabilir. Baz hayvanlar et yiyerek
beslenir. Bu hayvalara etobur hayvanlar diyelim. Her etobur hayvan bir hayvandr. Aslan
etobur bir hayvandr. Baz etobur hayvanlarn nesli tkenmektedir. Baz etobur hayvanlar
evcilletirilebilir. Kedi evcilletirilebilen bir etobur hayvandr.
Nesne ynelimli programlama, d dnyann daha iyi modellenmesine ve programn
problem dzleminde soyutlanarak gerekletirilmesine dayanr. Gerek dnyay nesnelere
ve bu nesnelerin ilikileri yoluyla soyutluyorsak, program yazarken de soyutlamay bu
biimde yapmamz iimizi kolaylatrr.
C++ dilinde "has a" ilikisi bileik nesne oluturma yoluyla (composition) salanr.
"Bileik nesneler" isimli blmde bu konuyu incelemitik. "is a" ilikisinin kurulmasna
yardmc olan ara ise tretmedir.
Bir snfn kendisini deitirmeden ilevleri geniletilmek istenirse, yani o snfa eitli ye
ilevler ve elemanlar eklenmek istenirse bu farkl biimlerde yaplabilir. rnein eski
snfn farkl bir isimde yeni bir kopyas oluturulabilir, eklemeler yeni snf zerinde
yaplabilir. rnein, A isimli bir snfn B isimli bir kopyas oluturulup btn eklemeler B
snf zerinde yaplabilir. Bylece hem A snf bozulmam olur hem de geniletilmi bir B
snf elde edilir. Ancak bu yntemin nemli bir dezavantaj vardr. Bu yntemde A snfnn
btn ye ilevlerin B snf iin yeniden yazlmas gerekir. Yani bu yntem ayn ilevlerin
farkl snf isimleriyle yeniden tanmlanmasn gerektireceinden kodun bymesine yol
aar. kinci nemli bir dezavantaj da snfn kodlarnda deiiklik yaplmas durumunda
olur. A snfnda bir deiiklik yaplmas durumunda bu deiiklik yeni oluturulan B
snfna yansmaz. Belki de en nemlisi, byle bir deiikliin yaplabilmesi iin snfn
kaynak kodlarna gereksinim duyulmasdr. Elde snfn kodlama dosyas deil de yalnzca
snfn arayz yani balk dosyas varsa byle bir deiiklik yapmak mmkn olmaz.
176/330
Bir baka yntem olarak, bileik nesne oluturma yolu seilebilir. rnein nce aadaki
gibi bir A snf tanmlanr:
classA{
//Varolanilevlerveelemanlar
};
Sonra B snf iinde A snfn bir eleman olarak kullanlr:
classB{
private:
Aa;
public:
//Yenieklenenilevlerveelemanlar
};
Artk B snf trnden tanmlanm olan b nesnesi ile hem A snfnn hem de B snfnn
ye ilevleri arlabilir. nk b.a ifadesi ile A snfna eriilerek bu ifadeyle A snfnn
ye ilevleri arlabilir. Bu tasarm biimi ilev yinelemesine yol amamakla birlikte
eriim bakmndan sorunludur.
Nesne ynelimli programlama tekniinde arlkl kullanlan ara tretmedir. C++ dilinde
nceki snf bozmadan ilev geniletmek amacyla "tretme" arac kullanlmaktadr.
Tretme burada aklanan iki yntemden ok daha etkin ve geni bir aratr.
<tremisnfismi:>
[tretmebiimi]
<public>
<protected>
<private>
<tabansnfismi>
{
snfelemanlarnnbildirimi
};
class ya da struct anahtar szcnden sonra tremi snf isminin belirtildiine, daha
sonra ":" atomu ile tretme biimi ve taban snf isminin yazldna dikkat edin. Genel
biimden de anlalaca gibi, tretme biimi hi belirtilmeyebilir. Bu durumda snflar iin
private, yaplar iin public tretmesinin yapld kabul edilir. Yani tretme biimi olarak
bir ey yazlmamsa snflar iin private, yaplar iin public yazlm gibi ilem grr.
Aada, rnek bir tretme ileminin szdizimini gryorsunuz.
classA{
//
};
classB:publicA{
//
};
Burada B tremi snf A ise taban snftr. Tretme biimi olarak public tretmesi
seilmitir. Aklamay ve anlatm kolaylatrmak amacyla snflarn tretilmesi ekilsel
177/330
EMBED Visio.Drawing.11
Yukardaki ekil yanl izilmedi. izilen okun yn pek ok kiiye olduu gibi size de ters
gelmi olabilir. Okun yn yukardaki gibi taban snf gsterecek biimde olmaldr.
Bundan sonra yalnzca tretme" dediimizde "public tretmesi" anlalmaldr. "is a"
ilikisi public tretmesiyle salanr. Dier tretme biimlerinin amac "is a" ilikisini
kurmak deildir. protected tretmesi ve private tretmesi baz zel amalar iin kullanlr.
public tretmesinde tremi snf taban snfn public blmn olduu gibi devir alarak
kendi mterilerine (clients) sunar. Yani taban snfn public blm tremi snfn public
blmnn bir parasdr.
Byle bir tretme ileminde taban snfn protected blm tremi snfn protected
blmym gibi kullanlabilir. Taban snfn private blm tam olarak korunmutur.
Taban snfn private blmne tremi snf tarafndan hibir biimde eriilemez.
178/330
179/330
voidBase::set_base(intx)
{
b=x;
}
voidBase::base_pro_func()
{
cout<<"base_pro_func()"<<endl;
}
voidBase::display_base()const
{
cout<<b<<endl;
}
voidDer::set_der(intx)
{
d=x;
}
voidDer::der_pro_func()
{
cout<<"der_pro_func()"<<endl;
}
voidDer::display_der()const
{
cout<<d<<endl;
}
180/330
181/330
182/330
183/330
184/330
//Geerli
//Geersiz!
return0;
}
185/330
186/330
classMember{
//...
};
classOwner{
Memberm;
//...
};
Yukardaki rnekte Owner snf Member snf trnden bir elemana sahiptir. Owner snf
trnden bir nesne yaratldnda bu nesnenin iinde Member snf trnden bir isel
nesne (embedded object) de yer alr. Member snf trnden isel nesne mnin varsaylan
kurucu ilevi, Owner snfnn kurucu ilevlerinin bana derleyici tarafndan eklenen edilen
bir kodla arlr. Yine Member snf trnden isel nesne mnin sonlandrc ilevi, Owner
snfnn sonlandrc ilevinin eklenen edilen kodla arlr. erilen nesne mnin varsaylan
kurucu ilevinin deil de, parametreli kurucu ilevlerinden birisinin arlmas MIL
szdizimiyle mmkn klnmtr. imdi de tretmeye bakalm:
classBase{
//...
};
classDer:publicBase{
//...
};
Der snf trnden bir nesne yaratldnda bu nesnenin iinde Base trnden bir alt
nesne (subobject) yer alr. Bu alt nesne tremi snf olan Der snfnn taban snf olan
Base snfndan devir ald ksmdr. Derleyici rettii kodda Der snfnn kurucu
ilevlerinin bana taban snfn varsaylan kurucu ilevine yaplan bir ar kodunu ekler.
Taban snf nesnesinin sonlandrc ilevinin arlmas da yine, tremi snfn sonlandrc
ilevinin koduna derleyici tarafndan otomatik olarak eklenen, taban snfn sonlandrc
ileve yaplan ar koduyla salanr. Tremi snf nesnesinin yaratlmas srasnda taban
snf nesnesi iin parametreli bir kurucu ilevinin arlmas yine MIL szdizimiyle
mmkn klnmtr.
Fiziksel karlklar birbirine bu kadar yaknda olsa iki aracn mantksal karlklar farkl
olduu gibi modelledikleri durumlar da tamamen farkldr. Bileik nesneler "has a" ilikisini
modellerken tretme "is a" ilikisini modeller. Baz durumlarda iki snf arasnda bir
sahiplik ilikisi mi yoksa bir genellik - zellik ilikisi mi olduu kolayca anlalamayabilir.
Yani bir bileik nesne mi oluturulmaldr yoksa bir snf m tretilmelidir? Bir proje iinde
tanmlanacak snflarn neler olacan saptamak, bu snflar arasndaki ilikilerin trn
belirlemek iin baz teknikler kullanlr. Bu tekniklere ileride deineceiz.
187/330
protected
Bir snfn protected blm koruma bakmndan private blmden gevek public
blmden sk olduuna dikkat edin. imdi yerleim bakmndan bir zet yapalm.
Blm
Neler Yerletirilmeli?
public
protected
private
Snf Hiyerarisi
Tretme ilemi birden fazla kademeyi ierebilir.
Bir taban snftan farkl tretme ilemleriyle dorudan yeni snflar tretilebilir. rnein
"Ara" isimli bir snftan, "hava arac", "kara arac", "deniz arac" snflar tretilebilir.
Bir taban snftan tretilmi bir snfn kendisi bu kez taban snf olarak kullanlarak da yeni
bir tretme yaplabilir. rnein "Kara Arac" snfndan "otomobil" snf tretilip bu kez
"otomobil" snfndan da, "Yar Otomobilir" snf tretilebilir.
Bir tretme zinciri iinde mantksal olarak ilikisi kurulan snflarn tmne "snf
hiyerarisi" denir. Tretmeden elde edilmek istenen ana faydalardan biri, tretme
hiyerari iinde yer alan snflarn tm zerinde belirli ilemleri yapacak ortak kodlarn,
hiyerarinin tepesinde yer alan snfa dayal olarak yazlmasdr. Bu konuyu ileride ayrntl
olarak ele alacaz.
188/330
189/330
190/330
//1
//2
//3
//4
//5
}
Yukardaki main ilevinde yorum satrlaryla iaretlenen ilev arlarna bakalm:
//1
//2
//3
//4
//5
Bilinirlik alan ile eriilebilirlik ayn eyler deildir. C++da her zaman nce isim aramas
daha sonra eriim denetimi yaplr. Bu kuraln etkisini aadaki gibi bir tretme ileminde
deerlendirelim:
191/330
//Geersiz
//voidDer::foo(int)
//voidDer::foo(int,int)
//voidBase::foo()
//voidBase::foo(int,int)
return0;
}
192/330
193/330
194/330
der_object.foo();
ars sz konusu olduunda
voidBase::foo();
ilevi de arya aday ilev olarak belirlenir. using bildirimiyle taban ve tremi snfn foo
ilevleri ayr bilinirlik alanlarna ait olmasalar da yklenebilir.
imdi de aadaki koda bakalm:
#include<iostream>
classBase{
public:
voidfoo(){std::cout<<"Base::foo()"<<std::endl;}
};
classDer:publicBase{
public:
usingBase::foo;
voidfoo(){std::cout<<"Der::foo()"<<std::endl;}
};
intmain()
{
Derder_object;
der_object.foo();
return0;
}
main ilevi iinde arlan, Der snfnn foo ilevidir. Der snfnn foo ilevinin gizli
parametre deikeni Der & trnden iken, Base snfnn foo ilevinin gizli parametre
deikeni Base & trndendir.
195/330
196/330
3. Snf baka bir snfn iinde bildirilmise, isim kapsayan snf bildiriminden ismin
bulunduu snfn bildirimine kadar olan blgede aranr.
rnein aada B snf iindeki Str isminin kullanlmas geerlidir. Ayrca bu durumda
eriim geerlilii iin ismin kapsayan snfn public blmnde bildirilmi olmas gerekir.
classA{
public:
typedefchar*Str;
classB{
private:
StrpStr;
};
};
4. Snf baka bir snf iinde bildirilmise, isim kapsayan snf bildiriminin taban snflarnn
her yerinde aranr. Bu durumda eriim geerlilii iin kapsayan snfn public tretme
biimiyle tretilmi olmas ve kullanlan isimlerin de snfn public blmnde bildirilmi
olmas gerekir. Aadaki rnei inceleyelim:
classA{
public:
enum{SIZE=50};
};
classB:publicA{
classC{
private:
intx[SIZE];//geerlieriim
//...
};
};
Burada C snf iinde SIZE isminin kullanlmas geerlidir.
5. sim, snfn iinde bulunduu isim alannn bandan kapsayan snf bildirimine kadar
olan blgede aranr. Aadaki rnekte SIZE ismine eriim geerlidir.
namespaceX{
enum{SIZE=50};
classB{
classC{
private:
intx[SIZE];
//...
};
};
}
//geerlieriim
6. sim dosya bandan snfn iinde bulunduu isim aralna kadar olan global isim
aralnda aranr. Snf bildirimi iinde bir isim znrlk ileci ile snf ismi ya da isim
aral ismi belirtilerek kullanlm olabilir. Bu durumu ileride ele alacaz.
197/330
classC:publicB{
public:
voidfunc();
};
voidC::func()
{
x=100;
}
Burada x ismi srasyla B ve A taban snflarnda aranr. sim bulunduu anda arama
ilemine son verilir. Dolaysyla buradaki rnekte bulunan x, B snfna ilikin olan x
ismidir.
198/330
4. ye ilevin ilikin olduu snf kapsayan bir snf varsa, isim kapsayan snf bildiriminin
her yerinde aranr. Eriim geerlilii iin ismin snfn public blmnde bulunmas gerekir.
rnein:
classA{
public:
classB{
public:
voidfunc()
{
inta[SIZE];
for(inti=0;i<SIZE;++i)
a[i]=0;
//
}
};
enum{SIZE=100};
};
Burada B snf iindeki func ilevinde SIZE ismi kullanlabilir. Tabii byle durumlarda
kapsayan snfa ilikin bir eleman kullanlamaz. nk kapsama ilemi tretme ilemi
deildir. Bu yzden kullanlacak ismin de bir enum ismi, typedef ismi gibi bildirimlerden
olumas gerekir.
5. Snf kapsayan snf varsa, isim kapsayan snfn taban snf bildirimlerinin her yerinde
aranr. Bu durumda eriim geerlilii iin ismin kendi snfnn public blmnde bildirilmi
olmas gerekir. Tretme biiminin bir nemi yoktur. rnein:
classA{
public:
typedefchar*Str;
};
classB:publicA{
public:
classC{
public:
voidfunc() {StrpStr="Deneme";
//...
}
};
};
Burada Str ismi C snf iinden kullanlabilir.
6. sim snfn ya da kapsayan snfn iinde bulunduu isim alannn (namespace)
bandan kapsayan snf bildirimine kadar olan blgede aranr. rnein:
199/330
200/330
//Geersiz!
}
Base snf iinde global func ilevine arkadalk veriliyor. Global func ilevi iinde
tanmlanan Der snf trnden der_object nesnesinin private eleman olan d elemanna
eriim derleme zamannda hataya neden olur. Ancak func ilevi iinde der_object
nesnesinin taban snftan devir ald b isimli private elemana eriebilir.
201/330
return0;
}
Derder2(der1);
tanmlama deyimiyle der2 nesnesi ilkdeerini der1 nesnesinden alarak yaratlyor. Bu
durumda yaratlan der2 nesnesi iin kopyalayan kurucu ilev arlr. der2 nesnesi iin
arlan kurucu ilevin bana derleyici, taban snfn varsaylan kurucu ilevine yaplan
ar kodunu ekler. Yani taban snf iin kopyalayan kurucu ilev deil varsaylan kurucu
ilev arlr. imdi Der snfnn kopyalayan kurucu ilevin tanmn programdan kartarak
program derleyin ve yeniden altrn. Bu kez der2 snf nesnesinin taban snf ksm iin
kopyalayan kurucu ilevin arldn greceksiniz. Tremi snfn kopyalayan kurucu
ilevini yazan programc, taban snf nesnesinin kopyalayan kurucu ilevin arlmasn
MIL szdizmiyle salayabilir:
Der::Der(constDer&r):Base(r){cout<<"Der(constDer&)"<<endl;}
202/330
203/330
//Asnfnnelemanlarnyazdracak.
//Bsnfnnelemanlarnyazdracak.
//Csnfnnelemanlarnyazdracak.
//Asnfnndisplayilevia?rlr.
//Bsnfnndisplayilevia?rlr.
//Csnfnndisplayilevia?rlr;
return0;
}
204/330
//Asnfnndisplayilevia?rlr;
//Bsnfnndisplayilevia?rlr;
//Csnfnndisplayilevia?rlr;
return0;
}
Taban snf ve tremi snflarda ayn isimli ilevler varsa, ar znrlk ileci ile
yaplmamsa, eer taban snf nesnesi ya da gstericisine ilikin bir ar sz konusu ise
doal olarak taban snfn ilevi arlr.
yle bir ilev olsun ki, bu ilev snf hiyerarisi iinde yer alabilecek bir nesnenin, hangi
snf trnden olursa olsun elemanlarnn deerlerini yazdrsn. Byle bir ilev nasl
tasarlanabilir? Taban snf trnden bir gstericiye, bu snftan tretilmi herhangi bir snf
nesnesinin adresi atanabileceine gre byle bir ilevin parametre deikeni taban snf
trnden bir gsterici olabilir. Global olarak tanmlanan display isimli ilevin bildirimi yle
olabilir:
voiddisplay(constA*ptr);
imdi display ilevi ister taban snf olan A snf trnden bir nesnenin adresiyle ister bu
snfta tremi snflar olan B ve C snfndan nesnelerin adresleri ile arlabilir.
Ancak byle bir ilev arld zaman iini doru yapabilmesi iin hangi snf trnden bir
nesnenin adresiyle arldn phesiz bilmek zorundadr. lev bu durumu nasl bilebilir?
Bunu salamann bir yolu u olabilir:
Taban snf bildiriminde, taban snfa bir eleman daha eklenenerek ve bu elemannn snf
trnden nesnenin tr bilgisini saklamas salanabilir.
enum{CLASSA,CLASSB,CLASSC};
classA{
//...
inttype;
public:
//
};
A snf ve bu snflardan tretilecek snflarn kodu, bir snf nesnesi yaratldnda snf
nesnesinin type isimli elemanna CLASSA, CLASSB, CLASSC vs. deerleri atanacak
biimde yazlabilir. Bu durumda global display ilevi, aadaki gibi bir switch deyimini
kullanarak iini grebilir.
205/330
Sanal levler
Snfn bir ye ilevi sanal (virtual function) yaplabilir. Bir ye ilevi sanal yapabilmek iin
ilev bildiriminin nne virtual anahtar szc getirilir. virtual anahtar szc yalnzca
ilevin bildiriminde kullanlr. lev tanmlanrken kullanlmaz. Bir global ilev ya da bir
snfn statik ilevi virtual anahtar szc ile bildirilemez. Bir ye ilev sanal yaplrsa, o
snfn tremi snflarnda bulunan ayn isimli, ayn bildirime sahip tm ilevler da sanal
olur. Yani virtual anahtar szc yazlmasa da yazlm gibi ilem grr. Ayn bildirime
sahip olmas demek geri dn deerlerinin parametre yaplarnn ve ilev isimlerinin ayn
olmas demektir. Tremi snfn taban snfn sanal ileviyle tamamen ayn parametrik
yapya sahip ve ayn isimli bir ilev tanmlamasna, tremi snfn taban snfn sanal ilevi
ezmesi (override) denir.
classBase{
//
public:
virtualintvfunc(int,int);
//
};
classDer:publicBase{
//
public:
public:
virtualintvfunc(int,int);
//
};
Yukardaki rnekte, Base snfndan tretilmi Der snf, Base snfnn sanal vfunc ilevini
eziyor. Der snf iinde bildirimi yaplan vfunc ilevinin bildiriminde virtual anahtar
szcnn kullanlmas zorunlu deildir.
Tremi snf nesnesinin adresi taban snf gstericisine atanr bu gsterici yoluyla da
sanal bir ilev arlrsa, adresi alnan nesne hangi snfa aitse o snfn sanal ilev arlr.
Hangi ilevin arlaca derleme zamannda deil ancak programn alma zamannda
belli olur. Bu yzden bu duruma "ge balama" (late binding) ya da dinamik balama
(dynamic binding) denir. Taban snfn tremi snfa erimesi ancak bu sanal ilev
kullanmyla mmkn olur. Bir dizi tretme yapldnda tremi snflardan birine ilikin
nesnenin adresi taban snflardan birine ilikin bir gstericiye ya da referansa atanabilir.
Bu gsterici ya da referans yoluyla sanal ilev arlabilir.
Bir sanal ilev snf ismi belirtilerek znrlk ileci ile arlrsa sanallk zellii kalmaz.
206/330
207/330
3. Taban snf trnden bir referans tremi snf trnden bir nesneyle ilkdeer verilerek
tanmlanr. Bu referans yoluyla sanal ilev arlabilir. Bu durumda tremi snfa ilikin
sanal ilev arlr.
intmain()
{
Derder_object(10,20);
Base&base_ref=der_object;
base_ref.vfunc();
return0;
}
Yukardaki rnekte
base_ref.vfunc();
deyimiyle arlan vfunc ilevi Der snfnn vfunc ilevidir.
4. levin parametre deikeni taban snf trnden bir referans olur. lev de tremi
snf nesnesinin kendisiyle arlr. lev iinde bu referans yoluyla tremi snfa ilikin
sanal ilev arlr.
voidfoo(Base&r)
{
r.vfunc();
}
intmain()
{
derder_object(10,20);
foo(der_object);
return0;
}
Yukardaki rnekte
foo(der_object);
deyimiyle arlan foo ilevi iinde arlan vfunc ilevi Der snfnn vfunc ilevidir.
5. Tremi snf trnden bir nesne ya da gsterici ile taban snfa ilikin sanal olmayan
bir ye ilev arlm olsun. arlan bu ye ilev iinde snfn sanal bir ilevine ar
yapldnda, ye ilev hangi snfa ilikin bir nesne iin arlmsa o snfa ilikin sanal
ilev arlr. Aadaki rnei inceleyin:
208/330
209/330
210/330
211/330
212/330
213/330
214/330
Der();
~Der();
};
Der::Der()
{
cout<<"Der::Der()"<<endl;
der_ptr=newchar[2000];
cout<<"Der::Der()icinde2000byte'lkblokeldeedildi!"<<endl;
}
Der::~Der()
{
cout<<"Der::~Der()"<<endl;
delete[]der_ptr;
cout<<"Der::~Dericinde2000byte'likblokgeriverildi!"<<endl;
}
intmain()
{
Base*ptr=newDer;
deleteptr;
215/330
216/330
Snfn sonlandrc ilev (virtual destructor) da snfn bir ye ilevi olduuna gre, delete
ilecinin terimi olan adres hangi snf trnden ise o snfn sonlandrc ilevi arlr.
Yukardaki programda dinamik olarak yaratlan tremi snf (Der) trnden adres, taban
snf trnden ptr gstericisine atanyor. Ancak delete ilecinin terimi ptr olduundan
doal olarak taban snfa (Base) ilikin sonlandrc ilev arlr. Baz durumlarda dinamik
yaratlan snf nesnesi iin, tremi snfn deil taban snfn sonlandrc ilevinin
arlmas programn tamamen yanl ya da zararl bir ekilde almasna neden olabilir.
rnein tremi snf nesnesi taban snf alt nesnesinin dnda ayr bir kaynak kullanyor
olabilir. Tremi snf nesnesine bu kaynak, tremi snfn kurucu ilevinin arlmasyla
balanr. Tremi snf nesnesine ilikin sonlandrc ilevi, nesneye balanan kayna geri
veriyor ya da serbest brakyor olabilir. Tremi snfn sonlandrc ilevi arlmad
zaman balanan kaynan geri verilme ilemi de yaplamaz.
Sonlandrc ilevler de virtual anahtar szc ile bildirilebilir. Bu durumda sonlandrc
ilev, tpk dier sanal ilevler gibi ok biimli davran gsterir.
Yukardaki kod parasnda taban snfn sonlandrc ilevinin bildirimine virtual anahtar
szcn ekleyelim:
classBase{
public:
virtual~base();
//
};
Artk Base snf trnden bir gsterici delete ilecinin terimi yapldnda, gsterici
deikene hangi snf trnden bir adres atanmsa o snfa ilikin sonlandrc ilev
arlr. Yani yukardaki deiiklikten sonra, altrlan programda arlan Base snfnn
deil Der snfnn sonlandrc ilevi olur.
Tremi snf sonlandrc ilevinin kodunun yrtlmesinin sonunda tremi snf
nesnesinin taban snf ksm iin taban snfn sonlandrc ilevinin de arlacan
unutmayalm. Yoksa bu kez de taban snf nesnesine balanm kaynaklar serbest
braklamazd. Yukardaki deiiklikten sonra program altrldnda ekrana
Dersnfnnsonlandrcilevia?rld!..
Basesnfnnsonlandrcilevia?rld!..
yazar.
Genel olarak u kural verilebilir: Bir snfn eer en az bir sanal ilevi varsa, snfn
sonlandrc ilevi de sanal yaplmaldr. ou zaman sanal sonlandrc ilev bir koda sahip
olmaz ve bo olarak tanmlanr. Bu durumda ilev snfn inline ilevi olarak yazlr.
classMyclass{
public:
virtualvoidfunc();
virtual~Myclass(){}
};
217/330
//safsanalilev
Yukarda tanmlanan Shape snfnn draw isimli ilevi saf sanal yaplm. Saf sanal bir
ilev, kendisinden tretilecek snflara arayz salayan, kendi kodu olmayabilen bir
ilevdir.
Saf sanal ilevin tanmlanmamas durumunda balama zamannda bir hata olumaz. Peki
saf sanal ilevin tanmlanmas geerli bir durum ise, bir taban snf nesnesi ile taban
snfn saf sanal ilev arldnda ne olur?
En az bir saf sanal ilev ieren snfa soyut snf (abstract class) denir. Bir soyut snf
trnden nesne yaratlamaz. Bir soyut snf trnden nesne yaratlmas durumunda
derleme zamannda hata oluur. Aadaki kodu inceleyin:
218/330
//safsanalilev
intmain()
{
Shapemyshape;
//Geersiz!
Shape*shape_ptr=newShape; //Geersiz!
return0;
}
Yukardaki main ilevi iinde yazlan her iki deyim de geersizdir. Her iki deyimde de soyut
Shape snf trnden bir nesne yaratlmak istenmitir.
Tremi snf taban snfn saf sanal ilevini ezmelidir. Ezmezse, arlacak bir taban snf
ilevi yoktur. Tremi snf bir soyut snfn herhangi bir saf sanal ilevini ezmezse, bu
durumda tremi snf da soyut olur. Yani tremi snf trnden de bir nesne
tanmlanamaz. Saf sanal bir ilevin kendisinden tretilecek snflara verdii ileti udur:
"Beni tanmlaman gerekiyor. nk ben yalnzca bir arayzm. Beni tanmlamaz isen sen
de benim gibi bir soyut snf olursun. Senin trnden de nesne yaratma olana olmaz."
Soyut bir snf trnden nesne yaratlmaz, ancak phesiz soyut bir snf trnden bir
gsterici deiken ya da referans tanmlanabilir. Bylece snf hiyerarisi zerinde ortak ve
genel ilem yapacak kodlar, yine soyut taban snf trnden referanslar ya da gsterici
deikenler kullanlarak yazlabilir.
Aadaki Shape snfnn bildiriminde yer alan draw ve write_shape_name isimli saf sanal
ilevler, tretilmi snflar tarafndan yeniden tanmlanyor:
classShape{
public:
virtualvoiddraw()const=0;
virtualvoidwrite_shape_name()const=0;
};
Shape snfndan dorudan ya da dolayl olarak tretilmi snflardan herhangi birine ait
bir nesne zerinde ilemler yapmak zere tanmlanan process isimli global bir ilevin
yazldn dnelim:
voidprocess(Shape&r)
{
r.write_shape_name();
r.draw();
}
process ilevine hangi snf trnden nesne gnderilirse o nesnenin write_shape_name ve
draw ilevleri arlr. Shape snfnn ve process ilevinin yazlmasndan yllarca sonra
bile, Shape snfndan yeni bir snf tretilip o tremi snf trnden bir nesne tanmlansa,
o nesne ile process ilevi arldnda mantksal olarak istenen i yaplr. Yani process
ilevi iinde yeni tretilmi snfa ilikin write_shape_name ve draw ilevleri arlr.
Shape snfnn ara yz saf sanal ilevleriyle, tretilecek snflarn tanmlamasnn zorunlu
olduu ye ilevlerini gsterir.
Bir snf soyut deil ise, yani bir snf trnden nesne tanmlanabiliyor ise bu snfa somut
snf (concrete class) denir.
Bir saf sanal ilevin tanmlanmamas, derleme ya da balama aamasnda bir hata
oluturmaz. Ancak saf sanal ilevler istenirse tanmlanabilir. Programc tretme hiyerarisi
219/330
221/330
222/330
223/330
224/330
225/330
226/330
//Geersiz
227/330
okbiimlilik
Nesne ynelimli programlama tekniinde sanal ilevlerin kullanlarak ilev arlarnn
balanmasnn programn alma zamanna kaydrlmas genel olarak okbiimlilik
(polymorphism) ya da alma zaman ok biimlilii (runtime polymorphism) olarak
bilinir. Nesne ynelimli programlama teknii gerek gcn alma zaman
okbiimliliinden alr.
Bu teknikle genelletirilmi ve soyutlanm baz ilemler farkl snf nesneleri iin farkl
biimlerde yaplr. Hangi ilevin arldnn programn alma zamannda belirlenmesi
program yazmnda daha byk esneklik salad gibi ok daha iyi soyutlama olana
verir.
okbiimlilik farkl trden snf nesnelerinin gerek trleri bilinmeden, bu nesnelerin ortak
zelliklerine dayanlarak ilenmesidir.
228/330
LEV ABLONLARI
C ve C++ dillerinde ilevler trlere gre yazlr. Oysa bir ok ilev belirli bir algoritmay
kodlamak iin yazlr. lev belirli bir tre gre yazlmasna karn algoritma trden
bamszdr. rnein "iki deerden byk olann bulma" algoritmas deerlerin hangi
trden olacana gre deimez. ki nesnenin deerini takas etmek iin yaplmas
gerekenler bu nesnelerin trlerine bal deildir. Ya da bir diziyi sralamak iin yaplmas
gerekenler dizinin trne gre deimez. Ancak bu ileri yapacak ilevler ilem
yapacaklar trlere gre yazlr.
Bir ok programda, ayn algoritma sz konusu olmasna karn, farkl trler iin ilevler
programc tarafndan birden fazla kez yazlr. Programcnn ayn algoritmay farkl trler
iin yeniden kodlamasnn baz sakncalar olabilir:
1. Olas yazm hatalar sonucunda ilevlerden bazlar yanl tanmlanm olabilir.
2. Genel algoritmada bir deiiklik yapld zaman tre bal her bir ilev iin bu
deiiklikler ayr ayr yaplmaldr. Yani deiiklii tek bir yerde yaparak gerekletirmek
mmkn deildir.
Bir dizinin en byk elemannn deeri ile geri dnecek bir ilev tanmlamak isteyelim.
lev int trden bir dizi iin aadaki biimde tanmlanabilir:
intget_max(constint*ptr,size_tsize)
{
intmax=*ptr;
for(size_tk=1;k<size;++k)
if(ptr[k]>max)
max=ptr[k];
returnmax;
}
lev double trden bir dizi iin yazlmak istendiinde
doubleget_max(constdouble*ptr,size_tsize)
{
doublemax=*ptr;
for(size_tk=1;k<size;++k)
if(ptr[k]>max)
max=ptr[k];
returnmax;
}
Bu i nasl daha kolay bir hale getirilebilir? levlerin yazm nilemci programa
yaptrlabilir. Makrolar konusunu hatrlayalm:
#include<string>
#definegenerate_get_max(T)Tget_max(constT*ptr,intsize){\
Tmax=*ptr;for(intk=1;k<size;++k)\
if(ptr[k]>max)max=ptr[k]; returnmax;}
generate_get_max(int)
generate_get_max(double)
generate_get_max(std::string)
229/330
230/330
#include<iostream>
template<classT>
voidprint(Ta)
{
std::cout<<a<<std::endl;
}
intmain()
{
print(3.5);
print(30L);
print('a');
print("Ahmet");
return0;
}
Yukardaki print ilev ablonunun tanmnda typename anahtar szc de kullanlabilirdi:
template<typenameT>
voidprint(Ta)
{
std::cout<<a<<std::endl;
}
main ilevi iinde print isimli ilev ablonunun drt ayr trden argman ile arldn
gryorsunuz. Derleyici bu durumda ilgili her ilev arsn grdnde ablondan ayr
ayr ilevler retir. Yukardaki kaynak kodun derlenmesi srasnda derleyici aadaki
ilevleri tanmlar. Bu duruma ablondan ilev retilmesi ("function generation ", "function
instantiation") denir.
print(3.5);
ar ifadesi iin derleyicinin yazd ilev:
voidprint(doublea)
{
std::cout<<a<<std::endl;
}
print(30L);
ar ifadesi iin derleyicinin yazd ilev:
voidprint(longa)
{
std::cout<<a<<std::endl;
}
print('a');
ar ifadesi iin derleyicinin yazd ilev:
231/330
//Geersiz!
Bir ablon parametresi, ilev ablonunun her yerinde tr belirten szck olarak
kullanlabilir:
#include<iostream>
template<classT>
Tmax_of_three(Ta,Tb,Tc)
{
Tmax=a;
if(b>max)
max=b;
if(c>max)
max=c;
returnmax;
}
intmain()
{
std::cout<<max_of_three(12,51,67)<<std::endl;
std::cout<<max_of_three(17.2,15.3,6.7)<<std::endl;
return0;
}
ablon parametresi olarak kullanlan isim ilev ablonu dnda grnr deildir, ilev
ablonu dnda bilinmez. Ayn isim baka ilev ablonlarnda, ya da normal ilevlerde bir
akmaya neden olmadan kullanlabilir.
232/330
#include<iostream>
template<classA,classB>
voiddisplay(Aa,Bb)
{
std::cout<<a<<""<<b<<std::endl;
}
#include<string>
usingnamespacestd;
intmain()
{
strings("Necati");
intx=10;
doubled=3.25;
display(x,d);
display(d,s);
display('A',s);
//voiddisplay(int,double);
//voiddisplay(double,string);
//voiddisplay(char,string);
return0;
}
233/330
234/330
//Geerli
//Geerli
//Geersiz
//Geersiz!
return0;
}
sum_square(10)
ars geersizdir. nk ilev ablonunun iki ar parametresi varken ileve tek bir
argman gnderiliyor.
sum_square(4,7.9)
ars da geersizdir. leve iki farkl trden argmanlar gnderiliyor. Oysa ilev ar
parametrelerinin tr ayndr.
235/330
236/330
237/330
template<classT>
Tsum_square(Ta,Tb)
{
returna*a+b*b;
}
intmain()
{
Complexc1(4.,7);
Complexc2(3,8);
Complexc3=sum_square(c1,c2);
return0;
}
Yukardaki kod parasnda eer Complex snf iin operator+ ve operator* ilevleri
bildirilmemi olsa derleme zamannda hata oluurdu. Derleyici yazlm olan ablonda T
template parametresi yerine Complex trn yerletirdiinde,
a*a+b*b
ilemini yapabilmek iin ilgili ile ilevlerini arar ama bulamazd.
Tr ve Referans ar Parametreleri
lev ablonu ar parametresinin bir gsterici ya da referans olmas, baz dnmlerin
yaplp yaplmayacan belirler. Aadaki ilev ablonlar bildirilmi olsun:
template<typenameT>
voidf(T);
template<typenameT>
voidg(T&);
doublea[20];
constintci=10;
imdi bu ablonlara ilikin yaplacak baz arlar ele alalm:
f(a);
238/330
lev ablonu ar parametresi referans olmad iin, bu durumda dizi ismi otomatik
olarak dizinin ilk elemann adresine dntrlr. Yukardaki ilev ars sonucunda
derleyici f ilev ablonu iin T trn double* tr olarak belirler.
g(a);
ilev ablonu ar parametresi referans olduundan bu durumda T tr, 20 elemanl
double trden bir dizi trdr.
f(ci);
ilev ablonu ar parametresi referans olmadndan T tr int tr olarak belirlenir.
g(ci);
ilev ablonu ar parametresi referans olduundan T tr const int tr olarak belirlenir.
f(5);
Bu ar sonucunda T tr int tr olarak belirlenir. ar geerlidir.
g(7);
T tr int tr olarak belirlenir. ar derleme zamannda hata oluturur. Ancak bir const
referansa, bir deimez ile ilkdeer verilebileceini anmsayn.
Aadaki rnei derleyerek altrn:
#include<iostream>
template<classT>
voiddisplay(T&a)
{
for(intk=0;k<sizeof(a)/sizeof(a[0]);++k)
std::cout<<a[k]<<"";
std::cout<<std::endl;
}
intmain()
{
inta1[5]={1,2,3,4,5};
display(a1);
inta2[3]={10,20,30};
display(a2);
inta3[10]={2,4,6,8,10,12,14,16,18,20};
display(a3);
return0;
}
Nasl oluyor da bir dizinin elemanlarn yazan ilev dizinin eleman saysn almadan bu ii
baaryor? display ilevinin ar parametresinin tr T& olarak seildiinden,
display(a1)
239/330
//Geerli
//Geerli
//Geersiz
return0;
}
func("veli","murat");
arsnda func ilev ablon parametresi T trn char * tr olarak alr.
foo("ali","can"); //Geerli
arsnda ise ablon parametresi char [4] trdr. Her iki argmann tr de ayn olduu
iin ar derleyici tarafndan geerli olarak deerlendirilir.
foo("veli","murat");
Derleyicinin yapt karmda ise her iki argmann tr ayn deildir. leve gnderilen
birinci argmana gre T tr char[5] tr olarak belirlenirken, ikinci argmana ilikin tr
char[6] trdr. Oysa ilev ablonu ar parametreleri ayn trdendir. lev ars
geersizdir.
240/330
Bir lev ablonuyla Ayn simli Normal Bir lev Bir Arada
Bulunabilir
Bir ilev ablonuyla ayn isimli normal bir ilev bir arada bulunabilir.
Bu durumda ift anlamllk hatas (ambiguity) olumaz. Normal ilevin ilev ablonuna
kar bir ncelii vardr:
#include<iostream>
template<classT>
Tsum_square(Ta,Tb)
{
std::cout<<"templatefunction "<<std::endl;
returna*a+b*b;
}
intsum_square(inta,intb)
{
std::cout<<"nontemplatefunction"<<std::endl;
returna*a+b*b;
}
intmain()
{
intx=sum_square(4,7);
return0;
241/330
Normal bir ilevle bir ilev ablonunun ayn isimde olabilmesi, ilev ablonlarnn
zelletirilmesine yardmc olur. rnein:
template<classT>
voidfoo(Ta)
{
//...
}
Yukarda verilen foo ilev ablonunun yazlm olan ablon kodunun Date isimli bir snf
iin uygun dmediini dnelim. Bu durumda ayn isimli Date parametreli normal bir
ilev yazlabilir:
voidfoo(Datedate)
{
//...
}
Bu durumda foo ilevi Date trnden bir argman ile arldnda ilev ablonu
tarafndan ilev retilmez. zel olarak yazlan foo ilevi arlm olur.
242/330
243/330
244/330
biiminde ise
sum(ch,ui)
ilevinin arlmas durumunda derleyicinin yazaca ilev
charsum(char,unsignedint);
biiminde olur ki bu durumda bilgi kayb kanlmazdr. Eer ablon ilevin tanm
template<classT,classU>
Usum(T,U);
biiminde yaplmsa
sum(uix,ch)
ilevinin arlmas durumunda derleyicinin yazaca ilev
charsum(unsignedint,char);
eklinde olur ki, yine bu durumda bilgi kayb kanlmaz olur.
Sorunu zmek iin ilev ablonu tanmna nc bir ablon parametresi eklenebilir:
template<classT1,classT2,classT3>
T1sum(T2a,T3b);
unsignedintux=sum('a'+768U);
lev ars byle yaplrsa, T1 trnn ne olduu derleyici tarafndan ilev ar
ifadesinden karlamaz. Oysa sz konusu ilev belirlenmi argmanla arlrsa bu
durumda hata olumaz:
unsignedintux=sum<unsignedint>('a'+768U);
Yukardaki ar ile derleyiciye T1 trnn hangi tr olarak ele alnmas gerektii ak bir
ekilde bildiriliyor. Derleyici bu arya ynelik olarak aadaki parametrik yapya sahip
bir ilev yazar:
unsignedintsum(chara,unsignedintb);
Birden fazla ablon parametresinin bulunmas durumunda, ablon parametrelerinin
hepsinin belirlenmesi zorunlu deildir. Soldan balayarak istenilen sayda ablon
parametresi, ilev arsnda aka belirlenebilir. Ak olarak belirlenmemi ablon
parametrelerinin hangi trden alnacaklar derleyicinin karmna braklm olur.
Aadaki rnei inceleyin:
template<classT,classM,classU>
Tfunc(Ma,Ub)
{
Ttemp;
//...
returntemp;
}
245/330
246/330
#include<iostream>
usingnamespacestd;
intmain()
{
cout<<getmin(10,20,30)<<endl;
cout<<getmin(7.0,42.0)<<endl;
cout<<getmin('a','b')<<endl;
cout<<getmin(7,42)<<endl;
cout<<getmin<>(7,42)<<endl;
cout<<getmin<double>(7,42)<<endl;
cout<<getmin('a',42.7)<<endl;
return0;
}
main ilevi iinde yaplan tm arlar tek tek ele alalm:
getmin(10,20,30)
ars iin
template<typenameT>
inlineconstT&getmin(constT&a,constT&b,constT&c)
ablonundan T trnn int tr olmas karmyla bir ilev retilir.
getmin(7.0,42.0);
ars iin
template<typenameT>
inlineTconst&getmin(Tconst&a,Tconst&b)
ablonundan T trnn double tr olmas karmyla bir ilev retilir.
getmin('a','b')
ars iin
template<typenameT>
inlineTconst&getmin(constT&a,constT&b)
ablonundan T trnn double tr olmas karmyla bir ilev retilir.
getmin(7,42)
ars bir ilev ablonuna ilikin deildir. nk
inlineconstint&getmin(constint&a,constint&b);
biiminde bir ilev bilidirmi yaplmtr. Gerek ilevin ilev ablonuna ncelii vardr.
247/330
getmin<double>(7,42)
ars ise iki ar parametreli ilev ablonuna ilikindir. Ancak ablon parametresinin
tr derleyicinin karmna braklmadan double tr olarak bildiriliyor.
Son olarak
getmin('a',42.7)
ars yine gerek ileve ilikindir. lev arsnda kullanlan argmanlarn trleri
farkldr. Ancak iki ar parametreli ilev ablonunun ar parametreleri ayn ablon
parametresi trndendir. Bu durumda ilev ablonundan ilev retilmesi mmkn deildir.
248/330
template<>
constchar*max<constchar*>(constchar*p1,constchar*p2)
{
return(strcmp(p1,p2)>0?p1:p2);
}
#include<iostream>
intmain()
{
inti=max(50,10);
std::cout<<"i="<<i<<std::endl;
constchar*p1="Necati";
constchar*p2="Ergin";
std::cout<<"max="<<max(p1,p2)<<std::endl;
return0;
}
Szdizimi inceleyelim. template anahtar szcn bu kez ii bo bir asal ayra izliyor.
Bu asal ayratan sonra ilevin geri dn deerinin tr yazlyor. Ancak bu kez ilev
bilinen bir tr bilgisi ile, yani ablon ilev argmanna bal kalmadan yazlyor. Daha
sonra ilev ismi geliyor. lev isminden sonra gelen asal ayra iine bu kez yine tr
bilgisi yazlyor.
Yukardaki szdizime gre belirli bir tre ynelik zelletirme yaplabilmesi iin, ana ilev
ablonu tanmnn derleyici tarafndan grlebilir olmas gerekir. Yani ancak daha nce
tanmlanm olan bir ilev ablonu zelletirilebilir.
//Geersiz
return0;
}
Yukardaki kod parasnda derleyici ilev ablonunun kodunun zerinden getiinde
herhangi bir hata vermez. nk uygun bir ilev arsyla T tr bir snf tr olarak
belirlenebilir. O snfn da func isimli bir public ye ilevi olabilir. Ancak ilev uygun
249/330
250/330
intmain()
{
func<int>();
//Geerli.Tinttrolarakalnr.
func(); //Geersiz.Ttrnnneoldu?ubilgisikarlamaz.
return0;
}
//Geersiz
Yukardaki ilev ablonu ana blou iinde T::Dollar bir tr bilgisi olarak kullanlyor.
Burada typename anahtar szcnn kullanlmas zorunludur.
typenameT::Dollarsum;
deyimiyle tanmlanan sum T tr hangi snfa ilikin ise, o snf trnn iinde bildirilmi
olan Dollar trnden bir nesnedir.
251/330
for(intk=0;k<size;++k)
a[k]=fptr();
sort(a,a+size);
for(intk=0;k<size;++k)
std::cout<<a[k]<<"";
std::cout<<"\n********************************"<<std::endl;
}
doubledrand()
{
returnstatic_cast<double>(rand())/RAND_MAX;
}
intmain()
{
srand(time(0));
display<int,100>(rand);
display<double,10>(drand);
return0;
}
Yukarda tanmlanan display isimli ilevin ablon parametresi var. Bunlardan ikincisi
tre bal olmayan bir parametre. Bu parametreye karlk gelen deer ilev arsnda
belirlenmelidir.
levin amac belirli trden N tane rastgele deeri kkten bye doru ekrana
yazdrmak. ablonun tre bal birinci parametresi yazdrlacak rastgele deerlerin trn
belirliyor. ablonun ikinci yani tr d parametresi rastgele deerlerin saysn belirliyor.
ablonun tre bal olan nc parametresi rastgele say retiminde kullanlacak ilevin
trn belirliyor. Aadaki ary inceleyelim:
display<double,10>(drand);
Derleyicinin bu ablondan retecei ilevin arlmasyla drand ilevi kullanlarak 10 tane
double trden deer elde edilir. Bu deerler sral bir biimde ekrana yazdrlr. lev
ablonu iinde tr d parametre olan size ilev tanm iindeki dizinin boyutu olarak
kullanlyor. arlan sort ilevi ise STL'in standart bir ilev ablonudur.
252/330
253/330
for(intk=0;k<size;++k)
a[k]=rand()%100;
cout<<"adizisiyazdrlyor"<<endl;
display(a,a+size);
sort(a,a+size);
cout<<"adizisisraland"<<endl;
cout<<"adizisiyazdrlyor"<<endl;
display(a,a+size);
random_shuffle(a,a+size);
cout<<"adizisikartrld\nadizisiyazdrlyor"<<endl;
display(a,a+size);
cout<<"dizidearanacakde?erigirin:";
intval;
cin>>val;
int*ptr=find(a,a+size,val);
if(ptr==a+size)
cout<<"dizide"<<val<<"degeribulunamad!"<<endl;
else{
cout<<"bulundu!"<<endl;
254/330
intb[size];
copy(a,a+size,b);
cout<<"bdizisiyazdrlyor"<<endl;
display(b,b+size);
for_each(b,b+size,func);
cout<<"for_eachilevindensonrabdizisiyazdrlyor"<<endl;
display(b,b+size);
return0;
}
255/330
256/330
257/330
Programn Sonlandrlmas
alma zamannda oluan hatalarn ilenmesi konusundaki en kkten aralardan biri
ciddi bir hata ile karlaldnda programn aknn sonlandrlmasdr. Bu durum
phesiz ilk iki yntemdeki baz yklerden programcy kurtarr. Program bir hata
olutuunda sonlandrlaca iin arlan bir ilevin geri dn deerinin bir hata
durumunun saptanmas amacyla snanmasna, ya da global bir bayrak deikeninin
deerinin her ilev arsndan sonra snanmasna gerek kalmaz. Standart C dilinde bir
programn sonlandrlmas iki ayr ilevle gerekletirilebilir. exit ve abort ilevleri. Bu
ilevleri ksaca hatrlayalm:
exit levi
Bir standart C ilevi olan exit, cstdlib balk dosyas iinde bildirilmitir.
voidexit(intstatus);
exit ilevi, arld zaman program sonlandrr. exit ilevi, main ilevi baarl bir ekilde
sona erdiinde arlabilecei gibi, bir hata durumununun olumas durumunda da
arlabilir. exit ilevi, kontrol iletim sistemine devretmeden nce btn dosyalarn
tampon alanlarn tazeler (flush eder), tm ak dosyalar kapatr. exit ilevine gnderilen
0 deeri, programn baaryla sonlandrldn anlatrken sfr d bir deer ise programn
baarszlk nedeniyle sonlandrld iletisini verir. exit ilevine ar olarak gemek iin,
okunabilirlik asndan cstdlib balk dosyas iinde aadaki simgesel deeimezler
tanmlanmtr:
#define
#define
EXIT_SUCCESS
EXIT_FAILURE
0
1
C++'ta exit ilevi arlrsa program sonlandrlmadan nce tm global snf nesneleri iin
sonlandrc ilevler arlr. Ancak yaratlm olan yerel snf nesneleri ve dinamik snf
nesneleri iin sonlandrc ilevler arlmaz.
exit ilevi arldnda programn sonlandrlmasndan nce, programcnn belirleyecei
baz ilevlerin arlmas salanabilir. Bu amala standart atexit ilevi kullanlr:
atexit ilevinin de bildirimi stdlib.h balk dosyas iindedir:
intatexit(void(*)(void);
Yukardaki bildirimden de grld gibi atexit ilevinin parametre deikeni geri dn
deeri ve parametresi olmayan bir ilevi gsterecek gstericidir. atexit ilevine adresi
gnderilen ilev, exit ileviyle program sonlandrlmadan nce arlmak zere kaydedilir.
levin geri dn deeri ilevin baar durumunu gsterir. levin 0 deerine geri
dnmesi kayt ileminin baarl olduunu anlatrken 0 d bir deere geri dnmesi
baarszlk durumunu bildirir. atexit ileviyle birden fazla ilev kayt edilebilir. Bu durumda
258/330
abort levi
abort ilevi de cstdlib balk dosyasnda bildirilmitir:
voidabort(void);
abort ilevi arldnda program sonlanr. abort arld yere baar durumu gsteren
bir deer iletmez. Bu ilev programn anormal bir ekilde sonlandrlmasna iaret eder.
lev stderr akmna "abnormal program termination" yazsn yazarak program
sonlandrr. abort ilevi ile program sonlandrldnda dosyalarn tampon alanlar
tazelenmez, ne yerel ne de global snf nesneleri iin sonlandrc ilevler arlr.
Kritik uygulamalarda bir hata olumas durumunda birdenbire programn sonlandrlmas
kabul edilebilecek bir yaklam deildir. rnein bir hastahanedeki yaam destek nitesini
yneten bir program iinde beklenmedik bir durum olutuunda programn
sonlandrlmas durumu kabul edilebilir mi? Ticari bir ok uygulamada da durum byledir.
Bir faturalama program, bankacla ynelik bir uygulama, bir hata olutu diye birdenbire
sonlandrlamaz. Gerek hayata ilikin programlarda bu durumlar iin yeterli ve gerekli
nlemlerin alnmas gereklidir.
letim sistemi gibi bir uygulamada bile, alma zamannda ciddi bir hatann olumas
durumunda programn (iletim sisteminin) sonlandrlmas sorunlu durumlar yaratabilir.
alan bir ilev bir hata durumu saptadnda ou zaman oluan hatann ciddiyeti
konusunda bir fikir sahibi olamaz. rnein dinamik blok elde etmeye alan bir ilev, sz
konusu ilemin baarsz olmas durumunda, dinamik yer ayrma ileminin ne amala
yapldn bilmez. rnein dinamik yer elde etme ilemi programcnn bir debugger
kullanmas annda ya da bir tablolama programn kullanmas durumunda istenmise,
pekala bir uyar iletisi verilerek kullancnn ak olan btn programlar kapatarak iini
259/330
260/330
try blou
try blou ile ilikilendirilen catch bloklar (hata ileyen kodlar -handler)
throw ifadesi
hata nesnesinin kendisi
261/330
try Blou
try C++ dilinin bir anahtar szcdr. try anahtar szcn bir bloun izlemesi
zorunludur. inden bir hata nesnesi gnderilme olasl bulunan kod paras bir try blou
iine alnabilir:
#include<iostream>
#include"array.h"
intmain()
{
Arraymyarray(10);
intindex;
std::cout<<"birindexdegerigirin";
std::cin>>index;
try{
intx=myarray.at(10);
}
//...
return0:
}
Yukardaki main ilevinde arlan at ilevi iinden bir hata nesnesi gnderilebilecei iin
intx=myarray.at(10);
deyimi bir try blou iine alnm.
Bir try blou iinde C++ dili szdizimine uygun herhangi bir deyim bulunabilir. try blou
iinde bildirim ya da tanmlama yaplabilir. Bir try blou yerel bir bilinirlik alan oluturur.
Bir try blou iinde bildirimi yaplan deikenler bu bloun dnda grlmez.
262/330
263/330
Yukarda tanmlanan main ilevi iinde bir try blou oluturuluyor. Oluturulan try blou
iinde may_raise_exception ilevi arlyor. try blounu ayr catch blou izliyor. Birinci
catch blou ya da baka bir deyile birinci hata ileyen blok, int trden bir hata nesnesini
yakalamaya alyor. kinci hata ileyen blok, double trden, -nc hata ileyen blok,
long trden bir hata nesnesini yakalamaya alyor. Herhangi bir catch blou gnderilen
hata nesnesini yakalarsa, programn ak iinde ilgili catch blounun iindeki deyimler
yrtlr. Programn ak catch bloundan sonra en son catch bloundan sonra yer alan
deyimle srer.
catch bloklar break deyimiyle sonlanan case'ler gibidir. Uygun bir catch blou bir hata
nesnesini yakaladnda programn ak bu catch blounun iine girer. Bu catch blounun
kodu yrtldkten sonra programn ak son catch blounu izleyen kod parasnn
yrtlmesiyle srer.
try blou iine alnan kod paras iinde gnderilen hata nesnesinin yakalanmas iin
throw ileminin dorudan bu blok iinde yaplmas gerekmez.
try{
func();
}
catch(int){
//...
}
Yukardaki try blou iinde func ilevi arlyor. func ilevi iinde arlan bir ilevin de,
int trden bir hata nesnesi gndermesi durumunda, catch blou bu hata nesnesini de
yakalar.
264/330
265/330
266/330
Ynn Dengelenmesi
Bir hata nesnesinin gnderilmesi ve gnderilen hata nesnesinin yakalanmas ilev ar
mekanizmasna benzer gibi grnse de aslnda durum ilev arsndan ok farkldr.
Aadaki rnei dikkatle inceleyin:
#include<iostream>
usingnamespacestd;
voidfunc1();
voidfunc2();
voidfunc3();
intmain()
{
func1();
//...
return0;
}
voidfunc1()
{
func2();
//...
return;
}
267/330
Yukardaki programda, main ilevi func1 ilevini, func1 ilevi func2 ilevini, func2 ilevi de
func3 ilevini aryor. Programn ak func3 ilevi iinde ilerlerken return deyimiyle
karlaldnda, ak func2 ilevinde kald yerden srer. func2 ilevinde return deyimi
ile karlaldnda ise bu durumda func1 ilevinde kalnan yerden ak srer. Nihayet
func1 ilevinin almas sona erdiinde, bu kez programn ak main ilevinde kald
yerden srer.
Oysa func3 ilevi iinde bir hata nesnesi gnderilmi olsayd, programn ak dorudan
bu hata nesnesinin yakaland catch blouna ynlenirdi. Bu durumu salamak iin
gerekletirilen ilemlere ynn dengelenmesi (stack unwinding) denir. Aadaki kodu
inceleyin:
268/330
269/330
270/330
voidfunc2()
{
thrownewSample(2);
}
intmain()
{
try{
//func1();
func2();
}
catch(Samplea){
a.display();
}
271/330
return0;
}
Tremi snf nesnesinin tr ile ilgili taban snf nesnesinin tr arasnda tam uyum
olduu kabul edilir. Yani tremi snf nesnesinin adresiyle throw ilemi yapldnda,
taban snf trnden bir gstericiye sahip catch blou bunu yakalayabilir. Uygulamalarn
ounda snf ktphanesinde bir tretme hiyerarisi iinde hata ileme snflar bulunur.
MFC'de ktphane ierisinde normal d bir durumla karlaldnda, ilgili hata snf
trnden bir nesne dinamik olarak yaratlr ve dinamik hata nesnesinin adresiyle throw
ilemi yaplr. Programc ktphanedeki btn throw ilemlerinin bir hata snf nesnesinin
adresiyle yapldn bilir. Dolaysyla oluabilecek her trl hata durumunu aadaki gibi
ele alabilir:
try{
//...
}
catch(CException*pcException)
{
//...
deletepCException;
}
Hata snflar da sanal ilevlere sahip olabilir. Bylece okbiimlilii destekleyecek bir
ekilde tasarm yaplabilir. Taban snfn sanal ilevlere sahip olmas ok biimli bir hata
ele alma ilemini salayabilir. rnein, yukardaki catch blounda CException snfnn
sanal display() isimli bir ilevi arlabilir. Bylece oluan hata durumuna ilikin hata iletisi
ekrana ya da bir dosyaya yazdrlabilir.
Bir snf ktphanesi kullanlarak proje gelitiriliyorsa, programc var olan hata
snflarndan tretme yaparak kendi hata snflarn oluturmaldr. rnein MFC de bir seri
port uygulamas gelitirildiini dnelim. Bu durumda CException snfndan
CSerialException gibi bir snf tretilerek, aadaki gibi bir throw ilemi yaplabilir:
try{
thrownewCSerialException;
//...
}
catch(CException*pCException){
pCException>Disp();
deletepCException;
}
CException snfndan tretilmi CMemoryException gibi bir snf olduunu dnelim.
Aadaki gibi try-catch bloklar oluturulmu olsun.
272/330
273/330
274/330
voidfunc3()
{
try{
//throw'A';
//throw1;
//throw1L;
throw1.5;
}
catch(char){
std::cout<<"hatafunc3isleviicindeyakalandi!"<<std::endl;
exit(EXIT_FAILURE);
}
}
func3 ilevi iinde gnderilen hata nesnesinin trn deitirerek program yeniden
derleyip altrn. Programn almas srasnda oluan farkll gzleyin.
275/330
276/330
//Geersiz!
Beklenmeyen Hata
Bir hata nesnesi gndermedii belirtilen bir ilev iinden throw ifadesiyle bir hata nesnesi
gnderilmesi, ya da belirli trden bir hata nesnesi gnderdii belirtilen bir ilevin baka
trden bir hata nesnesi gndermesi derleme zamannda kontrol edilmez . Bu durummda
derleme zamannda bir hata olumaz. Bu kontrol programn alma zamannda yaplr. Bir
ilevin bildiriminde throw anahtar szc kullanlmsa, yani gnderebilecei hata
nesnesi ya da nesnelerinin trleri hakknda bilgi verilmise o ilevin belirtilmeyen bir
trden hata nesnesi gndermesi durumuna "beklenmeyen hata" (unexpected exception)
denir.
Bir ilevin iinde beklenmeyen bir hata durumu olutuunda, yani ilev bildiriminde
belirtilmeyen bir trden hata nesnesi gnderildiinde, unexpected isimli standart bir ilev
arlr. unexpected ilevinin nceden belirlenmi davran standart terminate ilevini
armaktr. terminate ilevinin de nceden belirlenmi davrannn, abort ilevini
armak olduu daha nce aklanmt.
Nasl terminate ilevinin nceden belirlenmi davrann deitirmeye ynelik
set_terminate() ilevi var ise, unexpected ilevinin de nceden belirlenmi davrann
deitirebilmek iin set_unexpected ilevi tanmlanmtr:
Bu ilev <exception> balk dosyas iinde aadaki gibi bildirilmitir:
typedefvoid(*terminate_handler)();
terminate_handlerset_unexpected(terminate_handlerfp)throw();
voidunexpected()
set_unexpected ilevinin bildiriminden aadaki bilgiler karlabilir:
terminate_handler parametre deikeni olmayan, geri dn deeri retmeyen bir ilevi
gsteren gsterici trnn yeni tr ismidir.
set_unexpected ilevi terminate_handler trnden bir geri dn deerine ve
terminate_handler trnden bir parametre deikenine sahiptir.
set_unexpected ilevinin geri dn deeri, geri dn deeri retmeyen ve parametre
deikeni olmayan bir ilev trnden adrestir. set_unexpected ilevinin parametre
deikeni geri dn deeri retmeyen ve parametre deikeni olmayan bir ilevi
gsteren gstericidir. Bu durumda set_unexpected ilevine byle bir ilevin adresi
geilmelidir. lev isimlerinin birer adres olduunu hatrlayn:
277/330
278/330
279/330
//Geersiz!
280/330
return0;
}
281/330
voidfoo(File&r)
{
try{
if(!r.is_valid())
throwFileException("letter.txt");
}
catch(FileException&r_ex){
cout<<"gecersizdosya:"<<r_ex.get_message()<<endl;
if(r.open_new_file()==FAILURE)
throw;
}
}
foo ilevinde yer alan try blou iinde, is_valid ilevi ile dosyann geerlilii snanyor.
Dosya geersiz ise FileException snf trnden bir hata nesnesi gnderiliyor. try blounu
izleyen catch blou ile gnderilen hata nesnesi yakalanyor. Ekrana bir hata iletisi
yazdrlyor. Daha sonra yeni bir dosya almaya allyor. Eer yeni dosya alamam ise
hata nesnesi yeniden gnderiliyor. main ilevi iinde yaplan foo ilevi arsnn bir try
blou iine alndn bu try blounu ise bir catch blounun izlediini gryorsunuz.
Yeniden gnderilen hata nesnesi bu kez catch all blou tarafndan yakalanr. Programn
ekran kts aadaki gibi olur:
gecersizdosya:letter.txt
yenidosyaacilamiyor!
tekrargonderilenhatanesnesiyakalandi
ou durumda hata nesnesini yakalayarak ksmi olarak ileyen kod paras, hata nesnesi
zerinde deiiklik de yapar. Bylece yeniden gnderilen hata nesnesini yakalayan kod
paras, hatann ksmen ele alndndan haberdar olur. te catch parametrelerinin bir
snf trnden olmas yerine bir snf trnden referans olarak seilmesinin bir nedeni de
budur. catch parametresi bir snf trnden olursa, hata nesnesi zerinde yaplan
deiiklikler yalnzca yerel bir kopya zerinde yaplr:
Aadaki gibi bir catch blounu ele alalm:
catch(Exceptione)
{
e.set_value(/**/); //catchparametresizerindede?iiklikyaplyor...
throw;
//Hatanesnesiyenidengnderiliyor.
}
Yukardaki catch blouyla, gnderilen hata nesnesinin kendisi yakalanmaz. Gnderilen
hata nesnesi catch parametresi olan e nesnesine ilkdeer verir. Dolaysyla
e.set_value(/**/);
arsyla deitirilen, asl hata nesnesi deil catch parametresidir. Yeniden throw
deyimiyle gnderilen hata nesnesi zerinde bir deiiklik yaplmamtr. Ancak catch
parametresi referans olsayd, durum deiirdi:
282/330
//hatanesnesizerindede?iiklikyaplyor...
//hatanesnesiyenidengnderiliyor.
283/330
284/330
285/330
uncaught_exception levi
286/330
287/330
STL'de yer alan standart snflardan bazlarna ilikin ilevler bir hata durumuyla
karlatklarnda, exception snfndan tretilmi snf trlerinden hata nesnesi gnderir.
STL ktphanesinden gnderilen tm hata nesneleri exception snfndan tretilmi
snflardan olduklar iin, bu hata nesnelerinin tm parametresi exception snf trnden
referans olan bir catch blou tarafndan yakalanabilir.
standart exception snfndan yine standart logic_error ve runtime_error isimli snflar
tretilmitir.
Bu snflarn tanm <stdexcept> isimli balk dosyasnda yaplmtr. logic_error snf
mantksal tabanl hatalarn ele alnmas iin dnlmtr. run_time_error snf ise
programn alma zamannda ortaya kabilecek hatalar iin dnlmtr. Programc
kendi hata ileme snflarn taban snf olan exception snf yerine bu snflardan da
tretebilir:
classMyException{
public:
MyException(constchar*pMessage):logic_error(pMessage){}
};
logic_error snfndan da aadaki snflar tretilmitir:
domain_error
invalid_argument
length_error
out_of_range
bad_cast : dynamic_cast ileci tarafndan gnderilen hata nesnesi bu snf trndendir.
bad_typeid:
runtime_error snfndan ise aadaki snflar tretilmitir:
range_error
overflow_error
bad_alloc:new ileci tarafndan gnderilen hata nesnesi bu snf trndendir. Bo bir snf
olarak tanmlanmtr.
Programc zel bir ktphane sisteminde almyorsa hata ileme snflarn STLin
snflarndan treterek oluturmaldr. C++ dilinin baz normal d durumlarnda
gnderdikleri hata nesneleri de exception snfndan tretilmitir.
288/330
289/330
290/330
291/330
dynamic_cast leci
Bu ile RTTI mekanizmasnn arlkl olarak kullanlan aracdr. Bu ilecin kullanlmasyla,
taban snf trnden bir adresin, programn alma zamannda tremi snf trnden bir
adrese gvenilir bir ekilde dntrlp dntrlemeyecei renilebilir.
lecin kullanm daha nce aklanan yeni tr dntrme ilelerinin (static_cast,
const_cast, reinterpret_cast ) kullanmna benzer.
dynamic_cast<Der1*>(baseptr)
Yukardaki ifade ile baseptr adresi Der1 snf trnden bir adrese dntrlmeye
allyor. Eer baseptr nesnesinin iinde gerekten Der1 snf trnden bir nesnenin
adresi varsa, dnm baarl olur. Bylece ile Der1 snf trnden bir adres retir.
Eer baseptr gsterici deikeni iinde Der1 snf trnden bir adres yoksa, dnm
baarl olmaz, ile 0 (NULL) adresini retir.
dynamic_cast ilecinin terimi olan adresin -yukardaki rnekte baseptr gstericisininokbiimlilii destekleyen bir snf trnden olmas zorunludur. Yani baseptr gsterici
deikenin ait olduu snfn, en az bir sanal ilev iermesi gerekir. Aksi halde ilecin
kullanm geersizdir.
292/330
bad_cast Snf
dynamic_cast ileci ile snf nesnesi gsteren bir ifade yani bir sol taraf deeri, snf
trnden bir referansa da dntrlebilir:
#include<iostream>
classBase{
//...
public:
virtual~Base(){}
};
classDer:publicBase{
//...
};
voidfunc(Base&baseref)
{
Der&r=dynamic_cast<Der&>(baseref);
//...
}
intmain()
{
Derder;
func(der);
return0;
}
Peki dntrme ileminin baars nasl snanabilir? Bir adres trne dntrme
yapldnda, dntrme ileminin baarsz olduu dynamic_cast ilecinin NULL adresi
deeri retmesiyle anlalyordu. Ancak NULL referans diye bir kavram olmad iin,
referansa yaplan dnmn baars bu ekilde snanamaz.
Dnmn baarszl durumunda exception handling mekanizmas devreye girer.
dynamic_cast ileciyle referansa yaplan bir dnm baarl olmaz ise standart bad_cast
snf trnden bir hata nesnesi gnderilir. bad_cast snf Standart C++ ktphanesi
tarafndan tanmlanm, standart exception snfndan tretilmi bir snftr. Bu snfn
tanm standart typeinfo balk dosyasndadr.
293/330
294/330
//...
}
Yukardaki rnekte func ilevi arldnda, func ilevine C snf trnden ya da C
snfndan treyen bir snf trnden adres geilirse, dynamic_cast ileci baarl olur. le
C snf trnden bir adres retir. Aksi halde ile NULL adresi retir. Snama programn
alma zamannda yaplr.
295/330
296/330
return0;
}
3. Snfn char trden bir adrese geri dnen name isimli bir ye ilevi vardr. Bu ilevin
geri dn deeri olan adreste snfn ismi gsteren bir karakter dizisi vardr:
std::cout<<typeid(Sample1).name()<<std::endl;
std::cout<<typeid(*ptrs2).name()<<std::endl;
std::cout<<typeid(rs1).name()<<std::endl;
std::cout<<typeid(int).name()<<std::endl;
std::cout<<typeid(longdouble).name()<<std::endl;
//classSample1
//classSample2
//classSample1
//int
//longdouble
Ayrca snfn bool deere geri dnen before isimli bir ye ilevi vardr. Bu ilev type_info
trnden nesnelerin sralanabilmesi iin tanmlanmtr. Bu ye ilev ile *this nesnesine
ilikin trn, arguman olarak alnan type_info nesnesi ile karlatrlmas yaplr.
297/330
298/330
bad_typeid Snf
Birok uygulamada typeid ilecinin terimi, ierik ileci ile oluturulan bir ifadedir.
Bu durumda, ierik ilecinin terimi NULL adresi ise, standart bad_typeid snf trnden
bir hata nesnesi gnderilir. bad_typeid snf, standart exception snfndan tretilmitir.
Snfn tanm typeinfo balk dosyas iindedir.
Aadaki rnei inceleyin:
#include <iostream>
#include <typeinfo>
class Base {
//...
public:
};
void func(Base *ptr)
{
try {
std::cout << typeid(*ptr).name() << std::endl;
}
catch (std::bad_typeid &) {
std::cerr << "dereferencing null pointer" << std::endl;
exit(EXIT_FAILURE);
}
//...
}
299/330
300/330
301/330
302/330
cout << "yazi = (" << ptr << ")" << endl;
//...
return 0;
rnekte, operator new ilevi, yerel str dizisine girilen yaznn uzunluundan 1 byte daha
byk bir dinamik alan elde ediyor. Bu alann adresi void * trnden bir deer olarak
dar iletiliyor. C++ da void trden adreslerin baka trden gstericilere dorudan
atanamadn anmsayn. Sz konusu dinamik bellek blounun balang adresi, char *
trne dntrlerek, ayn trden ptr gstericisine atanyor. Bu gsterici deiken
yardmyla ilgili yaz ileniyor.
operator new ilevi de yklenebilir. Bu ileci global olarak yklemek mmkn olduu gibi,
bir snfn ye ilevi olarak yklemek de mmkndr. Yklenen new ileci deil operator
new ilevidir. new ilecinin nceden belirlenmi davrann deitirme olana yoktur.
returnptr;
}
303/330
intmain()
{
cout<<"sizeof(A)="<<sizeof(A)<<endl;
A*pd=newA;
cout<<"pd="<<pd<<endl;
//...
return0;
}
Yukardaki programda tanmlanan A snfnn kurucu ilevi iinde, ilevin arldn
gsteren bir yaz ile this gstericisinin deeri ekrana yazdrlyor.
Tanmlanan operator new ilevinin ana blou iinde de, ekrana ilevin arldn
gsteren bir yaz bastrlmas salanyor. Bu ilev, parametre deikeni olan size
boyutunda bir dinamik bellek blou elde ederek bloun balang adresini dndryor.
lev geri dn deerini retmeden nce dinamik bellek blounun balang adresini
ekrana yazdryor.
main ilevi iinde ise nce ekrana A snfnn sizeof deerinin yazdrldn gryorsunuz.
Daha sonra new ileci kullanlarak bir A snf nesnesi iin bellekte dinamik bellek blou
elde ediliyor. new ilecinin rettii deer A snf trnden gsterici deiken olan pdye
atanyor. Son olarak pd gsterici deikeninin deeri ekrana yazdrlyor.
altrlan programa ilikin rnek bir ekran kts aada veriliyor:
sizeof(A)=8
operatornew()
size=8
eldeedilenblogunadresi:0x3d3dc0
A::A()
this=0x3d3dc0
pd=0x3d3dc0
Globaloperator newilevi istenirse birden fazla parametre deikenine sahip olacak
ekilde yklenebilir. Ancak ilevin ilk parametre deikeninin size_t trnden olmas
zorunludur. Bu durumda ilevin new ileci yoluyla arlmas mmkn olmaz. leve ar
yapmak zorunludur.
304/330
305/330
306/330
307/330
308/330
309/330
returnallocation_pool+(block_no*sizeof(A));
}
voidA::operatordelete(void*ptr)
{
if(!ptr)
return;
unsignedlongblock=reinterpret_cast<unsignedlong>(ptr)
reinterpret_cast<unsignedlong>(allocation_pool);
block/=sizeof(A);
cout<<block<<"nolublokgeriveriliyor!"<<endl;
allocation_map[block]=0;
}
intmain()
{
A*pa[A::max_object_size];
try{
for(size_ti=0;i<A::max_object_size;++i)
pa[i]=newA;
newA;
}
catch(bad_alloc){
cerr<<"bellekyetersiz!"<<endl;
}
deletepa[3];
//geriverilenbellekalnkullaniliyor!
A*ptr=newA;
deleteptr;
for(size_ti=0;i<A::max_object_size;++i)
deletepa[i];
return0;
}
310/330
rnek programda A isimli bir snf tanmlanyor. Snfn eleman olan char trden s dizisi
yalnzca snf nesnesinin belirli bir yer kaplamas amacyla ekledik. Snfn
max_object_size isimli const statik eleman snf trnden dinamik olarak yaratlabilecek
en fazla nesne saysn tutuyor. Snfn iki statik dizi eleman var. Byte trden
allocation_pool isimli dizi snf nesnelerinin yaratlabilecei bellek havuzunu belirliyor. En
fazla allocation_pool dizisinin boyutu kadar sayda dinamik A nesnesi yaratlabilecek. Byte
trden allocation_map dizisi ise allocation_pool dizisinin hangi bloklarnn kullanmda
olduu bilgisini tutuyor. Yani bir bayrak dizisi olarak kullanlyor. Dizinin k indisli
elemannn deerinin 0 olmas bellek havuzundaki ilgili bloun henz kullanmda
olmadn gsteriyor.
A snf iin tanmlanan operator new ilevi nce bayrak dizisini tarayarak bellek
havuzunda henz kullanlmayan bir blok olup olmadna bakyor. Bellek havuzundaki
bloklarn tm kullanmda ise yani havuzun tamam kullanma sunulmu ise, bad_alloc
snf trnden bir hata nesnesi gnderiliyor. Bellek havuzunda en az bir nesnelik yer
varsa, ilgili bloun balang adresi geri dn deeri olarak dar iletiliyor. Snfa ilikin
operator delete ilevi ise parametresine geilen adresin hangi bellek blouna ilikin
olduunu hesaplayarak bayrak dizisinin ilgili elemannn deerini sfrlyor. Blou yeniden
kullanma ayor.
#include<iostream>
#include<new>
classA{
inta,b;
public:
A():a(0),b(0){std::cout<<"A::A()"<<std::endl;}
~A(){std::cout<<"A::A()"<<std::endl;}
voiddisplay()const{std::cout<<"a="<<a<<"\n"<<"b="<<b<<
std::endl;}
};
intmain()
{
chars[sizeof(A)];
A*ptr=new(s)A;
ptr>display();
ptr>~A();
}
311/330
for(intk=0;k<3;++k)
ptr[k].display();
for(intk=0;k<3;++k)
ptr[k].~A();
return0;
}
Bu kez yerel s dizisini, A nesnesinin saca byklkte tanmladk. Yeri belirli new
ileci ile bu kez A snf nesneleri balang adresi verilen blokta yaratld. A snfnn
sonlandrc ilevinin her nesne iin ayrca arldna dikkat edin.
312/330
classA{
//...
};
voidfunc()
{
A*ptr=new(nothrow)A;
if(!ptr){
//...
}
}
//...
new anahtar szcnden sonra alan ayra iinde nothrow isminin yazldn
gryorsunuz. Daha sonra da bir tr ismi yer alyor. Derleyicinin rettii kodla, dinamik
bellek blou elde etmek iin arlan ileve sizeof(A) deeri ve nothrow nesnesi geilir.
Yukardaki main ilevinde nothrow new ileciyle, A snf trnden bir nesne iin dinamik
bir blok elde ediliyor. lemin baars, ilecin rettii deerin NULL adresi olup olmad
ile snanyor. Eer dinamik bir blok elde edilirse, derleyicinin rettii kodla A snfnn
kurucu ilevi arlr. Ayn ilecin keli ayral biimiyle birden fazla snf nesnesi iin de
dinamik bir bellek blou elde edilebilir:
A*ptr=new(nothrow)A[10];
Yukardaki deyimin yrtlmesi sonucunda dinamik bellek blou elde etme giriimi
baarl olmaz ise ptr gsterici deikenine NULL adresi atanr. levin baarl olmas
durumunda A snfnn varsaylan kurucu ilevi her bir snf nesnesi iin bir kez, yani
toplam 10 kez arlr.
set_new_handler levi
new ilecinin baarsz olmas durumunda bad_alloc snf trnden bir hata nesnesi
gnderdii aklanmtr. Ancak hata nesnesinin gnderilmesinden nce programc
tarafndan gnderilen bir ilevin arlmas salanabilir. Bu amala set_new_handler ilevi
kullanlr.
set_new_handler ilevi, new ilecinin baarszl durumunda bir hata nesnesi
gnderilmesinden nce arlmas istenen ilevin adresini argman olarak alr. Yani ilevin
parametre deikeni bir ilev gstericisidir. levin geri dn deeri de daha nce
belirlenmi bir ilevin adresidir:
typedefvoid(*new_handler)();
new_handlerset_new_handler(new_handler);
Yukardaki bildirimleri inceleyelim:
new_handler geri dn deeri ve parametre deikeni olmayan bir ilev adresi trne
verilen typedef ismidir.
set_new_handler ilevinin parametre deikeni bu trdendir ve ilev de bu tre geri
dner.
set_new_handler ileviyle bir ilev belirlemesi yaplmaz ise, new ileci baarsz
olduunda hibir ilevi armaz dorudan bad_alloc snf trnden bir hata nesnesi
gnderir.
operator new ilevi dinamik blok elde etme ileminde baarsz olduunda, belirlenen
ilevi yalnzca bir kez armaz. Gereken bellek alann buluncaya kadar bir dng iinde
313/330
314/330
OKLU TRETME
Bir snf tek bir tretme ilemiyle birden fazla taban snftan tretmeye oklu tretme
(multiple inheritance) denir.
classA{
//
};
classB{
//
};
classC:publicA,publicB{
//
};
Yukardaki kod parasnda C snf, A ve B snflarndan oklu tretme yoluyla tretiliyor.
Szdizimde : atomundan sonra her bir taban snf isminin eriim belirteciyle birlikte yer
aldna dikkat edin. Eer tretme ilemi
classC:publicA,B{
//
};
biiminde yaplsayd, C snf, A snfndan public tretmesiyle ancak B snfndan private
tretmesiyle tretilmi olurdu.
Bir oklu tretme ilemine ilikin taban snflarn says hakknda bir kstlama yoktur.
Ancak uygulamalarn ounda bir snf iki ayr taban snftan public tretmesi ile tretilir.
oklu tretme ile elde edilmi tremi snf nesnesinin iinde her taban snfn elemanlar
da yer alr. Tretilmi nesnelerin bellekteki yerleimi standart olarak belirlenmemitir.
Ancak derleyicilerin ou tremi snf nesnesi iinde taban snf nesnelerini, nce
bildirilen taban snf nesnesi daha dk saysal adreste olacak biimde yerletirir:
Yukardaki gibi bir tretme ileminden sonra tremi snf olan C snf trnden bir nesne
tanmland zaman, nesnenin bellekte yerleimi aadaki gibi olabilir:
315/330
316/330
317/330
classMder:publicBase1,publicBase2{
public:
Mder(){std::cout<<"Mder::Mder()"<<std::endl;}
~Mder(){cout<<"Mder::~Mder()"<<endl;}
};
voidfunc()
{
Mdermder;
}
intmain()
{
func();
return0;
}
Taban snflarn varsaylan kurucu ilevlerinin deil de parametreli kurucu ilevlerinin
arlmas isteniyorsa bunun iin MIL szdizimi kullanlmaldr.
318/330
//Geersiz!iftanlamllkhatas
319/330
voidfoo()
{
Mdermd;
display(static_cast<Base1>(md));
display(static_cast<Base2>(md));
}
Elmas Oluumu
oklu tretmelerde taban snfn bazen iki kopyas bulunur. Bu ou kez istenmeyen bir
durumdur.
Aadaki rnei inceleyin:
classBase{
inta;
public:
voidfunc();
};
classDer1:publicBase{
public:
//...
};
classDer2:publicBase{
public:
//...
};
classMder:publicDer1,publicDer2{
public:
//...
};
Yukardaki rnekte Der1 ve Der2 snflar Base snfndan tretiliyor. Mder snf da oklu
tretme yoluyla Der1 ve Der2 snflarndan tretiliyor. Burada Mder snfndan bir nesne
tanmlanrsa nesnenin bellekteki grnts aadaki gibi olur:
320/330
//Geersiz!iftanlamllkhatas
md.func() ars geersizdir. nk hangi taban snftan alnan func ilevine eriildii belli
deildir. Ancak eriim iin znrlk ileci ile kullanldnda bir hata olumaz.
md.Der1::func();//Der1snfndanalnanfuncilevia?rlyor.
md.Der2::func();//Der2snfndanalnanfuncilevia?rlyor.
Mder snf trnden bir nesne hem Der1 hem de Der2 snf trnden nesne ise, normal
olarak Mder snf trnden bir nesnenin adresi Base snf trnden bir gstericiye
atanabilir. Ancak elmas oluumu sz konusu ise byle bir atama ift anlamllk hatasna
neden olur. Derleyici Mder snf trnden nesnenin iinde yer alan iki ayr Base snf
trnden nesnenin hangisinin adresini Base snfndan gsterici iine yerletirecei
konusunda seim yapamaz. Aadaki main ilevini inceleyin:
voidfunc()
{
Mdermder;
Base*base_ptr=&mder;//Geersiz!iftanlamllkhatas
}
Ancak tr dntrme ilemiyle, Mder snf nesnesi iinde yer alan Base snf trnden
nesnelerden hangisinin adresinin Base snf trnden gstericiye atanmas gerektii
derleyiciye aka bildirilebilir:
Base*base_ptr=static_cast<Base*>(static_cast<Der1*>(&mder));
Yukardaki ifade ile mder nesnesinin adresi nce static_cast tr dntrme ileciyle Der1
* trne dntrlyor. Sonra elde edilen Der1* trnden adres bu kez yine static_cast
ileciyle Base * trnden bir adrese dntrlerek base_ptr gstericisine atanyor.
Elmas oluumunun yol at bir sorun da udur: Taban snflardan herhangi birinin ye
ilevi arldnda bu ye ilevin ortak olan taban snfn elemanlar zerinde deiiklik
yaptn dnelim. Byle bir deiikliin yaplmasndan sonra, bu kez oklu tretilmi
snf nesnesiyle dier dorudan taban snf nesnesinin bir ye ilevi arldnda, yaplan
deiiklik etkisiz kalr. nk her dorudan taban snf kendi taban snf nesneleri
zerinde ilem yapmaktadr. Dorudan taban snflarn taban snflar tek deildir.
Aadaki rnei inceleyin:
321/330
return0;
}
Yukardaki rnekte main ilevi iinde MDer snf trnden bir nesne tanmlanyor. Bu
nesne ile MDer snfnn Der1 snfndan ald func isimli ye ilevi arlyor. Der1 snfnn
func ye ilevi Base snfnn eleman olan m_a deikeninin deerini deitiriyor. Daha
sonra bu kez MDer snfnn Der2 snfndan ald display ye ilevinin arldn
gryorsunuz. Bu ilev Der2 snfnn taban snf olan Base snfnn m_a elemannn
deerini alyor ve ekrana yazdryor.
mder.func(135);
ars ile m_a elemannn deeri 135 yaplmasna karn, daha sonra yaplan
mder.display();
arsyla m_a elemannn deeri olarak ekrana 0 deeri yazlr. nk MBase snfnn
dorudan taban snflar olan Der1 ve Der2 Base snf trnden ayr taban snf alt
nesneleri zerinde ilemler yapmaktadr.
322/330
#include<iostream>
classBase1{
public:
virtualvoidbase1_vfunc(){std::cout<<"Base1::base1_func()"<<
std::endl;}
};
classBase2{
public:
virtualvoidbase2_vfunc(){std::cout<<"Base2::base2_func()"<<
std::endl;}
};
classMder:publicBase1,publicBase2{
public:
virtualvoidbase1_vfunc(){std::cout<<"Mder::base1_func()"<<
std::endl;}
virtualvoidbase2_vfunc(){std::cout<<"Mder::base2_func()"<<
std::endl;}
virtualvoidmder_vfunc(){std::cout<<"Mder::mder_func()"<<
std::endl;}
};
intmain()
{
Mdermd;
Base1*base1_ptr=&md;
Base2*base2_ptr=&md;
base1_ptr>base1_vfunc();
base2_ptr>base2_vfunc();
return0;
}
Yukarda yer alan main ilevinde Mder snf trnden bir nesne olan md nesnesinin adresi
hem Base1 snf trnden gsterici olan base_ptr nesnesine hem de Base2 snf
trnden gsterici olan base2_ptr nesnesine atanyor.
Hem base1_ptr hem de base2_ptr ile yaplan sanal ilev arlar sonunda, arlan Mder
snfnn ilevleri olur.
Sanal Tretme
oklu tretmeye konu olan dorudan taban snflar, ortak bir taban snftan tretilerek
elde edilmilerse, oul tretilmi snf nesnesinin iinde iki ayr ortak taban snf nesnesi
yer alr. ou zaman istenmeyen bu duruma "Elmas oluumu" ismini vermitik.
Ortak olan taban snfn oklu tretilmi snfta yalnzca bir kez yer almas isteniyorsa
sanal tretme (virtual inheritance) denilen ara kullanlabilir. En son elde edilecek oklu
tretilmi nesne, yalnzca bir kez yer almas istenen taban snftan tretme yaplrken
virtual anahtar szcnn kullanlmasyla bu durum salanabilir. Aadaki kodu
derleyerek altrn:
323/330
324/330
B snfnn kurucu ilevi sz konusu olduunda en alttaki tremi snf B snf olur. Yani B
snfnn kurucu ilevi, ilk olarak A snfnn kurucu ilevini armaldr.
D snfnn kurucu ilevi sz konusu olduunda bu kez en alttaki tremi snf D snf olur.
Yani D snfnn kurucu ilevi ilk olarak A snfnn kurucu ilevini armaldr.
E snfnn kurucu ilevi sz konusu olduunda en alttaki tremi snf E snf olur. Yani E
snfnn kurucu ilevi ilk olarak A snfnn kurucu ilevini armaldr.
Sanal taban snfn kurucu ilevinin arlmas gerektii durumlarda bir sorun olumaz.
nk derleyici zaten en alttaki tremi snfn kurucu ilevinin koduna, sanal taban
snfn (rneimizde A snf) kurucu ilevinin arsn ekler. Ancak sanal taban snfn
parametreli bir kurucu ilevi arlacaksa, bu en alttaki tremi snfn kurucu ilevi iinde
M.I.L. szdizimi ile sanal taban snfn parametreli kurucu ilevinin arlmas ile olur.
Aadaki rnei derleyerek altrn:
#include<iostream>
classBase{
public:
Base(){std::cout<<"Base::Base()"<<std::endl;}
};
classDer1:virtualpublicBase{
public:
Der1(){std::cout<<"Der1::Der1()"<<std::endl;}
};
classDer2:virtualpublicBase{
public:
Der2(){std::cout<<"Der2::Der2()"<<std::endl;}
};
classMder1:publicDer1,publicDer2{
public:
Mder1(){std::cout<<"Mder1::Mder1()"<<std::endl;}
};
classMder2:publicMder1{
public:
Mder2(){std::cout<<"Mder2::Mder2()"<<std::endl;}
};
intmain()
{
Mder2mder2;
Mder1mder1;
Der1der1;
Der2der2;
return0;
}
Programc asndan bu durumun nemi udur: Tretme hiyerarisi iindeki herhangi
derinlikte bir snf, sanal taban snfn varsaylan kurucu ilevini deil de parametreli bir
kurucu ilevinin arlmasn isterse, sanal taban snfn kurucu ilevini M.I.L. szdizimiyle
armaldr. Aadaki rnei inceleyin:
325/330
326/330
A snfnn func isimli bir sanal ilevinin olduunu dnelim. Bu ilev hem B snfnda
hem de E snfnda ezilmise (override edilmise) bu durum hata oluturur. Yalnzca E
snfnda ya da yalnzca F snfnda ezilmise bu durum geerlidir.
C++n standart ktphanesinde giri k ilemlerini yapacak snflarda oklu tretme
uygulanmtr:
ios snfndan sanal tretme yoluyla istream ve ostream snflar tretilmi, istream ve
ostream snflarndan da oklu tretme ile iostream snf tretilmitir.
Bu durumda C++n standart iostream snf sisteminde ios taban snf alt nesnesinden
yalnzca bir tane bulunur.
Burada istream snf zerinde ilem yapldnda ve bu ilemler ios elemanlarn
deitirdiinde ostream bu deiiklikleri grr.
327/330
cout<<"ServerBase::nvfunc()const"<<endl;
//...
}
ServerBase::~ServerBase()
{
cout<<"ServerBase::~ServerBase()"<<endl;
//...
}
voidServerDer::vfunc()const
{
cout<<"ServerDer::vfunc()"<<endl;
//...
}
ServerDer::~ServerDer()
{
cout<<"Der3rd::~Der3rd()"<<endl;
}
voidglobal_func(constBase3rd&r)
{
r.vfunc();
r.nvfunc();
}
Snama amacyla nce ismi NewBase olan bir snf tanmlayalm:
328/330
classNewBase{
public:
virtualvoidvfunc()const=0;//safsanalilev
virtualvoidnvfunc()const=0;//safsanalilev
//yeniarayuzeiliskinilev
virtualvoidnew_vfunc()const=0;
virtual~NewBase(){cout<<"~NewBase()"<<endl;}
};
NewBase isimli snfta vfunc, nvfunc ve new_vfunc isimli ilevlerin saf sanal ilev olarak
bildirildiini gryorsunuz. imdi de ismi Join olan bir snf NewBase ve ServerBase
snflarndan oklu tretme yoluyla treteceiz:
classServerBase{
public:
virtualvoidvfunc()const;//sanalilev
voidnvfunc()const;//sanalolmayanilev(sanalolmasnistiyoruz)
~ServerBase();//sanalolmayansonlandrcilev(tasarmhatas)
};
classServerDer:publicServerBase{
public:
voidvfunc()const;//sanalileveziliyor
~ServerDer();
};
#include<iostream>
usingnamespacestd;
voidglobal_func1(constServerBase&);//okbiimliilemeyapanilev
//elimizdeolmayankodlamadosyas
voidServerBase::vfunc()const
{
cout<<"ServerBase::vfunc()const"<<endl;
//...
}
voidServerBase::nvfunc()const
{
cout<<"ServerBase::nvfunc()const"<<endl;
//...
}
ServerBase::~ServerBase()
{
cout<<"ServerBase::~ServerBase()"<<endl;
//...
}
voidServerDer::vfunc()const
{
cout<<"ServerDer::vfunc()"<<endl;
//...
}
ServerDer::~ServerDer()
{
329/330
voidglobal_func(constServerBase&r)
{
r.vfunc();
r.nvfunc();
}
classNewBase{
public:
virtualvoidvfunc()const=0;//safsanalilev
virtualvoidnvfunc()const=0;//safsanalilev
//yeniarayuzeiliskinilev
virtualvoidnew_vfunc()const=0;
virtual~NewBase(){cout<<"~NewBase()"<<endl;}
};
classJoin:publicNewBase,publicServerBase{
public:
voidvfunc()const{cout<<"Join::vfunc()"<<endl;
ServerBase::vfunc();}
voidnvfunc()const{cout<<"Join::nvfunc()"<<endl;
ServerBase::nvfunc();}
voidnew_vfunc()const{cout<<"Join::new_vfunc()"<<endl;}
~Join(){cout<<"Join::~Join()"<<endl;}
};
intmain()
{
Join&join_ref=*newJoin;
NewBase&nbase=join_ref;
nbase.vfunc();
cout<<"*******************************************"<<endl;
nbase.new_vfunc();
cout<<"*******************************************"<<endl;
global_func(join_ref);
cout<<"*******************************************"<<endl;
delete&nbase;
cout<<"*******************************************"<<endl;
return0;
}
330/330