You are on page 1of 311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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++ Kitabi (1/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

C++ Kitabi (2/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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

C++ Kitabi (3/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

C'de Geerli C++'da Geersiz Olan Durumlar 1. C'de anahtar szck olmayan baz szckler C++'da anahtar szcktr. Standart ANSI C dilinin (C89) 32 anahtar szc vardr. Bu szckler ayn zamanda C++ dilinin de anahtar szckleridir. Ancak C++ bunlara ek olarak yeni 42 anahtar szck daha tanmlamtr. Aadaki szckler C++'da anahtar szck iken C'de anahtar szck deildir: and/and_eq/asm /bitand /bitor /bool /catch /class /compl /const_cast /delete /dynamic_cast /explicit /export /false /friend /inline/ mutable /namespace/ new /not /not_eq /operator /or /or_eq /private /protected /public /reinterpret_cast/ static_cast /template /this /throw /true /try /typeid /typename /using /virtual /wchar_t /xor/ xor_eq Geerli bir C kodunda yukardaki szcklerden herhangi biri bir isim (identifier) olarak kullanlm olabilir. Ancak ayn kod C++'da derlendiinde geerli deildir: void foo() { int new; /***/ } Yukarda int trden new isimli bir deiken tanmlanyor. Tanmlama Cde geerli C++da geersizdir. 2. C'de geri dn deeri reten bir ilevin tanm iinde, return deyimi ile bir geri dn deeri retilmez ise bu durum derleyiciler tarafndan bir szdizim hatas olarak deerlendirilmez. Bu durumda arlan ilev bir p deere (garbage value) geri dner. phesiz bu durum bir programlama hatasdr. Oysa C++'da geri dn deeri reten bir ilevin tanm iinde mutlaka return deyimi yazlarak bir geri dn deeri retilmelidir. Eer bir return deyimi ile geri dn deeri retilmez ise kod geersizdir. int func() { } Yukardaki ilev tanm C'de geerli, C++'da geersizdir. C++'da geri dn deeri reten ilevlerin tanm iinde yaln return deyimi kullanlamaz: int func() { /***/ return; }

Yukardaki rnekte yer alan return deyimi C'de geerli ancak C++ geersizdir. phesiz yukarda return deyiminin kullanlma biimi bir programlama hatasdr. C derleyicilerinin hemen hemen hepsi bu durumu mantksal bir uyar iletisi ile bildirir. Ancak main ilevi C++'da bu kuraln dnda tutulur. main ilevi int trden bir geri dn deeri retecek ekilde tanmlanm olsa da, main ilevi iinde bir return deyimi yazlmayabilir. Bu durumda derleyici otomatik olarak main ilevinin ana blounun sonlanmasndan nce, kaynak kod iinde return 0; deyimi yazlm gibi kaynak kodu ele alr. Kod geerlidir.
C++ Kitabi (4/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Aadaki kod hem Cde hem de C++'da geerlidir. C++ derleyicileri iin mantksal bir uyar gerektirecek bir durum sz konusu deildir. int main() { } 3. C'de bir ilevin geri dn deerinin tr bilgisi yazlmaz ise, ilevin int trden deer dndrd anlalr (implicit int). Oysa C++'da ilev bildirimlerinde ve tanmlarnda geri dn deerinin trn yazmak zorunludur: foo(void); func(double x) { /***/ }

Yukarda foo ilevinin bildirimi Cde geerli C++da geersizdir. Yukarda func ilevinin tanm Cde geerli C++da geersizdir. 4. C'de bir ilevin tanmlanmasna ilikin iki ayr szdizim kural vardr. Bu szdizim kurallar eski biim (old style) ve yeni biim (new style) olarak isimlendirilir. Eski biim ilev tanmlamas programclar tarafndan artk hi tercih edilmemesine karn, C dilinin kurallarna gre halen geerlidir. Oysa C++ dili standartlarna gre eski biim ilev tanmlamas geerli deildir: int foo(a, b, c) int a; double b, c; { /***/ }

Yukardaki ilev tanm Cde geerli, C++da geersizdir. 5. C'de ilev bildirimi zorunlu deildir. C derleyicisi bildirimi yaplmayan bir ilevin arsyla karlatnda ilevin int trden geri dn deeri rettiini varsayarak kod retir. Oysa C++'da ilev bildirimi zorunludur: int main() { foo(); } Yukardaki rnekte foo ilevine yaplan ar C'de geerli C++'da geersizdir: Ancak bir ilevin bildirim yaplmadan arlmas C'de geerli olmasna karn doru kabul edilmez. 6. C'de ilev bildiriminde ilev parametre ayracnn iinin bo braklmas ile parametre ayracnn iine void anahtar szcnn yazlmas farkl anlam tar: int func(); void foo() { func(5); }

C++ Kitabi (5/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki rnekte func ilevi iin yaplan bildirimde parametre ayrac iine bir ey yazlmamtr. Cde bu bildirimin anlam udur: Bildirilen func ilevinin parametre deikenleri hakknda derleyiciye bir bilgi verilmemitir. Yani func ilevine yaplan arda derleyici ileve gnderilen argman says ile ilevin parametre deiken saysnn ayn olup olmadna bakmaz. Gemie doru uyumluluk iin bu bildirime zel bir anlam yklenmitir. Cde bu bildirim int func(void); biiminde yaplsayd, derleyiciye func ilevinin parametre deikeni olmad bilgisi aka verilmi olurdu. Oysa C++'da her iki bildirimin de anlam ayndr: int func(); int foo(void) C++'da ilev bildiriminde parametre ayracnn iinin bo braklmasyla ayracn iine void anahtar szcnn yazlmas arasnda bir fark yoktur. Her ikisinde de ilevin parametre deikenine sahip olmad bilgisi verilmi olur. 7. C de main ilevinin adresi alnabilir. main ilevi kendi kendini arabilir. Ancak C++'da bu duruma izin verilmemitir. C++'da main ilevi kendi kendini aramaz. main ilevinin adresi alnamaz. 8. C'de belirleyiciler (auto, register, static, extern, const, volatile), deiken tanmlamalarnda tr belirten anahtar szck olmakszn dorudan deiken ismiyle kullanlabilir. Bu durumda derleyici, tanmlanan deikenin int trden olduunu kabul eder. Ancak C++'da belirleyicilerin byle kullanlmas geerli deildir. void foo() { const i = 0; } Yukardaki kod Cde geerli C++da geersizdir. 9. C'de const bir deikenin ilk deer verilmeden tanmlanmas tamamen geerlidir. phesiz otomatik mrl bir nesne iin bu durum bir programlama hatasdr. C++'da const deikenlere ilk deer vermek zorunludur: void foo() { const int x; char *const ptr; /***/ }

Yukardaki tanmlamalar Cde geerli, C++da geersizdir. C++da statik mrl const deikenlere de ilk deer vermek zorunludur: const int x; void foo(){} Yukardaki rnekte x deikeninin tanm Cde geerli, C++da geersizdir. Ancak aadaki tanmlama iki dilde de geerlidir:
C++ Kitabi (6/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin const int *ptr; Burada ptr deikeninin kendisi const deildir. ptr deikeninin gsterdii yer const olarak ele alnr. 10. C'de void trden bir adresin void trden olmayan bir gsterici deikene, tr dntrme ilemi yaplmakszn atanmas geerlidir. Baz C derleyicileri bu durumda bir mantksal uyar iletisi verebilir. Ama bu durum bir szdizim hatas olarak deerlendirilmez. Ancak bu durum C++'da bir szdizim hatasdr. C++'da void trden adresler tr dntrme ileci kullanlmadan, baka trden bir gstericiye atanamaz: #include <stdlib.h> void foo() { int *ptr; ptr = malloc(sizeof(int)); /***/ } Yukardaki rnekte ptr deikenine yaplan atama Cde geerli, C++da geersizdir. 11. C'de bir gsterici deikene adres bilgisi dndaki bir deerin atanmas ya da bir gsterici deikene farkl trden bir adresin atanmas yanltr. C derleyicilerinin ou bu durumu bir mantksal uyar iletisiyle bildirir. Cde doal trler ile adres trleri arasnda otomatik tr dnm vardr. C++'da bir adres deeri olmayan bir ifadenin bir gsterici deikene atanmas ya da bir adresin farkl trden bir gsterici deikene atanmas durumu dorudan szdizim hatasdr. void foo() { int x = 10; double *dp; int *ip; dp = &x; ip = 20; /***/ } Yukardaki kod parasnda dp ve ip isimli gsterici deikenlere yaplan atamalar C++da geerli deildir. 12. C'de const bir deikenin adresinin, gsterdii yer const olmayan bir gstericiye atanmas yanltr. C derleyicilerinin ou byle bir durumda yalnzca bir mantksal uyar iletisi verme eilimindedir. C++'da bir const deikenin adresinin gsterdii yer const olmayan bir gstericiye tr dnm yaplmadan atanmas geersizdir. C++'da const nesnelerin adresleri ayr bir adres trnden kabul edilir. Byle adresler ancak gsterdii yer const olan gsterici deikenlere atanabilir: void foo() { const int x = 20; volatile int y = 50; int *ptr; ptr = &x; ptr = &y } Yukardaki kod parasnda ptr gstericisine yaplan atamalar C++'da geerli deildir.
C++ Kitabi (7/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Benzer ekilde C++da gsterdii yer const olan bir gstericinin deeri de, gsterdii yer const olmayan bir gstericiye atanamaz: void func(const char *ptr) { char *temp = ptr; /***/ } Yukardaki kod parasnda temp gstericisine yaplan atama C++'da geerli deildir. 13. C'de const ya da volatile bir nesnenin adresi void trden bir gstericiye atanabilir. C++'da void trden bir gstericiye const ya da volatile anahtar szc ile tanmlanm bir nesnenin adresi tr dnm yaplmadan atanamaz. void trden gstericilere her trden adres atanabilir. Ancak C++'da const ya da volatile anahtar szc ile tanmlanm bir nesnenin adresi tr dntrmesi yaplmakszn void trden bir gstericiye atanamaz. void foo() { void *vptr1, *vptr2; const int x = 10; volatile int v = 20; vptr1 = &x; vptr2 = &v; /***/ } Yukardaki kod Cde geerlidir. Ancak vptr1 ve vptr2 deikenlerine yaplan atamalar C++da geerli deildir. 14. C'de ilev tanmlarnda ya da bildirimlerinde, geri dn deerinin yazld yerde, parametre deikenlerinin bildirildii yerde programc tarafndan tanmlanan trler bildirilebilir. C++'da byle bildirimler geerli deildir. struct A{int a1, int a2;} foo(); Yukardaki bildirim C'de geerlidir. foo ilevinin geri dn deeri bildirilen struct A yaps trndendir. Ancak C++'da bu bildirim geersizdir. void func(struct B{int b1, int b2;} x); Yukardaki bildirim de C'de geerlidir. func ilevinin parametre deikeni, parametre ayrac iinde bildirilen struct B yaps trndendir. C++'da bu bildirim geersizdir. 15. C'de doal trlerden bir deerin bir numaralandrma trnden deikene atanmas geerlidir. C++'da bir numaralandrma trnden bir nesneye ancak sz konusu numaralandrma trne ilikin bir numaralandrma deimezi (enumaration constant) atanabilir. enum Position {ON, OFF, HOLD}; void foo() { enum Position pos = 1; /***/ } pos deikenine yaplan atama Cde geerli iken C++da geersizdir.

C++ Kitabi (8/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

16. C'de global bir deiken extern anahtar szc olmadan birden fazla kez bildirilebilir. C++ bir kaynak dosya iinde bir deiken extern anahtar szc kullanlmadan yalnzca bir kez bildirilebilir. int x; int x; void foo() { } Yukardaki kod C'de geerli iken C++'da geersizdir. 17. C'de programc tarafndan tanmlanan bir tr iin kullanlan etiket-isim (tag) ayn bilinirlik alan iinde kullanlan bir typedef ismiyle ayn olabilir. Bu durum bir szdizim hatas deildir. struct Word { char hb, lb; }; typedef int Word; Yukardaki kod paras C'de geerli, C++'da geersizdir. 18. C'de isel bir yap bildirimi ait olduu bilinirlik alannda dorudan grlebilir. C++'da isel bildirilen trler dorudan kullanlamaz: struct Outer { struct Inner { /***/ } /***/ } struct Inner i;

Yukardaki rnekte struct Inner trnn bildirimi struct Outer trnn bildirimi iinde yaplyor. struct Inner trnden i isimli deikenin tanm C'de geerlidir. nk bu tanmlama noktasnda struct Inner tr bilinir. Ancak yukardaki kod C++'da geersizdir. C++'da struct Inner tr dorudan deil Outer::Inner biiminde kullanlabilir. 19. C'de char trden bir diziye bir dizgeyle ilkdeer verilirken, dizge iinde yazlan karakterlerin says, tanmlanan dizinin belirtilen boyutuna eit olabilir. Bu durumda diziye yerletirilen yaznn sonunda sonlandrc karakter bulunmaz. C++'da byle bir ilkdeer verme ilemi geersizdir. void foo() { char message[4] = "hata"; /***/ } Yukardaki kod paras C'de geerli, C++'da geersizdir. 20. C++'da dizgeler bir ileme sokulduunda char trden adrese deil const char trden adrese dntrlr.

C++ Kitabi (9/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

C'de kaynak kod iinde bir dizge gren derleyici, nce bu dizge iin bellekte gvenilir bir yer ayarlar. Daha sonra bellekte ayarlanan yerin (bloun) balang adresini dizgenin yerine yerletirir. C'de gstericiler yoluyla bir dizgeyi deitirmeye almak, bir programlama hatas olmasna karn, bir szdizim hatas oluturmaz. Oysa C++'da dizgeler ileme sokulduklarnda const char trden bir adres olarak ele alnr. Ancak gemite yazlan ok sayda C++ kodunun geersiz hale gelmemesi iin const char trden birer adres olan dizgelerin, const olmayan char trden gstericilere atanmasna 1998 Standartlaryla izin verilmitir. Ancak bu durum "deprecated"(2) ilan edilmitir. Bu durumda 1998 C++ standartlarna gre aadaki kod geerlidir: char *p = "Necati Ergin"; Burada gsterdii yer const olmayan p gstericisine const bir adres atanmas geerlidir. Ama C++ derleyicilerinin ou bu durumda mantksal uyar iletisi verir. Ancak dizgeler herhangi bir ifade iinde, const olmayan char trden (char *) bir adres gereken yerde kullanlrsa C++ da bu geersizdir. void foo() { char *p = "Necati Ergin" + 1; char *str = x > 1 ? "Books" : "Book"; *"selam" = 'k'; }

Yukardaki deyimler C'de geerli C++'da geersizdir. Yazlacak yeni kodlarda artk dizgeler const char trden bir adres olarak grlmeli ve ancak okuma amal gsterici deikenlere, yani const char * trden deikenlere atanmaldr. 21. Cde bilinirlik alan iinde bir sramay salayan goto ve switch kontrol deyimleri bir tanmlama deyiminin bulunduu kod blgesinin sonrasna sramay salayabilir. C++da bu durum yasaklanmtr.

C++ Kitabi (10/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

C++'Da Geerli C'de Hatal Olan Durumlar


1. C89 standartlarna gre yerel her trl bildirim bloklarn banda yaplmak zorundadr. Yani bir blok iinde yaplan bildirimden nce yrtlebilir bir deyim (executable statement) bulunamaz. [C99 standartlarna gre yerel bir blok iinde her yerde bildirim yaplabilir.] C'de byle bir tasarmn yaplm olmasnn nedeni, programcnn yaplm olan bildirimin yerini kolayca bulabilmesini salamaktr. Oysa C++'da bildirimler ve tanmlamalar blok iinde herhangi bir yerde yaplabilir: void foo() { int y; y = 10; int x; /***/ }

Yukarda x deikeninin tanmlanmas Cde geersiz C++'da geerlidir. C++'da bir blok iinde yaplan bildirim ya da tanmlamann, bildirimin ilk kullanlaca yere yakn bir yerde yaplabilmesi, kaynak kodun okunabilirliini artrc bir etken olarak dnlmtr. Ancak asl nemli neden, programc tarafndan tanmlanan trlere ait deikenlere ilikindir. C++'da byle nesneler bir ok durumda tanmlanmalaryla birlikte dsal baz kaynaklar kullanmaya balar. Eer bu deikenler bloun en banda tanmlanmak zorunda olsalard, ilk kullanldklar yere kadar gereksiz bir biimde kaynak kullanmak zorunda kalrlard. Bir ismin derleyici tarafndan tannabildii kaynak kod aralna bilinirlik alan (scope) denir. C'de yerel deikenler blok bilinirlik alanna (block scope) uyar. Yani bildirilmi olduklar bloun bandan sonuna kadar herhangi bir yerde bilinirler. C++'da bildirimler blok balarnda yaplmak zorunda olmadndan, bilinirlik alan kuraln u biimde deitirmek gerekir: C++'da yerel deikenlerin bilinirlik alan bildirildikleri yerden ilgili bloun sonuna kadar olan kaynak kod blgesidir. C++'da da C'de olduu gibi, bir blok iinde ayn isimli birden fazla deikenin bildirimi yaplamaz. 2. C++'da bool tr doal bir veri trdr. C'de bool nceden tanmlanm bir veri tr deildir. (C99 standartlar ile C diline de bool tr eklenmitir.) void foo() { bool flag; /***/ } Yukardaki kod C++da geerli Cde geersizdir. C'de byle bir gereksinim duyulduunda baz aralar kullanlabilir. #define BOOL int typedef int BOOL enum BOOL {TRUE, FALSE} /* nilemci komutuyla */ /* typedef bildirimiyle */ /* numaralandrma tr olarak */

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 trlerden bool trne otomatik tr dnm vardr. bool trden nesneye atanan 0 d
C++ Kitabi (11/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

bir deerse true deerine 0 deeri ise false deerine dnm gerekleir. bool trden bir nesneye bir adres bilgisi de atanabilir. NULL adresi atama ncesi false deerine dntrlrken dier adres bilgileri true deerine dntrlrler. bool trden bir deer de, dier doal trlere atanabilir. Bu durumda atama ncesi true deeri int trden 1 deerine false deeri ise int trden 0 deerine dntrlr. C'de karlatrma ileleri ve mantksal ileler int trden 1 ya da 0 deerini retir. Oysa C++'da bu ileler bool trden true ya da false deerlerini retir. 3. C++'da deimez ifadesiyle ilkdeerini alan const deikenler deimez ifadesi gereken yerlerde kullanlabilir. C'de const deikenler, deimez ifadesi olarak kabul edilmez. const deikenleri ieren ifadeler deimez ifadesi olarak ele alnmaz. Ancak C++'da deimez ifadeleri ile ilkdeerini alan const deikenler, "deimez ifadesi" olarak kullanlabilir. void foo() { const int size = 10; int a[size]; /***/ }

Yukardaki kod C++da geerli Cde geersizdir. const nesne tanmlamasnda C ile C++ dilleri arasndaki en nemli fark, C++'da const anahtar szc ile tanmlanan deikenin simgesel deimez gibi ele alnmasdr. Yani C++'da const anahtar szc #define ileminin derleme modl tarafndan yaplan biimi gibidir. const bir deikenin kullanldn gren C++ derleyicisi, const deiken yerine ona verilen ilk deeri deimez olarak ileme sokar. 4. C++'da yap, birlik, numaralandrma etiketleri (tags) ayn zamanda tr ismi olarak kabul edilir. struct Complex { /*....*/ }; enum POS {OFF, ON}; Complex func(Complex, Complex); POS position;

Yukardaki rnekte func ilevinin bildirimi ve position deikeninin tanm C++da geerli Cde geersizdir. Cde bildirilen bu trlerin ismi struct Complex, enum Postur. Eer struct ve enum szckleri kullanlmadan tr bilgisinin ifade edilmesi istenirse, Cde bir typedef bildirimi yaplmaldr. 5. C++'da // atomuyla balayan yorum satr kullanm olana vardr. Bu yorum satr biimi C'de geerli deildir. C'de /* ve */ atomlar arasnda kalan kaynak kod blgeleri C derleyicileri tarafndan yorum satrlar olarak ele alnr ve derleme ilemine sokulmaz. x = func(); /* xe func ilevinin geri dn deeri atanyor */ /* ve */ atomlar arasnda kalan blgenin uzunluu bir satrdan daha byk olabilir.
C++ Kitabi (12/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

x = func(); /* x deikenine func ilevinin geri dn deeri atanyor. Ama gerek programlarda yorum satrlar ancak gereken durumlarda kullanlmal, yorum satrlarnn gereksiz bir biimde kullanlmas programlarn okunabilirliini zorlatrr */ C++'da bu tr yorum satrlar tamamen geerli olmakla birlikte, ikinci bir yorum satr biimi daha eklenmitir: C++'da // atomunun bulunduu noktadan satr sonuna kadar olan ksm derleme ilemine sokulmaz. Bu yorumlama biiminin zellikle ksa aklamalar iin daha kullanl olduu sylenebilir. (C++ bu yorum satrn BCPL dilinden almtr.) Ancak // yorum satr alt satrda devam edemez. Eer aklama ya da yorum bir satrdan daha uzunsa, her satrn bana tekrar // atomu yerletirilmelidir. C'de yorum satrnn kapatlmamas zor bulunan hatalara yol aabilir: int a, b; a = 10; b = 20; /* a nesnesine 10 deeri atand. /* b nesnesine 20 deeri atanmad! */

Yukardaki rnekte birinci yorum satr kapatlmad iin b = 20 atamas derleme ilemine sokulmaz. C++ biimi yorum satr tek bir atomla oluturulduu iin yukardaki yanlln yaplmas olasl yoktur: void foo() { int a, b; a = 10; b = 20; /***/ }

// a nesnesine 10 deeri atand. // b nesnesine 20 deeri atand.

Yukardaki kod C++'da geerli C'de geersizdir. Ancak C derleyicilerinin ou // yorum satrlarnn kullanmna izin verir. // yorum satrlar C99 standartlaryla C diline de eklenmitir. 6. C++'da statik mrl deikenlere ilkdeer verilirken deimez ifadesi kullanma zorunluluu yoktur. C'de statik mrl deikenlere deimez ifadeleriyle ilkdeer vermek zorunludur. int foo(); int x = 5; int y = x; void func() { static int z = foo(); /***/ }

Yukardaki rnekte global y deikenine ve statik yerel z deikenine, deimez ifadesi olmayan ifadelerle ilkdeer verilmitir. Bu deyimler C++'da geerli iken C'de geersizdir.

C++ Kitabi (13/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

7. C++'da for dng deyimi ayracnn birinci ksmnda, if, while, switch deyimlerinin ayrac iinde deiken tanmlanabilir. C'de yerel deikenler yalnzca blok balarnda ve ilev parametre ayralarnn iinde tanmlanabilir. C++'da for dnglerinin birinci ksmnda, while dnglerinin ayralar iinde ve if deyiminin ayrac iinde deiken tanmlama olana getirilmitir. #include <cstdio> int main() { for (int i = 0; i < 100; ++i) printf("%d\n", i); return 0; } Yukardaki for deyimi C++da geerli, Cde geersizdir. for dng deyimi ayracnn birinci ksmnda tanmlanan nesne herhangi bir trden olabilir. Burada virgl ileci kullanlarak ayn trden birden fazla nesne de tanmlanabilir: 1998 ISO standartlar ncesinde if, while ve switch ayralar iinde bildirim yaplmasna izin verilmiyordu. Ancak standartlar ile bu durumlar da mmkn klnd. Eski derleyicilerin bu deyimlerin ayralar iinde deiken tanmlamasna izin vermeyebileceini hatrlatalm. nk bu durum 1998 standartlar ile C++ diline eklenmitir. Peki for ayracnn birinci ksmnda tanmlanan deikenin bilinirlik alan nedir? Yukardaki programda, for dngsnn birinci ksmnda tanmlanan i deikeni nerelerde kullanlabilir? Bu tr deikenlerin bilinirlik alanna ilikin kural standartlar ile deitirilmitir. C++ standartlar ncesinde Baz derleyiciler halen bu kurala gre derlemektedir- bilinirlik alanna ilikin kural u ekildeydi: for ayrac iinde tanmlanan deiken, for dngsnn iinde bulunduu bloun sonuna kadar, yani kapanan kme ayracna kadar bilinir. Ancak 1998 standartlar, for ayrac iinde tanmlanan deikenlerin bilinirlik alann dng gvdesi ile (loop body) snrlamtr. Yani aadaki kod paras daha nceki kurallara gre geerliyken, C++ standartlarna gre geersizdir. #include <cstdio> int main() { for (int i = 0; i < 100; ++i) printf("%d\n", i); printf("i = %d\n", i); return 0; } Dng deikeninin bilinirlik alanna ilikin bu deiiklik tehlikeli bceklerin kayna olabilir. //Geersiz

C++ Kitabi (14/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <cstdio> int i = 0; int main() { for (int i = 0; i < 100; ++i) printf("%d\n", i); i = 100; // global deiken olan i'ye atama yaplyor. //... }

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 (int x = 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'de bu karakter ifteleri geerli deildir:

C++ Kitabi (15/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin %:include <stdio.h> void copy(char dest<::>, const char source<::>, size_t nbytes) <% while (nbytes--) *dest++ = *source++; %>

Yukardaki program C++'da geerli, C'de geersizdir. 10. C++da sonek konumundaki ++ ileci ile oluturulan ifade sol taraf deeridir. C'de byle ifadeler sol taraf deeri deildir: #include <stdio.h> int main() { int x = 5; ++x = 10; printf("x = %d\n", x); return 0; }

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> int main() { int x, y; x = y = 0; srand((unsigned int)time(0)); (rand() % 2 ? x : y) = 1; printf("x = %d\n", x); printf("y = %d\n", y); return 0; }

Yukardaki programda yaplan (rand() % 2 ? x : y) = 1; deyimi C++'da geerli, C'de geersizdir.


C++ Kitabi (16/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

12. C++'da virgl ileci ile oluturulmu bir ifade, virgl ilecinin sa terimi bir nesne ise bu nesneye karlk gelir. C'de virgl ileciyle yazlan bir ifade hi bir zaman sol taraf deeri deildir: #include <stdio.h> int main() { int x = 10; int y; (x, y) = 30; printf("x = %d\n", x); printf("y = %d\n", y); return 0; }

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. typedef double dollar; typedef double dollar; Yukardaki bildirimler C++'da geerli, C'de geersizdir. 14. C++'da tr dntrme ilecinin ilevsel biimi vardr. ilecin byle kullanm C'de geerli deildir. void foo() { double d = 4.5; int x; 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: void foo(); void func() { /***/ return foo(); } Yukardaki rnekte kullanlan return deyimi C++'da geerli, C'de geersizdir.
C++ Kitabi (17/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

16. C++'da bir ilev tanmnda parametre deikenine isim vermek zorunlu deildir: void func(int) { /***/ } Yukardaki rnekte func ilevinin tanm C++'da geerli C'de geersizdir. phesiz byle bir ilev tanm yapldnda isim verilmeyen parametreyi ilev iinde kullanmak mmkn deildir. Zaten C++'da da byle ilevlerin tanmlanmasnn nedeni "ilev yklemesi" ismi verilen arala ilgilidir. 17. C++'da dizilere, yap ya da birlik nesnelerine ilkdeer verilirken, deimez ifadeleri kullanmak zorunluluu yoktur. Ancak C'de bu durumlarda ilk deer verici ifade olarak yalnzca deimez ifadeleri kullanlabilir. void func(int x) { int a[3] = {x, x + 1, x + 2}; /***/ } Yukardaki rnekte a dizisinin tanm C++'da geerli, C'de geersizdir.

C++ Kitabi (18/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

C ile C++ Arasndaki Kural Farkllklar


1. C'de bir numaralandrma tr derleyici asndan signed int trnden farkl deildir. Bu yzden bir numaralandrma trnden nesnenin deeri <= INT_MAX >=INT_MIN (=ten sonra boluk var m yok mu) olabilir. BLUE bir numaralandrma deimezi olmak zere C'de sizeof(int) == sizeof(BLUE) ifadesi her zaman dorudur. Oysa C++'da, derleyici numaralandrma tr iin kullanaca tamsay trnn seiminde serbesttir. Numaralandrma tr iin signed int, unsigned int, signed long, unsigned long trlerinden herhangi biri seilebilir. Bir ok sistem sz konusu olduunda C++ da C'ye gre daha byk numaralandrma deimezi tanmlanabilir. (*bo satr*) C++ derleyicisi bir numaralandrma trnn bildirimini grdnde bildirmde kullanlan numaralandrma deimezlerinin deerine bakar. Eer tm deerler int tr say snrlarnda ise, numaralandrma tr olarak int tr seilir. Numaralandrma deimezlerinin deer(ler)inin signed int trnde tutulamamas durumunda srasyla unsigned int, signed long, unsigned long trlerinin uygunluu snanr. Yukardaki rnek sz konusu olduunda sizeof(int) == sizeof(BLUE) ifadesi doru olmak zorunda deildir.

2. C++'da karakter deimezlerinin char trnden olduu varsaylr. Karakter deimez ifadeleri char trdendir. C++da sizeof('A') == sizeof(char) == 1 olduu gvence altna alnmtr. Cde karakter deimezlerinin int trnden olduu varsaylr ve karakter deimez ifadeleri int trdendir. Cde sizeof('A') == sizeof(int) iken C++'da sizeof('A') == sizeof(char) ifadesi dorudur. 3. C'de const anahtar szc ile tanmlanm global nesneler dorudan d balantya (external linkage)sahiptir. Oysa C++'da const global nesneler i balantya sahiptir. Program oluturan baka modllerde bu nesnelere extern bildirimi ile eriilemez. Baka bir deyile C++da const global nesneler, static anahtar szc ile tanmlanmasalar da static anahtar szc ile tanmlanm gibi ele alnr. (*bo satr*) C++'da global const bir nesnenin d balantya sahip olmas isteniyorsa bu nesneler extern anahtar szc ile tanmlanmaldr: const int size = 10; extern const int value = 20; //size C++'da i balantya sahip. //value C++'da d balantya sahip.

C++ Kitabi (19/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki rnekte tanmlanan deikenlerden size i balantya sahipken value deikeni d balantya sahiptir. (*bo satr*) 4. C'de bir yapnn etiket ismi (structure tag) ait olduu yalanna katlmaz. Bir yapnn etiket ismi hi bir zaman daha geni bir bilinirlik alanndaki ayn ismi maskelemez. Ancak C++'da programc tarafndan tanmlanan trlere ilikin etiket isim, bildirimin yapld bilinirlik alanna katlr. int a[100]; void foo() { struct a{int i;} int n; printf("sizeof(a) = %d\n", sizeof(a)); }

Yukardaki rnekte sizeof(a) ifadesinde kullanlan a C'de global diziye ilikin iken C++'da foo ilevi iinde bildirilen a isimli yapya ilikindir.

C++ Kitabi (20/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

REFERANSLAR
Referanslar yazlmsal baz amalara salamak iin kullanlan dzeyi ykseltilmi gstericiler olarak dnlebilir.

Referanslar Nasl Tanmlanr


Nasl bir gsterici tanmlanrken gsterici isminin nnde '*' atomu kullanlyorsa, bir referans tanmlanrken de, referans isminden nce & atomu yazlr. rnein: int i = 20; double d = 10.2; int &ri = i; // ri int trden bir referanstr. double &rd = d;//rd double trden bir referanstr. Gsterici deikenlerden farkl olarak, bir referans ayn trden bir nesne ile ilkdeer verilerek tanmlanmaldr. Yukardaki rnekte, ri referans int trnden i nesnesi ile, rd referans ise double trnden d nesnesiyle ilkdeer verilerek tanmlanyor. Yukardaki tanmlamalarda kullanlan '&' bir ile deildir. Yalnzca tanmlanan nesnenin bir referans olduunu derleyiciye bildirir. Referansn ilkdeer verilmeden tanmlanmas geersizdir. Derleme zaman hatas olarak deerlendirilir. rnein: int &a; tanmlamas geersizdir. Bir referans ilk deer olarak ona atanan nesnenin yerine geer. int &r = b; gibi bir tanmlamadan sonra artk r nesnesi, grlebilir olduu her kaynak kod noktasnda b deikenin yerine geer, onun yerine kullanlabilir. Artk b deikenine ulamann bir baka yolu da, b ismini kullanmak yerine dorudan r ismini kullanmaktr. Bu durum yle de ifade edilebilir: r referans b deikenine balanmtr.

Tanmlamadan Sonra Referansn Kullanlmas


Tanmlama ileminden sonra referans deikeni kullanlrsa, referansn baland deiken kullanlm olur: #include <iostream> int main() { int a = 10; int &r = a; r = 20; std::cout << "a = " << a << std::endl; return 0; }

Yukardaki main ilevini inceleyelim: r referans daha nce tanmlanan a deikenine balanyor. Bylece r referans a deikeninin yerine geiyor. Daha sonra r referansna yaplan atama, aslnda a deikenine yaplm olur.

C++ Kitabi (21/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bu ilemler referans deil de gsterici deiken kullanlarak yaplm olsayd, aadaki gibi bir kod yazlabilirdi: #include <iostream> int main() { int a = 10; int *ptr = &a; *ptr = 20; std::cout << "a = " << a << std::endl; return 0; }

Gerekten de derleyiciler her iki program iin de ayn makine kodlarn retir. lk deerini alan bir referans, yani bir nesnenin yerine geen bir referans, bir ilecin terimi olduunda, ile referans deil, referansn yerine getii nesneyi ileme sokar: #include <iostream> using namespace std; int main() { int a = 10; int &r = a; cout << "a = " r += 2; cout << "a = " r = -r; cout << "a = " return 0; } << a << endl; << a << endl; << a << endl;

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> using namespace std; int main() { int a = 10; int *ptr = &a; cout << "a = " *ptr += 2; cout << "a = " << a << endl; << a << endl;
C++ Kitabi (22/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin *ptr = -*ptr; cout << "a = " return 0; }

<< a << endl;

Tanmlanm Bir Referans Bir Baka Nesnenin Yerine Geemez


Bir referans, tanmlanmasyla birlikte bir nesnenin yerine geer. Artk sz konusu referans, bilinirlik alan iinde hep ayn nesnenin yerine geer. Referansn daha sonra bir baka nesneye balanmas mmkn deildir: #include <iostream> int main() { int a = 10; int b = 20; int &r = a; r = b; std::cout << "a = " return 0; }

<< a << std::endl;

Yukardaki main ilevinde tanmlanan r referans, a deikeninin yerine geiyor. Daha sonra yaplan r = b; atamas, r referansnn b deikeninin yerine getii anlamna gelmez. Bu atamann anlam udur: r referansnn baland nesneye b deikeninin deeri atanmaktadr. Derleyicilerin rettii kod asndan bu durum u ekilde de deerlendirilebilir: Referanslar kendisi const olan gstericilere karlk gelir. Yukardaki kodun bir referans ile deil de bir gsteri deiken kullanlarak yazldn dnelim: #include <iostream> int main() { int a = 10; int b = 20; int *const ptr = &a; *ptr = b; std::cout << "a = " << a << std::endl; ptr = &b; //Geersiz return 0; }

Yukardaki rnekte ptr, kendisi const olan bir gsterici deikendir. ptr deikeninin tanmndan sonra, ptr'ye artk baka bir nesnenin adresi atanamaz, deil mi?

Referanslarn const Olarak Bildirilmesi


Bir referans const anahtar szc ile tanmlanabilir. const anahtar szc & atomundan nce yazlr. Bu ekilde tanmlanm bir referans deikeninin yerine getii nesne, referans yoluyla deitirilemez. rnein:

C++ Kitabi (23/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin int main() { int a = 10; const int &r = a; // Geersiz! r = 20; return 0; }

main ilevi iinde yer alan r = 20 ifadesi geerli deildir. Atama aslnda a deikenine yaplr. const referans kullanlarak, referansn yerine getii nesne deitirilemez. Ancak tabii, const referans sa taraf deeri olarak ilemlere sokulabilir. Yukardaki program parasnn gstericilerle oluturulan edeer C karl yle olur: int main() { int a = 10; const int *const ptr = &a; *ptr = 20; return 0; } //Geersiz

Burada ptr, hem kendisi const hem de gsterdii yer const olan bir gsterici deikendir. ptrnin kendisine yaplan atamalar geersiz olduu gibi ptr'nin gsterdii nesneye yaplan atamalar da geersizdir. Peki const anahtar szc & atomundan sonra, referansn isminden nce kullanabilir mi? Gsterici isminden nce * atomundan sonra const anahtar szcn kullanldnda, bu durum gstericinin kendisinin const olduu anlamna geliyordu. int x; int *const ptr = &x; int &const r = x;

//Geersiz!

Yukardaki r isimli referansn tanm geersizdir. nk referanslar zaten tanmlar gerei yalnzca belirli bir nesnenin yerine gemek zere yaratlr. Yani bir referans, zaten bir nesne ile ilkdeerini aldktan sonra artk baka bir nesnenin yerine geemez. Referanslar bu anlamlaryla zaten kendileri const nesnedir. Dolaysyla, const anahtar szcnn yukardaki biimde kullanlmasna gerek yoktur. const bir nesne adresinin, ancak gsterdii yer const olan bir gstericiye atanabileceini anmsayn: const int x = 10; //Geersiz int *p1 = &x; //Geerli const int *p2 = &x; Benzer ekilde, const olmayan bir referans const bir nesnenin yerine geemez. const bir nesnenin yerine ancak const bir referans geebilir.

C++ Kitabi (24/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin const int x = 10; int &r1 = x; //Geersiz! const int &r2 = x; //Geerli

const Referanslar Neden Kullanlr


Gsterdii yer const olan gstericiler ne amala kullanlyorlarsa, const referanslar da ayn amala kullanlr. const referanslar ile bunlarn balandklar nesneler deitirilemez. Byle referanslar, yalnzca okuma amacyla nesnelerin yerine geer.

Referanslarn Parametre Deikeni Olarak Kullanlmas


C++da referanslar genellikle ilev tanmlamalarnda parametre deikeni olarak kullanlr. Parametre deikeni referans olan ilevler, ayn trden bir nesne ile arlmaldr. Byle bir ar sonucunda parametre deikeni olan referans argman olan nesnenin yerine geer: #include <iostream> void func(int &r) { r = 20; } int main() { int a = 10; func(a); std::cout << a << std::endl; return 0; }

Parametre deikenlerinin ilev arldnda otomatik olarak yaratldn hatrlayalm. rnein func isimli ileve func(a); biiminde yaplan ar ile aslnda a deikeni, func isimli ilevin referans parametresine ilkdeer olarak atanr. Yani ileve yaplan ar ile parametre deikeni olan r referans a deikeninin yerine geer. func ilevine gnderilen a deikeninin kendisidir. Aslnda derleyicinin rettii kod sz konusu olduunda, gerekte bir adres aktarm yaplmaktadr. Parametresi referans olan bir ilevin bir adres bilgisiyle deil, bir nesnenin kendisiyle arldna dikkat edin. ar bu biimde yaplsa da aslnda bir adres aktarm sz konusu olur. Aadaki rnei inceleyin: #include <iostream> using namespace std; void swap(int &a, int &b) { int temp = a; a = b; b = temp; }

int main()
C++ Kitabi (25/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin { int x = int y = cout << cout << swap(x, 10; 20; "x = " << x << endl; "y = " << y << endl; y);

cout << "x = " << x << endl; cout << "y = " << y << endl; return 0; }

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> using namespace std; void swap(int *a, int *b) { int temp; temp = *a; *a = *b; *b = temp; } int main() { int x = 10; int y = 20; cout << "x = " cout << "y = " swap(&x, &y); cout << "x = " cout << "y = " return 0; } << x << endl; << y << endl; << x << endl; << y << endl;

const Referans Parametreli levler


Bir ilevin parametresi const referans da olabilir. Byle bir ilev bir nesne zerinde deiiklik yapmayan, yalnzca o nesnenin deerini kullanan bir ilevdir. void access(const T &r); Yukarda bildirimde yer alan T isminin, bir tr bilgisi olduunu dnelim. access isimli ilev, T trnden bir nesne zerinde ilem yapabilir, ama sz konusu nesneyi deitiremez. access ilevi T trnden bir nesnenin yalnzca deerinden faydalanr. void mutate(T &r);

C++ Kitabi (26/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

mutate isimli ilev de T trnden bir nesne zerinde ilem yapar ama sz konusu nesneyi deitirebilir. Szdizim kurallar asndan bakldnda phesiz mutate ilevinin kendisine gelen T trnden nesneyi deitirmesi konusunda bir zorunluluk yoktur. Ancak byle bir ilevin kendisine gelen nesneyi deitirecei kabul edilmelidir. Eer nesneyi deitirmeseydi ilevin parametresi const olarak seilirdi. Bu durumda unu da syleyebiliriz: Bir ilev referans yoluyla adresini ald nesne zerinde bir deiiklik yapmayacak ise, ilevin parametresi const referans yaplmaldr. Bir ilevin parametresi const olmayan bir referans ise ve ilev ald nesne zerinde deiiklik yapmyor ise, bu durum bir programlama hatas olarak deerlendirmelidir. levin arayzn yani bildirimini gren programclar, ilevin gnderilen nesne zerinde deiiklik yapacan dnrler.

Yap Nesnelerinin Referans Yoluyla levlere Geirilmesi


Bir yap nesnesinin bir ileve iki ekilde geirilebileceini biliyorsunuz. 1. Yap nesnesinin deerinin geirilmesi durumu (call by value). Bu durumda ilevin parametre deikeni bir yap deikenidir. lev baka bir yap deikenin kendisi ile arlr. Ayn trden iki yap deikeni birbirine atanabildiine gre, bu ar biimi de geerlidir. Aadaki rnei inceleyin: #include <iostream> struct Person { char name[30]; int no; }; void display_person(Person y) { std::cout << y.name << '\n' << } int main() { Person per = {"Necati Ergin", display_person(per); return 0; }

y.no << std::endl;

123};

Bu tr bir aktarmda, yapnn karlkl elemanlar birbirlerine blok olarak kopyaland iin greli bir zaman kayb sz konusudur. Bu nedenle bu tr aktarm biimi C dilinde kt bir teknik olarak kabul edilir ve pek kullanlmaz. Tabii ok kk yap nesnelerinin bu biimde aktarlmas sz konusu olabilir. lev deerle arld iin, yani ilev ar ifadesindeki argman olan yap nesnesi ilevin parametre deikenine kopyalanarak aktarld iin, bu yolla argman olan yap nesnesinin ilev tarafndan deitirilebilmesi olas deildir. 2. Yap nesnesinin adresinin ileve geirilmesi durumu (call by reference). Bu durumda ilev yap deikenin adresi ile arlr. levin parametre deikeni de ayn trden bir yap gstericisi olur. Bu aktarm biiminde yap nesnesi ne kadar byk olursa olsun gerekte aktarlan tek bir adres bilgisidir. lev tanm iinde adresi alnan yap nesnesinin elemanlarna erimek iin parametre deikeni olan gsterici -> ilecinin terimi olur. Yap deikenleri ounlukla ilevlere bu biimde geirilir. Aadaki rnei inceleyin:

C++ Kitabi (27/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void display_person(const Person *ptr) { std::cout << ptr->name << '\n' << } int main() { Person per = {"Necati Ergin", display_person(&per); return 0; }

ptr->no << std::endl;

123};

Madem C++ dilinde referanslar bir eit gstericidir, o halde yaplarn da verimli bir biimde referans yntemiyle aktarmlar da sz konusu olabilir. Byle bir aktarm biiminde, ilev yap nesnesinin kendisiyle arlr. levin parametre deikeni ayn yap trnden bir referans olur. Byle bir arda ilevin parametre deikeni olan referans, ileve argman olarak gnderilen yap nesnesinin yerine geer. Peki ilev iinde yap nesnesinin elemanlarna nasl eriilir? Nokta ileci ile mi, ok ileci ile mi? Evet nokta ileci ile. nk artk yap referans kullanldnda, referans dardan gnderilen nesnenin yerine geeceine gre parametre olan referans dardan gnderilen yap nesnesinin yerine geer. Yani referansn kullanm bir adres belirtmez. O nedenle yap referans ile yap elemanlarna eriimde nokta ileci kullanlr. Aadaki rnei inceleyin: #include <iostream> using namespace std; struct Person { char name[30]; int no; }; void display_person(const Person &r) { cout << } int main() { Person per = {"Necati Ergin", display_person(per); return 0; } r.name << '\n' << r.no << endl;

123};

levin Parametre Deikeni Gsterici mi Referans m Olmal


C++ dilinde argman aktarmnda gsterici mi yoksa referans m kullanlmaldr? Nesne ynelimli programlama sz konusu olduunda, gsterici kullanmak yerine mmkn olduu kadar referans kullanmak gerektii sylenebilir. Bir ok programc u ilkeyi benimser: "Kullanabildiin her yerde referans kullan, ancak zorunlu olduun yerde gsterici kullan!" (use references wherever you can, use pointers when you have to!). Ancak C++ dili yalnzca nesne ynelimli programlama teknii ile program yazmak iin kullanlmak zorunda deildir. C++ ile phesiz "prosedrel" programlama teknii kullanlarak da program yazlabilir. Eer amacnz C++ daha iyi bir C olarak kullanmak ve prosedrel programlama teknii ile program yazmak ise, makina dzeyinde olanlar daha iyi betimledii iin gsterici kullanmay tercih edebilirsiniz.
C++ Kitabi (28/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

C++da, a bir nesne olmak zere aadaki gibi bir ilev arsnn yapldn dnelim: func(a); Argman olan ifadede a nesnesinin ismi yazlmtr. Bu durumda iki olaslk sz konusudur: 1. levin parametre deikeni ayn trden bir nesnedir. Bu durumda derleyici argman olan nesnenin deerini parametre deikenine kopyalayacak bir kod retir. rnein: void func(int val) { val = 20; } int main() { int a = 5; func(a); return 0; } Yukardaki rnekte a deikeninin deeri, ilev arldnda yaratlan, parametre deikeni vale kopyalanr. Bu yzden func isimli ilevde yaplan val = 20; atamasnn main ilevinin as ile bir ilgisi yoktur. Atama func ilevinin parametresi olan val deikenine yaplr. func deerle arlan (call by value) bir ilevdir. 2. levin parametre deikeni ayn trden bir referanstr. Bu durumda gizli bir adres aktarm sz konusudur. Yani derleyici, ar ifadesinde argman olarak kullanlan nesnenin adresini arlan ilevin referans parametresine kopyalayacak bir kod retir. #include <iostream> using namespace std; void func(int &r) { r = 20; } int main() { int a = 5; func(a); cout << "a = " << a << endl; return 0; }

r = 20 atamasyla gerekte main ilevi iinde tanmlanan a deikeni deitirilir. func isimli ilevin iinde kullanlan r a nesnesinin yerine geer. Bu durumu, a nesnesinin kendisinin func ilevine gnderilmesi olarak grebilirsiniz. C++da bir ilevin
C++ Kitabi (29/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

func(a); biiminde arldn gren programc, eer ilevin tanmn ya da bildirimini grmemise standart Cdeki alkanlkla ilevin a deikenini deitiremeyeceini dnerek yanl bir fikir edinebilir. Nesnenin deerinin mi, yoksa adresinin mi kopyalanacann net olarak bilinmemesi baz durumlarda okunabilirlii azaltabilir. Bu durumu engellemek iin referans kullanmna dikkat edilmelidir. Ancak Nesne Ynelimli Programlama Teknii (NYPT), programn programlama dilinin alma dzleminde deil de, problemin kendi dzleminde yazlmasn hedefler. Bu durumda nesne ynelimli olarak yazlm bir programda, program okuyan a nesnesinin deerinin mi adresinin mi ileve gnderildiini merak etmez, func ilevinin problem dzlemindeki anlamyla ilgilenir. Yani C dilinde okunabilirlik asndan bir eksiklik olarak grnen bu durum C++ dilinde bir eit veri saklama" (information hiding) olarak grlr. C programclarnn C++a geite karlatklar temel zorluklardan biri budur. C dili ile programlamada, genel ilke ortada gizliliin bulunmamasdr. Ancak C++ dilinin NYPT iin kullanlmasnda durum byle deildir. nk ana ama problemin kendi dzlemine yaklamaktr. Program yazan, program hakknda dnrken programlama dilinin aralar ile deil de, programn yazlma amac olan problemin paralar ile dnr. Parametrenin deitirilmeyecei durumlarda referans kullanlabileceinden sz ettik. Madem byle bir deitirme sz konusu deil, o halde okunabilirlii artrmak amacyla referans da const olarak tanmlanmaldr.

Referanslar ile Gstericilerin Benzerlikleri ve Farkllklar


Hem gsterici deikenler hem de referanslar adres bilgileri tutan nesnelerdir. Ancak gsterici deikenler kullanldnda ilerindeki adres bilgisi ele alnd halde, referans kullanldnda artk referansn baland nesne ele alnr. Aadaki kodlar inceleyelim: #include <iostream> int main() { int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int *p = &a[0]; //int *p = a; *p = 10; ++p; *p = 20; std::cout << a[0] << " " << a[1]; return 0; }

imdi de aadaki kodu inceleyelim: #include <iostream> using namespace std; int main() { int a[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];
C++ Kitabi (30/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin return 0; }

Referanslar tek bir elemann adresine ilikindir. Bir referansa adres ilkdeer verilirken yerletirilir. Daha sonra bu adres bilgisi deitirilemez. rnein: int int int r = a = 10; x = 20; &r = a; x;

ilemleri yapldnda, x deeri referansa deil, referansn yerine getii nesneye yani aya aktarlr. Referanslar arka planda kendisi const olan gsterici deikenlere karlk gelir. Oysa bir gsterici deiken eer kendisi const deilse program iinde dinamik olarak farkl nesneleri gsterebilir. Bir diziyle ilgili genel ilem yapan ilev tanmlanabiliyordu. Byle ilevlere dizinin balang adresi ve boyutu argman olarak gnderiliyordu. Dizinin balang adresini ileve gndermek iin gsterici kullanyordu. Peki, byle bir ilevin parametresi bir referans olabilir mi? Hayr! referanslarla bu i gstericilerle olduu gibi yaplamaz. Ancak, rnein 10 elemanl int trden bir diziyi gsteren gsterici olduu gibi 10 elemanl int trden bir dizinin yerine geecek bir referans da tanmlanabilir. Aadaki kodu inceleyin: #include <iostream> using namespace std; void display(int(&r)[10]) { int k; for (k = 0; k < 10; ++k) cout << r[k] << " "; cout << endl; } int main() { int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; display(a); return 0; }

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:

C++ Kitabi (31/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

#include <iostream> int main() { int x = 10; int *ptr = &x; int *&r = ptr; *r = 20; std::cout << "x = " << x << std::endl; return 0; } lev gstericileri olduu gibi ilev referanslar da olabilir. lev referanslarn ileride ayrntl olarak inceleyeceiz.

NULL Adresi ve Referanslar


Deeri NULL adresi olan bir gsterici deiken hibir yeri gstermeyen bir gsterici deikendir. NULL adresinin Cde ne kadar yaygn bir biimde kullanldn biliyorsunuz: Bir ilev bir adrese geri dndnde baarszlk belirtmek amacyla NULL adresine geri dnebilir. rnein standart bir C ilevi olan strchr ilevinde bir yaz iinde bir karakter arar. Aranan karakteri yazda bulursa, bulduu yerin adresi ile bulamazsa NULL adresi ile geri dner. Dardan adres alan bir ileve zel bir bilgi iletmek amacyla NULL adresi geilebilir. rnein standart time ilevi kendisine NULL adresi gnderilirse takvim zaman deerini yani time_t trnden deeri bir adrese yazmaz yalnzca geri dn deeri olarak retir. NULL adresi bir gsterici iin bir bayrak deeri olarak kullanlabilir. rnein bir kontrol deyiminde bir gstericinin deerinin NULL adresi olup olmamasna gre farkl iler yaplabilir. Oysa hi bir nesnenin yerine gemeyen bir referans tanmlanamaz. Bir referans tanmland zaman bir nesnenin yerine gemelidir. C++da referanslarn eklenmesiyle gstericilere olan gereksinim tamamen ortadan kalkmamtr. Ancak zellikle "Nesne Ynelimli Programlama Teknii"nin kolay ve doal bir biimde uygulanmas referans kullanmn gerektirir.

Referanslar zerinde Adres lemleri


Bir referansn adres ileci ile adresi alnabilir. Bu durumda referansn yerine getii nesnenin adres deeri elde edilir. rnein: #include <iostream> int main() { int a = 10; int &r = a; int *p = &r; *p = 20; std::cout << "a = " << a << std::endl; return 0; }

ilemlerini inceleyin. Burada: p = &r;

C++ Kitabi (32/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

deyiminde referans adres ilecinin terimi olmutur. Bu durumda referansn yerine getii nesnenin adresi deeri elde edilir. rneimizde bu adres aslnda a deikeninin adresidir. Elde edilen adresin referans tr ile ayn trden olduuna dikkat edin. Nihayet, *p = 20; ile aslnda a nesnesine 20 deeri atanr. Yukardaki program parasnn gsterici deikenlerle oluturulmu edeer C karl da yledir: #include <iostream> int main() { int a = 10; int *p = &a; int *ptr = &*p; *ptr = 20; std::cout << "a = " << a << std::endl; return 0; }

Referanslara Farkl Trden Bir Nesne ile lkdeer Verilmesi Durumu


Bir referansn ayn trden bir deikenle ilkdeer verilerek tanmlanmas gerektiini belirtmitik. Referansns farkl trden bir deikenle ilkdeer verilerek tanmlanmas geersizdir. Aadaki rnei inceleyin: void func { double d = 10.5; int &r = d; //Geersiz //... } Ancak const bir referansa baka trden bir nesne ile ilk deer verilmesi geerlidir: void func() { double d = 10.5; const int &r = d; //... }

// 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: int main() { double d = 10.5; int temp = (int)d; const int &r = temp; return 0; }

C++ Kitabi (33/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Referanslara Deimezlerle lkdeer Verilmesi


Bir referansa bir deimez ile ilk deer verilmesi de geersizdir. Ancak bir const referansa bir deimez ile ilk deer verilebilir: int &r = 10; const int &r = 10; // Geersiz! // Geerli

Bu durumda derleyici nce geici bir nesne yaratr. Geici nesneye deimezi atar, referans geici nesneye balayan bir kod retir. Yukardaki atamada aslnda geri planda unlar yaplr: int temp = 10; // geici nesne 10 deeri ile yaratlyor. const int &r = temp; //r referans geici nesneye balanyor.

Referansa Geri Dnen levler


levlerin geri dn deerlerinin derleyici tarafndan nce geici bir blgeye alndn, buradan ekilerek kullanldn anmsayn. rnein: x = func(); gibi bir arda, nce func isimli ilev arlr. levin geri dn deeri geici bir nesnede saklanr. Daha sonra bu geici nesneden ekilerek kullanlr: Bunu kaba kod olarak aadaki biimde gsterebiliriz: temp = return ifadesi; x = temp; Aslnda ilevin geri dn deerinin tr, bu geici nesnenin trn belirtir. double func() { // return ifade; } rnein yukardaki func isimli ilevin geri dn deerinin yerletirilecei geici blge double trdendir. Yani aslnda return deyimi ile bu geici nesneye ilkdeer verilmesi sz konusudur. levin geri dn deerinin referans olmas geici blgenin referans olmas anlamna gelir. Bu durumda return ifadesi bir referansa ilk deerini verir, deil mi? Aadaki rnei inceleyin: #include <iostream> int x = 10; int &func() { return x; } int main() { func() = 20; std::cout << "x = " << x << std::endl; return 0; } Burada geici blge int trden bir referanstr. Yani aslnda return deyimiyle yaplan:
C++ Kitabi (34/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

int &temp = x; gibi bir ilemdir. Bu durumda geici nesne olan referans x nesnesine balanr. Bylece ilev ar ifadesi artk bir nesne belirtir duruma gelmitir. Bu durumda func() = 20; atamas gerekte global x deikenine yaplm olur. Geri dn deeri bir referans olan ilevlerin, kendilerini aran kod parasna, dorudan bir nesnenin kendisini ilettiini dnebilirsiniz. Byle ilevlere yaplan ar ifadeleri nesne belirtir. Yani sol taraf deeri olarak kullanlabilirler. Geri dn deeri referans olan ilevlerin ar ifadeleri return ifadesi ile belirtilen nesne anlamna gelir. Byle ilevlerin return ifadelerinin de ayn trden bir nesne belirtmelidir. Yukardaki programn gsterici deikenlerle oluturulmu karl aadaki gibi olur: #include <iostream> int x = 10; int *func() { return &x; } int main() { *func() = 20; std::cout << "x = " << x << std::endl; return 0; }

Her iki program iin retilecek makina kodlar tamamen edeerdir. Geri dn deeri referans olan ilevlerin aslnda adresle geri dndn ama bu adres yoluyla ieriklerinin alnarak kullanldn grebiliyor musunuz? Yukardaki programda *func() = 20; deyimini inceleyelim. Bu ifadede ile vardr. nce ilev ar ileci ele alnr, func ilevi arlr. Bu ilev bir adres deeri dndrr. Daha sonra * ileciyle bu adresteki nesneye eriilir. Eriilen nesne x nesnesinin kendisidir. Nihayet atama ileciyle 20 deeri ilevin adresini dndrd nesneye, yani x deikenine atanr.

Referansa Geri Dnen lev Yerel Nesne le Geri Dnmemeli


Yukardaki rnekte x deikeni yerel olabilir miydi? Referansa geri dnen bir ilevin yerel bir nesne ile geri dnmesi, adrese geri dnen bir ilevin yerel bir deikenin adresi ile dnmesine edeer bir yanllktr. Yani bir gsterici hatasdr. levin geri dn deeri ile kendisine aran kod parasna ilettii yerel nesnenin mr, ilevin kodunun yrtlmesinin tamamlanmasyla sona erer. Ancak bu durum derleme zamannda bir hata oluumuna neden olmaz. Derleyicilerin ou bu durumu mantksal bir uyar iletisi ile bildirir.

C++ Kitabi (35/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Referanslar Neden Kullanlr


Referanslar temel olarak adres ilemlerinin daha yaln bir yazm biimiyle ifade edilmesi amacyla kullanlr. rnein bir ilevin parametre deikeni gsterici yerine referans olsa, ilev iinde adres yardmyla nesneye eriilecei zaman * ya da -> ilecini kullanmak gerekmez. Bylece ifadeler daha yaln bir biimde yazlabilir. & ya da * ilecinin kullanlmas programn yazmn programlama dilinin dzlemine yaklatrrken, problemin kendi dzleminden uzaklatrc bir etki yapar. Adres ya da ierik ilecinin kullanlmamas retilen makina kodunda bir klme yaratmaz. nk derleyici referans yoluyla yaplan eriimleri tpk gstericilerde olduu gibi yine adres ilemleriyle gerekletirir. Bunun dnda C++ dilinde nesne ynelimli programlama tekniinin uygulanabilmesi iin eitli biimlerde referans trne gereksinim duyulmutur. Yani referanslar C++da ileride greceimiz pek ok konunun uygulamas iin gerekmektedir.

C++ Kitabi (36/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

PARAMETRE DEKENLERNE VARSAYILAN DEERLERN AKTARILMASI


Cde bir ilevin ka tane parametre deikeni varsa ileve o kadar argman geilmelidir. Oysa C++da bir ilev, parametre deikeni saysndan daha az sayda argmanla arlabilir. Bir ilevin bir ya da birden fazla parametre deikeni varsaylan argman (default argument) alabilir. Bunun anlam udur: lev ars ile ilevin parametre deikenine bir deer aktarlmaz ise otomatik olarak daha nceden belirlenmi bir deer aktarlr. Bir parametre deikeninin nceden belirlenmi bir deer alaca ilevin bildiriminde ya da ilevin tanmnda, parametre deikeninden sonra eittir (=) ilecinden yazlan bir ifadeyle belirtilir. Aadaki rnei inceleyin: #include <iostream> void foo(int x = 10, int y = 20); void foo(int x, int y) { std::cout <<"x = " << x << '\t' << "y = " << y << std::endl; } int main() { foo(); foo(100); foo(100, 200); return 0; }

// // //

x = 10 y = 20 x = 100 y = 20 x = 100 y = 200

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: void func(int x = 10, int y); void foo(int x, int y = 20); //Geersiz! //Geerli

Varsaylan deer almam olan btn parametre deikenlerine ar ifadesi ile gereken argmanlar gnderilmek zorundadr.

C++ Kitabi (37/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bir ilevin parametre deikeni olan gsterici de varsaylan deer alabilir. Aadaki rnei inceleyin: #include <iostream> void put_message(const char *p = "Success!") { std::cout << p; } int main() { put_message("Failed!"); put_message(); return 0; }

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. int func1(); int func2(int); int func3(double

= 3.14);

int g = 10; int func4(int a = func1(), int b = func2(g), int c = 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 int func(int a, int b, int c = 0); /////// file2.h #include file1.h" int func(int a, int b, int c = 0); //Geersiz

C++ Kitabi (38/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Ancak ikinci kez yaplan bildirimde, bu kez daha nce varsaylan argman belirlenmemi bir parametre deikeni iin varsaylan deer bildirilebilir. Tabii yine bu durumda daha sada kalan tm parametre deikenleri iin mutlaka daha nce varsaylan deerlerin belirlenmesi gerekir. file2.h dosyas aadaki gibi olsayd, derleme zamannda bir hata olumazd: /////// file2.h ///////////////////////////

#include "file1.h" int func(int a, int b = 1, int c); int func(int a = 2, int b, int c); Varsaylan argman olarak belirlenen deerler yalnzca bir kez yazlmaldr. Bu deerler ilevin arayznn bir parasdr. Doal olan varsaylan argmanlarn ilev bildiriminde belirtilmesidir. Varsaylan argman deerin hem ilev bildiriminde, hem de ilev tanmnda yer almas geersizdir: void func(int x = 10, int y = 20); void func(int x = 10, int y = 20) { } // Geersiz.

Hatrlayacanz gibi, ilev bildirimlerinde parametre deikenlerinin isimleri yazlmak zorunda deildir. Varsaylan bir deer alacak parametre deikeni iin de isim yazlmas zorunlu deildir. Aadaki iki bildirim de geerlidir: void func(int a = 10, int b = 20); void func(int = 10, int = 20); Ancak ilevin varsaylan deer alacak parametre deikeni bir gsterici ise ve ilev bildiriminde parametre deikeni olan gstericiye isim verilmiyorsa dikkatli olunmaldr: void func(char *= "Ahmet"); // 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: void func(char * = "Ahmet"); Gsterici parametre deikenleri varsaylan argman alabildii gibi, referans parametre deikenleri de varsaylan deerler alabilir: #include <iostream> int g = 20; void func(int &r = g); int main() { int y = 30; func(); func(y); return 0; }

C++ Kitabi (39/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void func(int &r) { std::cout << r << std::endl; }

Varsaylan Argmanlar Neden Kullanlr?


Baz ilevlerin ok sayda parametre deikeni vardr. zellikle byle ilevler sz konusu olduunda ve bu parametre deikenlerinin belli blmne ileve yaplan arda ounlukla ayn deerler gnderiliyorsa, varsaylan argmanlarn kullanlmas byk bir yazm kolayl salar. leve gnderilen argman saysnn azaltlmas hem programcnn i ykn azaltr, hem de kodun okunabilirliini arttrr. Parametre deikenleri iin varsaylan argman belirlenirken dikkat edilmelidir. Bir ilev ounlukla ayn argman deerleriyle arlyorsa varsaylan argman alan parametre deikeni kullanlabilir. Bir rnek verelim: Standart olmasa da, C derleyicilerinin ounda stdlib ktphanesi iinde itoa isimli bir ilev bulunur. lev verilen bir tamsay deerini verilen bir say sisteminde bir yazya dntrerek, adresini ald bir diziye yazar: char *itoa(int, char *, int); levin levin levin levin birinci parametresi yazya dntrlecek deerdir. 2. parametresi yaznn yerletirilecei adrestir. 3. parametresi dnmn yaplaca say sistemidir. geri dn deeri yaznn yerletirildii adrestir.

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(int n, char *str, int base = 10) { return itoa(n, str, base); } int main() { char s[100]; itoa_d(123, s); std::cout << s; return 0; }

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 amacyla kullanlr. Gerek varsaylan deerler ilevin iinde ve bir dizi ilemle elde edilebilir. Aadaki rnei inceleyin:

C++ Kitabi (40/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <cstdio> #define DEFAULT_CALL (-1)

void write_file(const void *ptr, unsigned nbytes, FILE *fp, long offset = DEFAULT_CALL) { if (offset != DEFAULT_CALL) fseek(fp, offset, SEEK_SET); fwrite(ptr, 1, nbytes, fp); } int main() { double d = 10.2; FILE *f; /*...*/ write_file(&d, sizeof(double), f); /*...*/ return 0; }

write_file isimli ilevin tanmn inceleyelim: lev birinci parametresine geilen adresten balayarak nbytes kadar byte' bir dosyaya yazar. levin offset isimli son parametresine bir ofset deeri geilirse, ilev dosya konum gstericisini bu ofset deerine konumlandrr ve yazma ilemini bu konumdan balatr. levin son parametresine bir deer geilmez ise, yazma ilemi dosya konum gstericisinin gsterdii konumdan balayarak yaplr.

C++ Kitabi (41/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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:

Ayn Arayz Farkl lemler


C'nin aadaki standart ilevlerini int abs(int x); double fabs(double x); long labs(long x); C'nin standart balk dosyalarndan biri olan math.h iinde bildirimleri yer alan yukardaki ilevlerin hepsi aslnda ayn ilemi yapar. Bu ilevler, kendilerine argman olarak gnderilen ifadenin mutlak deerini geri dn deeri olarak retir. Bu ilevlerin dardan aldklar deerlerin trleri farkldr, dolaysyla rettikleri geri dn deerlerinin trleri de farkldr. C dilinde ayn isimli ilevler tanmlanamayaca iin, bu ilevlere ayr isimler verilmitir. Bu ilevlerin hepsinin ismi ayn olsayd, rnein hepsinin isimleri abs olsayd, mutlak deer alma ilemi yapan programcnn, birden fazla ilev ismini bilmesi ya da anmsamas gerekmezdi, deil mi? Aslnda dilin temel ilelerini dndnz zaman benzeri bir aracn kullanma hazr bir biimde sunulduunu grebilirsiniz. Toplama ilecini ele alalm. Toplama ileci ne i yapar? ki deerin toplanmasn salar, deil mi? 10 + 20 Yukarda, 10 ile 20 deerlerinin toplama ilecinin terimleri olduunu gryorsunuz. Toplama ileci bir ilemin yaplmasn salyor, yaplan ilemin sonucunda 30 deeri retiliyor. imdi de aadaki ileme bakalm: 1,5 + 3.5 Yukardaki rnekte ise 1.5 ve 3.5 deerlerinin toplama ilecinin terimleri olduunu gryorsunuz. Toplama ileci yine bir ilemin yaplmasn salyor, yaplan ilemin sonucunda 5.0 deeri retiliyor. Oysa makina asndan bakldnda, tamsay trnden iki deerin birbiriyle toplanmasyla, gerek say trnden iki deerin birbiriyle toplanmas bambaka ilemlerdir. Bize ok doal grnen bu iki rnekte yaplan ilemi, aslnda ayrntlarndan soyutlayarak "toplama" olarak ifade ediyoruz. Matematikte olduu gibi C dilinde de, bu ilemleri yapmak iin, bir soyutlama kullanlarak, ayn ileler yani ayn simgeler kullanlyor. ki ilem iin farkl iki simge kullanlm olsayd, alglama bu kadar kolay olur muydu? C dilinde ayn isimli ilevlerin bir arada bulunamamasnn nedeni nedir? C derleyicileri, bir ilev ar ifadesi ile karlatnda, hedef koda (object code) yalnzca arlan ilevin ismini yazar. rnein Borland derleyicileri, arlan ilevin isminin bana alttire karakteri ekleyerek oluturduklar ismi hedef koda yazar. Aada bildirimi verilen ilevi rnek olarak ele alalm: int foo(int, int); Derleyici bu ilevin arlmas durumunda hedef koda _foo
C++ Kitabi (42/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

gibi bir isim yazar. Oysa foo ilevinin bildirimi farkl olsa da, C derleyicisinin hedef koda yazaca isim ayn olur. Ayn isimli fakat parametrik yaplar farkl, birden fazla foo ilevi olsayd, derleyici bu ilevlerin hepsi iin ama kodda ayn ismi kullanrd. Bu durumda da, balayc program ayn isimli ilevlerden hangisinin arlm olduunu anlayamazd. C++ dilinde ayn isimli ilevlerin tanmlanabilmesi nasl mmkn oluyor? C++ dilinde farkl olan nedir? C++ derleyicisi, bir ilev ar ifadesinin karl olarak hedef koda, C derleyicisi gibi yalnzca ilevin ismini yazmaz. C++ derleyicisi arlan ilevin ismini, ilevin parametre deikenlerinin trleri ile birletirerek bir isim oluturur, bu ismi ama koda yazar. Ama koda yazlmak zere oluturulan bu ismin oluturulma biimi, standartlarca kesin olarak belirtilmemi, derleyiciyi yazanlara braklmtr. rnein bir C++ derleyicisi yukarda bildirimi verilen foo ilevini hedef koda aadaki gibi yazabilir: _foo@i@i C++derleyicisinin, ilev isimlerinin sonuna @ karakterini izleyecek biimde, parametre deikeninin trnn ba harfini yazdn varsayalm. Bu durumda int foo(double, double); gibi ikinci bir foo ilevi var olsayd, derleyici bu ilevin ar ifadesi karl hedef koda bu kez _foo@d@d yazard. Bylece sra balayc programa geldiinde, balayc program ayn isimli birden fazla ilev olmasna karn, hedef koda yazlan ismi grerek, ayn isimli ilevlerden hangisinin arlm olduunu anlar. Ayn isimli ilevlerin var olabilmesi iin gerekli koul da ortaya kyor: Ayn isimli iki ilevin, ayn bilinirlik alannda var olabilmesi iin, ilevlerin parametrik yaplarnn farkl olmas gerekir. Ayn isimli ilevlerin parametre deikeni says ve/veya parametre deikenlerinin trleri bir farkllk gstermelidir. Eer ayn isimli iki ilevin, hem parametre deikeni says ayn hem de parametre deikenlerinin trleri ayn ise, bu durumda her iki ilev de hedef koda ayn ekilde yazlacandan, balayc program yazlan isimlerden, hangi ilevin arlmak istendiini anlayamaz. Yukardaki aklamalardan, C++derleyicilerinin arlan ilevi hedef koda yazarken kullandklar ismin oluturulmasnda, ilevin geri dn deeri trnn hibir katks olmadn anlamalsnz. Bu durumda, imzalar birbirleriyle tamamen ayn, ancak geri dn deerleri farkl trlerden olan, ayn isimli iki ilev, ayn bilinirlik alannda bildirilemez. Geri dn deeri tr dnda tm parametrik yaps ayn olan iki ilevin var olduunu dnelim. Bu isimle bir ilev ars yapldnda, derleyici hangi ilevin arldn anlayabilir miydi? int foo(int); double foo(int); int main() { foo(); return 0; } //Geersiz!

Yukardaki her iki bildirim de geerli olsayd, hangi foo ilevinin arld nasl anlalrd?

C++ Kitabi (43/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: //ilev bildirimi iki kez yaplyor (function redeclaration) int func(int); int func(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: //function overloading (lev yklenmesi) int func(int); int func(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: //ilev bildirimi yineleniyor(function redeclaration) int max(int *ptr, int size); int max(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. //ilev bildirimi elikili biimde yineleniyor int func(int) double func(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: typedef unsigned char BYTE; BYTE foo(BYTE); int foo(unsigned char); 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? Bir ilev arsnn, hangi ileve ilikin olduunun saptanmas ilemine Yklenmi levin Saptanmas" (function overload resolution) denir.
C++ Kitabi (44/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

"Yklenmi ilevin saptanmas", derleyici tarafndan aamada yaplan bir ilemdir. Birinci aamada, derleyici sz konusu ilev ars iin ele alnacak ayn isimli ilevleri saptayarak bu ilevlerin parametrik yaps hakknda bilgi edinir. arlmas sz konusu olan ayn isimli ilevlere "aday ilevler" (candidate functions) denir. Aday ilevler, ilev ar ifadesinde kullanlan isimle ayn isme sahip olan, ilev ar ifadesinin bulunduu yerde grlebilen (visible) ilevlerdir. Birinci aamada derleyici, aday ilevler hakknda gerekli bilgiyi de edinir. Aday ilevlerin parametre deikeni saylarn, parametre deikenlerinin trlerini renir. kinci aamada, ilev ar ifadesinde yer alan argmanlar kullanlarak, hangi aday ilevlerin geerli biimde arlabilecei saptanr. Sz konusu ilev arsyla, geerli biimde arlabilen ilevlere, uygun ilevler (viable functions) denir. Bir ilevin uygun ilev olarak belirlenebilmesi iin, aadaki koullar salamas gerekir: i) lev ar ifadesindeki argman says ile, ilevin parametre deikeni saysnn ayn olmas zorunludur. lev ar ifadesindeki argman says, ilevin parametre deikeni saysndan daha az ise, bu durumda ilevin argman gnderilmeyen parametre deikenleri varsaylan argman almaldr. ii) Argman olan ifadenin, parametre deikeni olan nesneye uygun bir ekilde dntrlebilmesi gerekir: rnek olarak, aada bildirimleri verilen ilevleri inceleyelim: void void void void foo(); foo(int); foo(double, double = 3.4) foo(char *); //1 //2 //3 //4

void func() { 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. 4 numaral ilev uygun deildir. levin parametre deikeni says ile, ilev arsndaki argman says birbirine eit olmasna karn, int trden ifade, dilin kurallarna gre char* trne dntrlemez.
C++ Kitabi (45/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

kinci aamada uygun bir ilev bulunmaz ise, ilev ars geersiz kabul edilir. Bu duruma ngilizcede "no match" durumu (arlacak uygun bir ilevin bulunmamas) denir. nc aama nc yani son aamada, uygun ilevler iinde en uygun olan ilev belirlenir. Uygun ilevler iinden seilerek, ar ifadesi ile elenecek ileve "en uygun ilev" (best viable function / best match function) denir. nc aamada, belirli sayda uygun ilev iinden, bir ilevin en uygun ilev olarak seilebilmesi iin aadaki koullar yerine getirmesi gerekir: lev ar ifadesindeki argmanlardan ilevin ilgili parametre deikenine yaplan dnmn derecesi dier uygun ilevlere gre daha kt olmamas gerekir. En az bir argman iin yaplacak dnmn dierlerine gre daha iyi olmas gerekir. Birden fazla uygun ilev iinden, en uygun ilevin seilememesi durumunda "ift anlamllk hatas" (ambiguity) denilen bir hata durumu oluur. Bu durumda, derleyici iin kurallara uygun olarak arlabilecek birden fazla ilev sz konusudur. "Argmanlardan parametre deikenlerine yaplacak dnmn derecesi" ne anlama gelir? C++ standartlar, argmanlardan parametre deikenlerine yaplabilecek otomatik dnmleri 4 ayr dereceye ayrmtr. 1. 2. 3. 4. Tam uyum (exact match) Ykseltme (promotion) Standart dnm (standard conversion) Programcnn tanmlad dnm (user defined conversion)

Kurallara gre, "tam uyum" durumu "ykseltme"den, "ykseltme" durumu standart dnmden, standart dnm de programcnn tanmlad dnmden daha iyi olarak kabul edilir. Yukardaki derecelendirmeleri ayrntl biimde inceleyelim: 1. Tam uyum durumu Argman olan ifadenin tr ile, bu argmann kopyalanaca parametre deikeni olan nesnenin tr tamamen ayn ise, bu durum tam uyum (exact match) olarak ele alnr. Ancak aadaki durumlar da tam uyum olarak ele alnr: i) Argman olan nesne bir sol taraf deeri, yani bir nesne ise, parametre deikenine kopyalanacak deerin, bu nesneden alnmas. Bu duruma sol taraf deerinden sa taraf deerine dnm denir (L-value to R-value transformation). ii) Parametre deikeninin bir gsterici olmas, ilevin de ayn trden bir dizinin ismi ile arlmas. Dizi isimlerinin bir ileme sokulduunda, ilem ncesinde otomatik olarak dizinin ilk elemennn balang adresine dntrldn (array to pointer conversion) biliyorsunuz. iii) Parametre deikeninin bir ilev gstericisi (function pointer) olmas, ilevin de ayn trden bir ilevin ismi ile arlmas. lev isimlerinin bir ileme sokulduunda, ilem ncesinde otomatik olarak ilev blounun balang adresine dntrldn biliyorsunuz. iv) lev parametre deikeninin, gsterdii yer const olan bir gsterici olmas, ilevin ayn trden ancak const olmayan bir adres ile arlmas (qualification conversion).

C++ Kitabi (46/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

2. Ykseltme durumu Ykseltme (promotion), aadaki durumlar kapsar i. char, unsigned char, short, unsigned short , bool trlerinden int trne yaplacak dnm. Bu duruma "int trne ykseltme" (integral promotion) denir. Argman olan ifade, int trnden kk trlerden ise, int trne ykseltme kural gerei, ilevin parametre deikenlerine yaplan bir atama sz konusu ise, bu durum ykseltme olarak ele alnr. void func(int); int main() { func('A'); //Ykseltme (integral promotion) func(true); // Ykseltme (integral promotion) //... }

ii) Argman olan ifade float trden ise, ilevin bu argmana karlk gelen parametre deikeni double trden ise, bu durumda, float trnden double trne yaplacak dnm de ykseltme olarak deerlendirilir: void func(double); int main() { float fx; //... func(fx); //... }

//ykseltme

iii. Bir numaralandrma trnden, o numaralandrma trne baz olan (underlying type) tre yaplan dnm de, ykseltme olarak deerlendirilir. Aadaki rnei inceleyin: enum POS {OFF, ON, HOLD, STAND_BY}; int func(int); int main() { POS position = OFF; func(position); func(STAND_BY); //... }

// Numaralandrma trnden int trne ykseltme) // Numaralandrma trnden int trne ykseltme)

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. ii. Gerek say dnmleri Bir gerek say trnden baka bir gerek say trne yaplan dnmler iii. Gerek say trleri ile tamsay trleri arasnda yaplan dnmler.
C++ Kitabi (47/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

iv. Adres trlerine ilikin dnmler 0 deerinin herhangi trden bir gstericiye atanmas ya da void trden olmayan herhangi bir adres bilgisinin void trden bir gstericiye atanmas durumu. v. bool trne yaplan dnmler Herhangi bir tamsay, gerek say, numaralandrma ya da adres trnden, bool trne yaplan dnmler. Aada standart dnmlere ilikin baz rnekler veriliyor: void void void void void func(int); foo(long); sample(float); pf(int *); vfunc(void *);

int main() { int x = 10; foo(x); foo('A'); func(20U); sample(7.5); pf(0); vfunc(&x) //... return 0; }

//standart dnm (int trden long trne) //standart dnm (char trden long trne) //standart dnm (unsigned int trnden int) //standart dnm (double trden float trne) //standart dnm (0 deerinin bir gstericiye atanmas //standart dnm (int * trden void * 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: int foo(int); int foo(double); void foo(char); long foo(long); void foo(int, int); void foo(char *); void foo(int *); //1 //2 //3 //4 //5 //6 //7

void func() { foo(10); foo(3.4F); foo((double *) 0x1FC0); foo(6U); }

func ilevi iinde yaplan ilev arlarn teker teker ele alalm: foo(10);
C++ Kitabi (48/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: void foo(const int *); void foo(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? void func() { int x = 20; foo(&x); //hangi ilev arlr? } Bu durum "const yklemesi" (const overloading) olarak bilinir. Hangi ilevin arlaca, ileve gnderilen nesnenin const olup olmamasna gre belirlenir. Eer ilev ars const bir nesne adresi ile yaplrsa, parametre deikeni gsterdii yer const gsterici olan ilev arlrken, ilev ars const olmayan bir nesnenin adresi ile yaplrsa, arlan dier ilev olur: void foo(const int *); void foo(int *); void func() { int x = 20; const int y = 30; foo(&x); // void foo(int *); foo(&y); // void foo(const int *); }

C++ Kitabi (49/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

phesiz const yklemesi, parametre deikeninin referans olmas durumunda da geerlidir: void foo(const int &); void foo(int &); void func() { int x = 20; const int y = 30; foo(x); // void foo(int &); foo(y); // void foo(const int &); }

Aada yer alan programda, ift anlamllk hatasnn olduu bir baka tipik durum gsteriliyor: #include <iostream> using namespace std; void foo(int &r) { cout << "void foo(int &)" << endl; } void foo(int x) { cout << "void foo(int)" << endl; } int main() { int a = 20; //foo(a); foo(5); return 0; }

ift anlamllk hatas //void foo(int);

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.

C++ Kitabi (50/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

C++ Kitabi (51/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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 (52/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bir snf tanmnda yer alan blok, private, protected ve public isimli blmden oluur. public, private ve protected C++n anahtar szckleridir. Bu szcklere bundan sonra eriim belirteci diyeceiz. Bir blm, eriim belirtecini izleyen iki nokta st ste (:) ayrac ile balatlr, dier bir eriim belirtecinin kullanlmasna kadar srer. blmn, hepsinin bir snf bildiriminde bulunmas zorunlu deildir. Hi bir eriim belirtecinin kullanlmamas durumunda snfn private blm anlalr. Yani snf bildirimi iinde varsaylan (default) blm private blmdr. ye ilevlerin yalnzca bildirimleri snf bildirimi iine yazlr. Bu ilevlerin tanmlamalar, normal bir ilev gibi ancak farkl bir szdizim kural ile snf bildiriminin dnda yaplr. Ancak ye ilevlerin tanm snf bildiriminin iinde de yaplabilir. Bu durumu ileride, snf ii inline ilevler balyla inceleyeceiz. C++da, snfn tanm iinde bildirilen ilevlerle snfn dnda bildirimleri yaplan ilevleri birbirlerinden ayrabilmek iin yeni terimler kullanlr. Snfn iinde bildirilen bir ileve, o snfn ye ilevi (member function) denirken, dier ilevlere global ilevler (global functions) denir. Yani C dilinde daha nce tanmlamaya alm olduumuz ilevlere artk bundan byle global ilevler diyeceiz. Snfn elemanlar, yaplarda olduu gibi snf iinde tr bilgileri ile bildirilir. Aadaki rnei inceleyin: class A { private: int a; void func1(); protected: long b; int func2(int); public: double c; double func3(); };

Yukardaki snf bildiriminde, snfn blmnn de kullanlm olduunu gryorsunuz. Snfn her blmnde, birer eleman ile birer ilev bildiriliyor. Snf bildirimi iinde, birden fazla eriim belirteci kullanlabilir. rnein yukardaki bildirim aadaki gibi de yaplabilirdi: class A { protected: long b; private: int a; public: double func3(); protected: int func2(int); public: double c; private: void func1(); };

Snf bildirimi bir eriim belirteci ile balatlmamsa, private blm zerinde ilem yapld anlalr. Aadaki rnei inceleyin:

C++ Kitabi (53/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class A { long b; int a; public: double func3(); protected: int func2(int); public: double c; private: void func1(); };

Yukardaki rnekte, A isimli snfn a ve b elemanlar snfn private blmnde bildiriliyor. imdi de aadaki snf bildirimini inceleyin: class Date { int day, mon, year; bool verify_date(); public: void set_date(int , int, int); void display_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. Snf tanm kaynak kodun neresinde yaplmaldr? Bir snfn tanm kaynak kodun herhangi bir yerinde yaplabilir. Ancak tanmn bilinirlik alannn (scope), dosya bilinirlik
C++ Kitabi (54/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

alannda (file scope) ya da blok bilinirlik (block scope) alannda olmas, farkl anlamlar ierir. rnein Date snfnn tanm, btn bloklarn dnda yani global dzeyde yaplrsa, dosya bilinirlik alanndaki bu bildirim sonucunda Date snfn, dosyadaki tm ilevler bilir. Yani snf bildiriminden sonra dosyann sonuna kadar her noktada Date snf kullanlabilir. Oysa bildirim yerel dzeyde yani bir blok iinde yaplrsa, ilgili snf yalnzca bildirimin yapld blok iinde bilinir. Yaplar gibi snflar da ounlukla balk dosyalar iinde tanmlanr. ou zaman bir snfn tanm hizmet veren kodlarn arayznn bir parasdr. Ancak, C++ dilinde balk dosyalarnn kullanlmas konusunu daha sonraki blmlere brakyoruz. Snf tanm, bir snfn elemanlar ile ye ilevlerinin derleyiciye tantlmas ilemidir. Snf tanm ile derleyici bellekte herhangi bir yer ayrmaz, yalnzca snf hakknda bilgi edinir. Snflar zerinde baz ilemlerin yaplabilmesi iin, snf trnden nesnelerinin tanmlanmas gerekir. imdi snf nesnelerinin tanmlanmasn ele alacaz:

Snf Trnden Deikenlerin Tanmlanmas


Daha nce bildirimi yaplm bir snf trnden, bir deiken tanmlanabilir. Doal trlerden deikenler nasl tanmlanrsa snf trnden deikenler de ayn biimde tanmlanr. Bildirimde nce tr bildiren szck ya da szckler, daha sonra tanmlanan deikenin ismi gelir. Daha nceki rneklerde tanmlanan A ve Date isimli snflar dnelim. A a; a deikeni A snf trnden bir snf deikenidir. Date date1, date2; date1 ve date2 isimli deikenler Date snf trndendir. C++da yap ve snf trnden deikenler tanmlarken, struct ve class anahtar szcklerinin yazlmasna gerek olmadn biliyorsunuz. Bu anahtar szcklerin kullanlmas herhangi bir soruna yol amaz. Yani yukardaki bildirim class Date date1, date2; biiminde de yaplabilirdi. Ancak class anahtar szcnn gereksiz bir ekilde kullanlmas tercih edilen bir biim deildir.

ye levlerin Tanmlanmas
Bir snfn ye ilevi, belirlenmi bir szdizim ile snfn dnda tanmlanr: [geri dn deerinin tr] <snf_ismi> :: <ilev_ismi> ([parametre deikenleri]) { // } 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. Date snfnn, parametre deikeni olmayan, geri dn deeri retmeyen display_date isimli ye ilevi yle tanmlanabilir:

C++ Kitabi (55/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void Date::display_date() { // }

Snf Nesneleri Yoluyla Snfn Elemanlarna ve ye levlerine Eriim


Yaplarda olduu gibi, bir snfa ilikin nesne yoluyla snfn veri elemanlarna ve ye ilevlerine nokta ileci ile eriilebilir. rnein object bir snf trnden nesne, m ise bu snfn bir eleman ve func da bu snfa ilikin bir ye ilev olsun. Eriim aadaki gibi gerekletirilir: object.m Bir ye ilev ancak bir snf nesnesi iin ile arlabilir. Global ilevlerde olduu gibi dardan dorudan arlamaz. rnein object isimli nesnenin ait olduu snfn func isimli bir ye ilevi varsa, bu ilev func(); biiminde arlamaz. Eer byle bir ar yaplrsa derleyici global bir func ilevinin arldn anlar. ye ilev ars aadaki gibi yaplabilir: object.func(); Yukardaki deyimde object bir snf trnden nesne ve func da object in ait olduu snfn ye ilevlerinden birinin ismidir. Bir snfn ye ilevi, snf trnden bir gsterici ya da referans araclyla da arlabilir. Bu durumlar biraz daha ileride ele alacaz.

ye levlere Yaplan arlarn Ama Kod ine Yazlmalar


C++da global bir ileve yaplan arnn ama kod iine nasl yazldna daha nce deinmitik. lev ismi parametre deikenlerinin says ve trleriyle birletirilerek bir isim elde ediliyor ama koda bu isim yazlyordu. Bir snfn ye ilevi arldnda derleyici bu kez ye ilev ismini, parametre deikenlerinin says ve trlerinin yan sra snfn ismiyle de birletirerek ama kod iine yazar. Bu durumda bir program iinde, ayn isimli ve ayn parametrik yapya sahip global bir ilev ile bir snfa ilikin ye ilev birlikte bulunabilir. Bu iki ilevin ama koda yazlmalarnda bir sorun ortaya kmaz. rnein Borland C++ 3.1 derleyicisinde void func() ; global ilevi @func$qv biiminde, X snfna ilikin X::void func() ilevi ise @X@func$qv biiminde ama kod iine yazlr. Derleyiciler, ayn isimli global ilevlerle ye ilevleri ar biimlerine bakarak ayrabilir. rnein:
C++ Kitabi (56/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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. class A { private: int a; int b; public: int c; int d; protected: int e; int f; private: int g; int h; };

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.

Snflarda Temel Eriim Kural


Global bir ilev iinde bir snf nesnesi, gstericisi ya da referans ile snfn her blmne eriilemez. Hangi elemanlara ya da hangi ye ileve eriebilecei, snfn elemanlarnn ya da ye ilevlerin yerletirilmi olduklar yer ile ilgilidir. Eriim kurallarn iki blme ayrarak inceleyeceiz:

C++ Kitabi (57/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Global levlerin Eriim Kural


Bir snf nesnesi, gstericisi ya da referans ile, global bir ilevden ancak snfn public blmndeki elemanlara ve ye ilevlerine eriilebilir. Global bir ilev iinde snfn private ya da protected blmlerindeki elemanlara ve ye ilevlere snf nesnesi yoluyla eriilemez. Aadaki rnekleri inceleyin: #include <iostream> #include <cstdlib> class Date { int day, month, year; bool verify_date(); public: void set_date(int, int, int); void display_date(); }; // ye ilev tanmlamalar... using namespace std; int main() { Date date; date.day = 10;

// Geersiz!

if (!date.verify_date()) { // Geersiz! cout << "Geersiz tarih" << endl; exit(EXIT_FAILURE); } date.set_date(10, 10, 1997); date.display_date(); return 0; } // Geerli! // Geerli!

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, xxx cannot access private member declared in Class YYY Borland derleyicilerinde ise, YYY::xxx not accessible biimindedir.

ye levlerin Eriim Kural


Bir snfn ye ilevi, hangi blmde bildirilmi olursa olsun snfn her blmndeki elemanlarna dorudan eriebilir. Snfn ye ilevlerini dorudan arabilir. rnein, bildirimini yukarda verdiimiz Date snfnn set_date isimli ye ilevi yle tanmlanm olsun:

C++ Kitabi (58/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void Date::set_date(int d, int m, int y) { date = d; // Geerli. month = m; // Geerli. year = y; // Geerli. if (!verify_date(d, m, y)) { // Geerli. cout << "Geersiz tarih" << endl; exit(EXIT_FAILURE); } }

Grld gibi set_date isimli ye ilevin tanm iinde, verify_date isimli ye ilev dorudan arlyor. verify_date ye ilevi, sz konusu tarihin geerli bir tarih olup olmadn snyor. Eer tarih geersiz ise program sonlandrlyor. set_date ye ilevi iinde snfn day, month, year isimli private elemanlarna dorudan eriiliyor. Yine set_date ilevi iinde snfn private ye ilevi olan verify_date isimli ilevi arlyor. private blmde olan bu isimlere eriim geerlidir. nk bir ye ilev kendi snfnn her blmne eriebilir. Snfn ye ilevleri iinde tanmlanan ayn snfa ilikin nesneler, referanslar ya da gsterici deikenler ile snfn her blmne eriebilir. rnein Date snfnn process_date isimli bir ye ilevi daha olsa, void Date::process_date() { Date date; //Geerli. date.day = day; date.month = month; //Geerli. date.year = year; //Geerli. if (!date.verify_date()) { //Geerli. cout << "Geersiz tarih: << endl; exit(EXIT_FAILURE); } }

process_date ye ilevi iinde tanmlanan date snf deikeni ile snfn her blmne eriilebilir. nk date deikeni snfn ye ilevi iinde tanmlanyor. Bir ilevin parametre ayracnn ii de ilevin ii kabul edilir. Bu durumda bir ye ilevin parametre deikeni ayn snf trnden bir nesne, referans ya da gsterici ise, parametre deikeni ile nokta ya da ok ilecinin kullanlmasyla snfn private elemanlarna ya da ye ilevlerine eriilebilir.

ye levlerin Snfn Elemanlarna Erimesi


Bir ye ilev hangi snf nesnesi iin arlmsa, ye ilevin iinde kullanlan veri elemanlar da o nesneye ilikindir. Aadaki rnei inceleyin: class A { public: void set(int, int); void display(); private: int a, b; }; void A::set(int x, int y) { a = x; b = y; }
C++ Kitabi (59/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

#include <iostream> using namespace std; void A::display() { cout << "a = " << a << endl; cout << "b = " << b << endl; } int main() { A a1; a1.set(10, 20); a1.display(); A a2; a2.set(30, 40); a2.display(); return 0; } Burada set ve display ye ilevleri tanmlar iinde dorudan kullanlan a ve b elemanlar, bu ilevler hangi nesne ile arlmsa o nesnenin elemanlardr. display ye ilevine yaplan a1.display() arsnda a1 deikeninin elemanlar, a2.display() arsnda ise a2 deikeninin elemanlar kullanlr.

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: void A::set(int x, int y) { a = x; b = y; display(); }

C++ Kitabi (60/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bu durumda set ye ilevi hangi snf nesnesi ile arlmsa, display ye ilevi de onun elemanlarn kullanr. int main() { A a1; a1.set(10, 20); return 0; }

Yukardaki main ilevinde arlan set ve display ye ilevleri a1 snf nesnesinin elemanlarn kullanr. Yani set ve display ilevlerinin iindeki a ve b elemanlar a1 nesnesine ilikindir.

Snf Elemanlarna Eriim ve this Gstericisi


Bir ye ilevin kendi snfnn elemanlarna ve ye ilevlerine dorudan eriebildii sylendi. Peki, gerekte bu nasl oluyor? Yalnzca bir grup ilev tarafndan eriilebilen bir nesne, C dilinde ne anlama gelir? Bir ilev kendi iinde tanmlanmayan bir yap nesnesinin elemanlarna nasl eriebilir? levin bu yerel nesnenin adresini almas gerekir, deil mi? C, C++dan daha doal bir dildir. Yani bilgisayarda olanlar daha iyi betimler. Bir ilem pek ok programlama dilinde farkl bir biimde yaplyorsa, gerekte ilem Cdekine(C dilindekine) benzer bir biimde yaplr. Bir ilemin C dilinde neye karlk geldiini aratrmak onun gerekte nasl olduunu yani makina dili dzeyinde neye karlk geldiini aratrmak anlamna gelir. rnein snf bilinirlik alan diye bir kavram C dilinde olmadna gre bu durum doal deildir. Aslnda Cdeki(C dilindeki) doal baka mekanizmalarla karlanabilir. Bir ye ilevin snfn elemanlarna erimesi ye ileve gizlice geirilen bir adres yoluyla yaplr. Bu adres, ye ilev hangi nesne ile arlmsa o nesnenin adresidir. rnein bir ye ilevin hi parametre deikeni yoksa, aslnda gizli bir parametre deikeni vardr. O parametre deikenine ilev arsyla bir snf nesnesinin adresi aktarlr. rnein aada tanmlanan Myclass snfnn display isimli ye ilevini inceleyelim. Grne gre display ilevinin parametresi yoktur: void Myclass::display() { cout << "a = " << a << endl; cout << "b = " << b << endl; }

Ancak aslnda ileve gizlice bir argman gnderilir. Bu argman ar ifadesindeki snf nesnesinin adresidir. Aslnda display ilevinin makina kodlar incelenirse byle bir argmann geirildii grlebilir. Bu durumda, display ilevinin C karl yle olur: void A::display(A *const this) { cout << "a = " << this->a << endl; cout << "b = " << this->b << endl; }

Bir ye ilev iinde snfn bir eleman kullanldnda, derleyici o elemana ileve gizlice geirilen bir adres ile eriir. Adres geirme ileminin gizlice yaplmas kolay yazm salamak iin dnlmtr. rnein bir ye ilevinin aka yazlm drt parametresi olsa, gerekte derleyici bu ileve be argman geirir.

C++ Kitabi (61/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ye ileve nesnenin adresi gizlice geiriliyor olsa da, ye ilevler iinde bu adres this anahtar szc ile kullanlabilir. rnein, void Myclass::display() { cout << "a = " << a << endl; cout << "b = " << b << endl; }

tanmyla, void Myclass::display() { cout << "a = " << this->a << endl; cout << "b = " << this->b << endl; }

tanm arasnda hi fark yoktur. Zaten rnein, this->a denmese bile a eleman kullanldnda derleyici gizlice geirilen snf nesnesi adresiyle a elemanna eriir. this bir anahtar szcktr ve hangi snfa ilikin ye ilev iinde kullanlrsa o snf trnden bir adres belirtir. rnein this anahtar szc Myclass snfna ilikin bir ye ilev iinde kullanlrsa, this Myclass snf trnden bir adres belirtir. this ayn zamanda kendisi const bir gstericidir. indeki adres deitirilemez. ye ilev iinde this = adr; gibi bir atama geersizdir. ye ilev hangi nesne iin arlmsa this gstericisine o nesnenin adresi geirilir. Tabii eer ye ilev bir adres yoluyla arlyorsa this gstericisine ye ilevin arld gsterici iindeki adres geirilir. rnein, func X snfnn bir ye ilevi, a bu snf trnden bir nesne ve p de bu snf trnden bir gsterici olsun, a.func(); arsnn C karl, Xfuncv(&a); biimindedir. Benzer biimde, ptr->func(); arsnn C karl ise, Xfuncv(ptr); biimindedir. C karlklarn yazarken ye ilevlerin snf isimleriyle ve parametre trleriyle birletirildiini vurgulamak iin bana ve sonuna ekler getirdik. Aadaki program yazarak altrn: class X { public: void display(); void set(int); private: int a; };

C++ Kitabi (62/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> using namespace std; void X::set(int val) { cout << "X::set()cagrildi" << endl; cout << "this = " << this << endl; a = val; } void X::display() { cout << "X::display() cagrildi!" << endl; cout << "this = " << this << endl; cout << "a = " << a << endl; } int main() { X myx; cout << "&myx = " << &myx << endl; myx.set(10); myx.display(); X *ptr = &myx; ptr->display(); return 0; } Elde edilen adres deerleri birbirinin ayns, deil mi? Bu durumda bir ye ilevin baka bir ye ilevi armasnn da ne anlama geldii de aktr. ye ilev dardan gizlice ald this adresini gizlice baka bir ye ileve geer. void A::set(A *const this, int x, int y) { this->a = x; this->b = y; display(this); }

Peki, bir snf gstericisi iinde rastgele bir deer varken o gstericiyle bir ye ilev arlrsa ne olur? rnein: int main() { X *p; p->func(); return 0; }

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:

C++ Kitabi (63/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class A { public: //... void a_func(); private: //... }; void g_func(A *); void A::a_func() { //... g_func(this); } int main() { A a; a.a_func(); return 0; }

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: void g_func(A); void A::a_func() { A a; a = *this; //... g_func(*this); } int main() { A x; x.a_func(); return 0; }

A snfnn a_func ilevi iinde tanmlanan A snf trnden a nesnesine *this ifadesi atanyor. main ilevi iinde a_func ilevi x nesnesi ile arldndan, a_func ilevi iindeki a ya x nesnesinin deeri atanyor. Bu kez global g_func isimli ilevin parametre deikeninin A snf trnden olduunu gryorsunuz. a_func ye ilevi iinde arlan global g_func ilevine bylelikle main ilevi iindeki yerel x nesnesinin deeri aktarlm olur.
C++ Kitabi (64/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

3. arlan bir ye ilev, hangi snf nesnesi iin arlmsa, o snf nesnesini ya da o snf nesnesinin adresini geri dndrebilir. Bu durum ileride anlatlacak baz aralarda youn bir biimde kullanlr. Aadaki rnei dikkatle inceleyin: class A { public: //... A a_func1(); A *a_func2(); A &a_func3(); private: //... };

A snfnn ye ilevlerinden a_func1, A snf trnden bir deerle, a_func2 ilevi A snf trnden bir adresle, a_func3 ilevi ise A snf trnden bir referansla geri dnyor. imdi bu ilevlerin tanmlarna bir bakalm: A A::a_func1() { //... return *this; } A *A::m_func2() { //... return this; } A &A::m_func3() { //... return *this; }

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.

Snf Bilinirlik Alan


C dilinde bilinirlik alanlarnn drde ayrldn anmsayn. Bunlar dardan genie doru ilev bildirim bilinirlik alan (function prototype scope), blok bilinirlik alan (blok scope), ilev bilinirlik alan (function scope) ve dosya bilinirlik alan (file scope) dr. Blok bilinirlik alan bir ismin yalnzca bir blok iinde, ilev bilinirlik alan bir ilevin her yerinde, dosya bilinirlik alan ise tm ilevler iinde bilinmesi, kullanlabilmesi anlamna gelir. C++da bu bilinirlik alanlarna ek olarak bir de snf bilinirlik alan (class scope) tanmlanmtr. Snf bilinirlik alan, bir ismin hem snf bildirimi iinde, hem de snfn tm ye ilevleri iinde bilinmesidir. Snfn elemanlar ile ye ilevleri, snf bilinirlik alan kuralna uyar. Snf bilinirlik alannn darlk genilik bakmndan ilev bilinirlik alan ile dosya bilinirlik alan arasnda bulunduuna dikkat edin. Bu durumda C++daki bilinirlik alanlar dardan genie doru,
C++ Kitabi (65/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

1. 2. 3. 4. 5.

lev bildirimi bilinirlik alan (function prototype scope) Blok bilinirlik alan (block scope) lev bilinirlik alan (function scope) Snf bilinirlik alan (class scope) Dosya bilinirlik alan (file scope)

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: #include <iostream> void func(); int a = 50; //ilev bildirimi //global deiken

class X { public: void foo(int); void func(); private: int a; }; using namespace std; void X::func() { cout << "X snfnn func isimli ye ilevi" << endl; } void func() { cout << "Global func isimli ilev" << 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: void X::foo(int a) { cout << a << endl; // Parametre deikeni olan a { int a = 30; cout << a << endl; // Blok iindeki a } func(); // ye ilev olan func }

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
C++ Kitabi (66/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

bilinirlik alanna, ye ilevler ise snf bilinirlik alanna sahiptir. imdi foo ilevinin parametre deikeninin ismini deitirelim: void X::foo(int x) { a = x; { int a = 30; cout << a << endl; } func(); }

// Snfn eleman olan a // Blok iindeki a // ye ilev olan func

imdi d bloktaki a, snfn eleman olan a olarak ele alnr. Peki, bir ye ilev iinde snfn elemanlar ya da ye ilevleriyle ayn isimli global deikenlere ya da ilevlere erimek mmkn olabilir mi? te znrlk ileci ile bu durum mmkn klnmtr.

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:

znrlk lecinin Tek Terimli nek Kullanm


Bu kullanm biiminde znrlk ilecinin tek terimi vardr. le bu durumda her zaman global olan isme eriimi gerekletirir. Aadaki rnei inceleyin: #include <iostream> using namespace std; int a = 10; int main() { int a = 20; ::a = 50; cout << a << endl; { int a = 30; ::a = 100; cout << a << endl; } cout << ::a << endl; return 0; } // Global a // Yerel a // Global a // Yerel a // Global a

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:
C++ Kitabi (67/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

#include <iostream> void func(); class X { public: void func(); void foo(); }; using namespace std; void func() { cout <<"Global func isimli ilev" << endl; } void X::func() { cout << "X snfnn func isimli ilevi" << endl; } void X::foo() { cout << "X snfnn foo isimli ilevi" << endl; func(); // X snfnn ye ilevi olan arlyor ::func(); // Global olan arlyor } //Global ilev

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.

znrlk lecinin ki Terimli Araek Kullanm


znrlk ileci iki terimli araek (binary infix) olarak da kullanlr. Byle bir kullanmda sol terim bir snf ismi, sa terim ise snfn bir eleman ya da ye ilevi olmak zorundadr.
C++ Kitabi (68/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bu kullanmla her zaman snfa ilikin olan elemana ya da ye ileve eriilir. rnein Date isimli bir snfn int trden day, month, year isimli veri elemanlar olsun. set_date bu snfn bir ye ilevi olmak zere class Date { public: void set_date(int, int, int); private: int day, month, year; }; void Date::set_date(int day, int month, int year) { Date::day = day; Date::month = month; 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.

Snfn Kurucu levleri


Bir snf nesnesi yaratldnda ismine kurucu ilev (constructor) denilen bir ye ilev derleyici tarafndan otomatik olarak arlr. Derleyici kurucu ilevleri isimlerine bakarak saptar. Kurucu ilevlerin ismi ait olduklar snfn ismidir. Kurucu ilevlerin geri dn deerleri yoktur. "Geri dn deerleri yoktur" demekle geri dn deerlerinin void olduunu anlatmak istemiyoruz. Bu ilevlerin geri dn deerleri diye bir kavramlar yoktur. Yazarken geri dn deeri yerine hi birey yazlmaz. Kurucu ilevlerinin parametrik yaps herhangi bir biimde olabilir. Aadaki snf bildirimini inceleyin: class Date { public: Date(); void display(); private: int day, month, year; };

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 yukarda da belirttiimiz gibi geri dn deerinin int ya da void olduu anlamna gelmiyor. Date snfnn kurucu ilevinin tanm dier ye ilevler gibi yaplr:

C++ Kitabi (69/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Aadaki rnekte Person isimli snfn kurucu ilevi hangisidir? class Person { public: Person(const char *, int); void display(); void set_name(const char *); void set_no(int ); // private: char *name; int no; };

// Kurucu ilev

Person snfnn kurucu ilevi, Person(const char *, int ); biiminde bildirilen ilevdir. Bu ilevin tanm aadaki gibi olabilir: Person::Person(const char *nm, int n) { // } Kurucu ilevlerin parametrik yaps herhangi bir biimde olabilir. C++da imzalar farkl ayn isimli ilevler olabildiine gre, bir snfn da farkl imzalara sahip birden fazla kurucu ilev de olabilir. Bir baka deyile kurucu ilevler de yklenebilir. rnein Date isimli bir snfn birden fazla kurucu ilevi olabilir: class Date { public: Date(); Date(int, int, int); Date(const char *); // private: int day, int month, year; };

Yukarda tanmlanan Date snf iin kurucu ilev bildirimi yaplyor: Parametre deikeni olmayan, parametre deikenli olan ve tek parametre deikenli olan. Bir snfn parametre deikeni olmayan, ya da tm parametre deikenleri varsaylan argman alan kurucu ilevine "varsaylan kurucu ilev" (default constructor) denir. Kurucu ilevler snf nesneleri yaratldnda derleyici tarafndan otomatik olarak arlr. Yani derleyici nce snf nesnesi iin bellekte yer ayrr, daha sonra uygun olan kurucu ilevi arr. Yerel deikenlerin programn aknn tanmlama noktasna geldiinde,
C++ Kitabi (70/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

global deikenlerin ise programn bellee yklenmesiyle yaratldn anmsayn. Buna gre yerel bir snf nesnesine ilikin kurucu ilev nesnenin tanmland yerde, global bir snf nesnesine ilikin kurucu ilev ise programn bellee yklenmesiyle, yani main ilevinden nce arlr. C++ dilinde main ilevinden nce alan bir kod da olabilir. Bir snfn birden fazla kurucu ilevi olabildiine gre, derleyici bunlardan hangisi aracan nasl saptar? Hangi kurucu ilevin arlaca nesnenin tanmlanma ifadesiyle belirlenir. Eer nesne isminden sonra ayra alm, ayra iine bir argman listesi yazlmsa, uygun imzaya sahip kurucu ilev arlr. rnein: Date bdate(6, 1, 1966); gibi bir tanmlamayla snfn Date(int, int, int); parametre yapsna sahip olan kurucu ilevi arlr. Burada, 6, 1 ve 1966 snfn kurucu ilevine yaplan arda, ileve gnderilen argumanlardr. Benzer biimde: Complex c(10.2, 5.3); gibi bir nesne tanmlamasnda da parametre yaps Complex(double, double); biiminde olan kurucu ilev arlabilir. Nokta ieren ve sonuna ek almam olan deimezlerin double trden deimez olarak ele alndn anmsayn. Peki, aadaki rnekte Person isimli snfn hangi kurucu ilevi arlr? Person y("Necati Ergin"); Dizgelerin ileme sokulduunda tr dntrme ilemiyle otomatik olarak const char * trne dntrldn anmsayn. Bu durumda y isimli snf nesnesinin tanmlanmasyla Person isimli snfn, const char * trnden gsterici parametresine sahip olan kurucu ilevi arlr. Eer tanmlama ilemi dier trden nesnelerde olduu gibi ayra almadan yaplmsa, nesne yaratlrken varsaylan kurucu ilev yani parametresi olmayan kurucu ilev arlr. rnein: Date date; Person person; Complex c; Yukardaki date, person ve c nesneleri iin varsaylan kurucu ilevler arlr. Eer bir snf nesnesi ilk deer verilerek tanmlanyorsa, ilk deer olarak verilen tre uygun kurucu ilev arlr. X bir snf olmak zere: X a = b tanmlamasyla X a(b) tanmlamas tamamen edeerdir. Bu durumda rnein: Person y = "Necati Ergin"; gibi bir tanmlamayla,
C++ Kitabi (71/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Person y("Necati Ergin"); tanmlamas ayn anlamdadr. Bu biimde yalnzca tek parametreli kurucu ilevler arlabilir. Zaten snf nesnelerine birden fazla deerle ilkdeer(ilk deer) verilemez. Snf nesnelerine yaplarda olduu gibi kme ayralar arasnda ilkdeer(ilk deer) verme de sz konusu deildir. rnein, X a = {10, 20, 30}; // 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> class Date { public: Date(); Date(int, int, int); void display(); private: int day, month, year; }; using namespace std; Date::Date() { time_t timer = time(0); tm *tptr = localtime(&timer); day = tptr -> tm_mday; month = tptr -> tm_mon + 1; year = tptr -> tm_year + 1900; } Date::Date(int d, int m, int y) { day = d; month = m; year = y; } void Date::display() { cout << day <<'/' << month << '/' << year; } int main() { Date date1; date1.display(); Date date2(28, 10, 1998); cout << endl; date2.display(); cout << endl; return 0; } rneimizde, Date date1;
C++ Kitabi (72/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

tanmlamasyla snfn varsaylan kurucu ilevi arlyor. Varsaylan kurucu ilev sistem tarihini alarak snfn elemanlarna yerletiriyor. rnein, kurucu ilev altrldnda date nesnesinin elemanlar yle doldurulmu olsun.

rneimizde daha sonra, date1.display(); arsnn yapldn gryorsunuz. display ilevi ile ekrana yazdrlan day, month ve year, date1 nesnesinin elemanlardr deil mi? Yerel snf nesnelerine ilikin kurucu ilevler, programn ak nesnenin tanmlama noktasna geldiinde arlacana gre, Date date2(4, 3, 1964); gibi bir tanmlamayla Date(int, int, int); bildirimine uygun olan kurucu ilev arlr. Bu ilev argman olarak gnderilen deerleri snfn elemanlarna yerletirir. Bu durumda date2 nesnesinin elemanlar kurucu ilev arldktan sonra yle olur:

rneimizde daha sonra, date2.display() ile date2 nesnesinin veri elemanlarnn yazdrldn gryorsunuz. Global deikenler programn bellee yklenmesiyle yaratldklarna gre, global snf nesnelerine ilikin kurucu ilevler de main ilevinden nce altrlr. Aadaki rnei inceleyin:

C++ Kitabi (73/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

class X { int a; public: X(int); void display(); }; #include <iostream> using namespace std; X::X(int n) { a = n; cout << "X snfnn kurucu ilevi" << endl; } void X::display() { cout << a << endl; } X g = 20; int main() { cout << "main ilevi balad" << endl; g.display(); return 0; } rneimizde g snf nesnesine ilikin kurucu ilev main ilevinden nce arlr. Ekranda unlar grmeniz gerekir: X snfnn kurucu ilevi main ilevi balad 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: class Complex { double real, imag; public: Complex(double, double = 0.); void display(); }; Complex::Complex(double r, double i) { real = r; imag = i; }

void Complex::display() {
C++ Kitabi (74/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin cout << real; if (imag != 0) cout << "+" << imag; cout <<'\n'; } int main() { Complex c1(10); c1.display();

// Complex x(10, 0)

Complex c2(10, 20); c2.display(); return 0; } 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: class Complex { public: Complex(double = 0, double // };

= 0);

Aadaki tanmlamalarn hepsi geerlidir: Complex x; Complex y(10); Complex z(10, 20); // // Complex(0, 0) ile ayn anlamda Complex(10, 0) ile ayn anlamda

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: class Point { public: void set(int, int); void display(); private: int x, y; };

Grld gibi, Point snfnn hi kurucu ilevi yok. Bu durumda varsaylan kurucu ilev varm gibi nesne tanmlanmas, herhangi bir hataya yol amaz. Point pt; // 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.

C++ Kitabi (75/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: class Point { public: Point(int, int); //... private: int x, y; }; gibi bir snf bildiriminden sonra, Point pt; //Geersiz!

Biiminde bir nesne tanmlanamaz. Fakat tabi, Point pt(10, 20); gibi bir nesne tanmlamas geerlidir.

Kurucu levin Snfn protected ya da private Blmnde Bildirilmesi


Bir ye ilev nasl snfn herhangi bir blmnde bildirilebiliyorsa, snfn kurucu ilevi de snfn herhangi bir blmnde bildirilebilir. Yani kurucu ilevin snfn public blmnde bildirilmesi zorunda deildir. Kurucu ilevin snfn private blmnde bildirildiini dnelim. Peki, bu durumda ne olur? Global bir ilev iinde snfn private blmne eriilemeyeceine gre, bir snf nesnesi yaratlmas durumunda derleme zamannda hata oluur, deil mi? Aadaki rnei inceleyin: class A { private: A(); void foo(); }; int main() { A a; //Geersiz! }

Bir ye ilev iinden, private blmde bildirilen bir baka ye ilev arlabilir, deil mi? Bu durumda, rnein A snfnn bir baka ye ilevi olan foo ilevi iinde A snf trnden bir nesne tanmlanabilir: void A::foo() { A a; //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:

C++ Kitabi (76/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class X { public: X(); }; Void foo() { X object; X.X(); }

// Geersiz!

Snfn Sonlandrc levi


Bir snf nesnesi bellekten boaltlaca zaman derleyici tarafndan arlan ye ileve snfn sonlandrc ilevi (destructor) denir. Nasl kurucu ilev nesne yaratldnda arlyorsa, sonlandrc ilev de nesnenin bellekten boaltlaca zaman, yani nesnenin mr sona erdiinde arlr. Sonlandrc ilevin ismi snf ismiyle ayndr. Ancak ismin nne ~ atomu eklenmitir. Yani sonlandrc ilevin ismi ~Snfsmi biimindedir. Aadaki snf bildirimini inceleyin: class Person { public: Person(const char *); //Dier ye ilevler ~Person(); private: char *name; int no; };

// Kurucu ilev // Sonlandrc ilev

Person snfnn sonlandrc ilevi, ~Person(); bildirimi ile belirtilmi olandr. Bu ilevin tanm da kurala uygun olarak, Person::~Person() { //... } biiminde yaplr. Sonlandrc ilevlerin de geri dn deerleri yoktur. Geri dn deerinin tr yerine hi birey yazlmaz. Bu durum onlarn int ya da void geri dn deerine sahip olduu anlamna gelmez. Snfn sonlandrc ilevi tektir. Parametresi void olmak zorundadr. Yani parametresi olmamak zorundadr. C++da parametre ayrac iine void yazmakla hibir ey yazmamann ayn anlama geldiini anmsayn. Bu durumda snfn birden fazla sonlandrc ilevi de olamaz. Snfn sonlandrc ilevi, snf nesnesi bellekten boaltlaca zaman arlr. Yerel deikenlerin program aknn tanmlanma blounu bitirmesiyle, global deikenlerin ise programn sonlanmasyla bellekten boaltldn anmsayn. Bu durumda yerel snf nesnelerine ilikin sonlandrc ilevler deikenin tanmland blounun sonunda, global snf nesnelerine ilikin sonlandrc ilevler de main ilevi sonlandnda arlr. Aadaki rnei inceleyin:

C++ Kitabi (77/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

#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.
C++ Kitabi (78/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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? class X { // }; X a; X b; int main() { X c; X d; // { X e; // .. } }

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: int foo() { X a; // { X b; // if (func()) return -1; // } X c; // return 0; }

Yukardaki foo isimli ilevde, i blokta func ilevi arlarak geri dn deeri snanyor. Bu ilevin geri dn deeri sfr d bir deerse, ilev sonlandrlyor. Derleyici ilev sonlanmadan nce, yaratlm olan a ve b nesneleri iin sonlandrc ilevleri de arr. c
C++ Kitabi (79/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

nesnesinin programn aknn return ileminin yapld noktaya gelene kadar tanmlanmadn gryorsunuz. Tabii bu durumda c nesnesi iin sonlandrc ilev arlmaz. Bir snf iin sonlandrc ilev yazmak zorunlu deildir. Sonlandrc ilev yazlmamsa, derleyici snfn sonlandrc ilevini kendisi yazar. Derleyicinin yazaca sonlandrc ilevin imdilik syle olduunu varsayabilirsiniz: Myclass::~Myclass() { } Zaten her snfn sonlandrc ileve sahip olmas gerekmez. Fakat kurucu ilev ile yaplanlarn otomatik olarak geri alnmas isteniyorsa sonlandrc ilevin yazlmas anlamldr. Genel olarak dsal bir kaynak kullanan snflar, kurucu ilevleriyle kendilerine balanan bir kayna, sonlandrc ilevler ile serbest brakrlar ya da geri verirler. Son olarak herhangi bir ilev iinde tm programn exit ilevi ile sonlandrlmas zerinde duralm. exit ilevi nerede arlm olursa olsun, program iinde tanmlanm tm global snf nesneleri iin eer varsa sonlandrc ilevler arlr. Tabii exit ilevi ile programn sonlandrlmas durumunda yerel snf nesneleri iin sonlandrc ilevler arlmaz.

Kurucu ve Sonlandrc levler Ne Amala Kullanlr


Kurucu ilevler snfn elemanlarna belirli ilkdeerleri vermek ve eitli ilk ilemleri yapmak amacyla tanmlanr. Bir snf nesnesi tanmlandnda eitli ilk ilemlerin kurucu ilevler tarafndan gizli bir biimde yaplmas alglamay kolaylatrr. Bylece snf nesnesini kullanan kodlar (client), snf nesnesini kullanmadan nce herhangi bir ilem yapmak zorunda kalmazl. Gerekli ilemler snfn kendi kodu tarafndan yaplr. Bu ekilde bir snf nesnesinin kararl bir ekilde ilk deerini almas salanr. lkdeer verme ilemi otomatik olarak arlan bir ilev tarafndan deil de, snf kullanan kod paras tarafndan aka arlmas gereken bir ilev tarafndan yaplsayd, hata oluma riski ok daha fazla olurdu, deil mi? Kurucu ilevler, veri gizleme (data hiding) prensibinin gerekletirilmesini salayan temel aralardan biridir. Ayn ekilde sonlandrc ilev ise kurucu ilev ile yaplan ilk ilemlerin geri alnmas iin kullanlr. rnein, kurucu ilev bir dosyay amsa, sonlandrc ilevi dosyay kapatabilir. Ya da kurucu ilev seri portu eitli deerlerle ayarlam olabilir. Bu durumda sonlandrc ilevi de bu ayarlar eski haline getirebilir. Ancak en sk karlalan durum, kurucu ilevinin(ilevin) new ileci ile dinamik olarak bellekte bir yer ayrmas ve sonlandrc ilevin de ayrlan yeri delete ileci ile bu alan geri vermesidir. Aadaki rnei inceleyin: #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;

C++ Kitabi (80/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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

index << endl;

void Array::set_item(int index, int val) { if (index < 0 || index >= size) { cout << "Geersiz index: " << index << endl; exit(EXIT_FAILURE); } pArray[index] = val; } int Array::get_max() { int max = pArray[0]; for (int i = 1; i < size; ++i) if (max < pArray[i]) max = pArray[i]; return max; } const int size = 10; int main() { Array x = size; for (int i = 0; i < size; ++i) x.set_item(i, rand()); x.display(); cout << "max = " << x.get_max() << endl; return 0; }
C++ Kitabi (81/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

rneimizde int trden bir dizi bir snfla temsil ediliyor. Snfn kurucu ilev bir dizi iin free store alanndan bir blok ayrm sonlandrc ilevi de bu dinamik blou geri veriyor. get_item ve set_item ilevlerinin snr kontrol yaparak diziye eleman yerletirme ve dizinin elemannn deerini alma ilemlerini yaptn gryorsunuz. Ayrca, get_max isimli ilev dizinin en byk elemann buluyor. display ilevi ise dizinin btn elemanlarn ekrana yazdryor.

Snf Trnden Gstericiler ve Adresler


Bir snf nesnesinin adresi alnabilir. Elde edilen adres nesnenin ilikin olduu snfn trndendir, ayn trden bir gsterici deikene atanmaldr. Daha nceki rneklerde bildirilen Date isimli snfa ilikin bir gsterici deiken tanmlanm olsun: Date *p; ile p gsterici deikeninin gsterdii yer, yani *p, Date snf trndendir. Baka bir deyile, p gstericisinin gsterdii yerde Date snf trnden bir nesne vardr. Derleyici bu adresten balayarak ilk sizeof(int) kadar byte snfn day eleman, sonraki iki sizeof(int) kadar byte ise srasyla month ve year elemanlar olarak ele alr. imdi bir snf nesnesinin adresinin bir snf gstericisine atandnda neler olduunu adm adm inceleyelim: Aadaki ekil int trnn 4 byte olduu varsaymyla izilmitir. 00501FC0 yalnzca konuya aklk getirmek amacyla kullanlan rastgele bir adrestir. Date date; // Date snfnn veri elemanlar iin yer ayrlyor.

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,
C++ Kitabi (82/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: int main() { Date x; Date *p; p = &x; p->display(); return 0; }

Snfn kurucu ilevi yalnzca snf nesnesi yaratlrken arlr. Bir snf trnden gsterici deiken tanmlandnda kurucu ilevi arlmaz. rnein, Date date; Date *p; // Kurucu ilev arlr, nk nesne yaratlm! // Kurucu ilev arlmaz, yalnzca gsterici yaratlm!

Snf Trnden Referanslar


Bir referans deikeni bir snf nesnesinin de yerine geebilir. Snf trnden referanslar ayn trden bir snf nesnesi ile ilkdeer vererek tanmlanr: Complex a(10, 20); Complex &r = a; Burada Complex snf trnden r isimli referansa yine Complex snf trnden a nesnesiyle ilkdeer veriliyor. Bylece artk r referans a nesnesinin yerine geer. rnin kullanld her yerde a nesnesi kullanlm olur. r referans a nesnesinin yerine getiine gre, referans yoluyla snfn elemanlarna ve ye ilevlerine -tpk yaplarda olduu gibinokta ileci ile eriilir. rnein Complex snfnn display isimli bir ye ilevi olduunu varsayalm. Bu ye ilev r referans ile, r.display();
C++ Kitabi (83/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

biiminde arlabilir. Bir referans ile bir ye ilev arldnda, ye ilev referansn yerine getii nesnenin elemanlarn kullanr. rnein yukardaki arda display ye ilevi r referansnn yerine getii nesnenin elemanlarn kullanr. Snf trnden bir referansa bir deimez ile ya da baka trden bir nesneyle ilkdeer verme ilemi baz durumlarda geerli olabilir. Bu durumu "dntren kurucu ilev" (conversion constructor) bal altnda ele alacaz. Aadaki rnei yazarak altrn: #include <iostream> class Complex { public: Complex(double = 0, double = 0); void display(); private: double real, imag; }; using namespace std; Complex::Complex(double r, double i) { real = r; imag = i; } void Complex::display() { cout << real; if (imag > 0) cout << "+ "; else cout << " "; cout << imag << "i"; } int main() { Complex a(10, -20); Complex &r = a; r.display(); return 0; } ncelikle bir nokta zerinde aklama yapalm. Complex snfnn kurucu ilevinin parametre deikenleri varsaylan deerler alyor. Bu kurucu ilev, hem tek parametreli kurucu ilev hem de varsaylan kurucu ilev olarak kullanlabilir. rneimizde r isimli referansa a nesnesi ile ilkdeer verildiini gryorsunuz. Bu durumda r referans a deikeninin yerine geer. Artk, r.display() arsnda, display ye ilevi r referansnn yerine getii elemanlar kullanr.

Snf Nesnelerinin levlere Geirilmesi


Bir snf nesnesiyle ilgili ilerin, bir snfn ye ilevleri tarafndan yaplmas zorunlu deildir. phesiz global ilevler de snf nesneleri zerinde ilemler yapabilir. imdi snf nesnelerinin ilevlere geirilmesi zerinde duracaz. Snf nesnelerinin ilevlere geirilmesi, tpk yaplarda olduu gibi yolla yaplabilir.

Snf Nesnesinin Deerinin leve Aktarlmas


Bu yntem bildiimiz "deerle arma" (call by value) ynteminden baka bir ey deildir. Bu yntemde ilevin parametre deikeni snf trnden bir nesne olur. lev de
C++ Kitabi (84/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

baka bir snf nesnesinin deeri ile arlr. nk C++da tpk yaplar gibi ayn trden snf nesneleri de birbirlerine dorudan atanabilir. Byle bir atama srasnda snfn elemanlar karlkl olarak birbirine kopyalanr. C++da bir snf nesnesi baka bir snf nesnesi ile ilk deer verilerek tanmlanrsa, tanmlanan snf nesnesi iin zel bir kurucu ilev arlr. Bu kurucu ileve "kopyalayan kurucu ilev" (copy constructor) denir. Programc kopyalayan kurucu ilevi tanmlamaz ise derleyici bu ilevi kendi tanmlar. Derleyicinin yazd kopyalayan kurucu ilev yaratlan snf nesnesinin tm elemanlarna, ilkdeer veren snf nesnesinin elemanlarnn deerini atar. Kopyalayan kurucu ilevi ileride ayr bir balk altnda ele alacaz. Daha nce verdiimiz Date snfnnn kullanld aadaki rnei inceleyin: void func(Date d) { // a.display(); } int main() { Date date; func(date); return 0; }

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.

Nesnenin Adresinin leve Geirilmesi


Bu ar biiminde ileve bir snf nesnesinin adresi gnderilir. levin parametre deikeni de ayn snf trnden bir gsterici deiken olur. Bu durumda yalnzca snf nesnesine ilikin veri blounun adresi ileve geirilir. lev iinde nesnenin elemanlarna ve snfn ye ilevlerine -> ileci ile eriilebilir. Aadaki rnei inceleyin: void func(Date *pdate) { // pdate -> display(); } int main() { Date date; func(&date); return 0; }

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.

Nesnenin Referans Yoluyla leve Geirilmesi


C++ Kitabi (85/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Referanslar da aslnda adres tutan deikenler olduuna gre, nesnenin adresi referans yoluyla da ilevlere geirilebilir. Bu yntemde ilev snf nesnesinin kendisi ile arlr. levin parametre deikeni de snf trnden bir referans olur. levin parametre deikeni olan referans ileve gnderilen argman olan nesnenin yerine geer. Artk ilev iinde nesnenin elemanlarna ve snfn ye ilevlerine nokta ilecinin terimi olan referans ile eriilir. Aadaki rnei de inceleyin: void func(Date &rdate) { // rdate.display(); } int main() { Date date; func(date); return 0; }

Snf nesnesinin referans yoluyla aktarm gsterici yoluyla aktarm ile edeer verimliliktedir. Ancak referanslar konusunda da belirtildii gibi, bir ilev ald adresteki bilgiyi deitirmiyorsa okunabilirlik bakmndan const referans ya da const gsterici tercih edilmelidir.

Snf Blmlerinin ve Eriim Kurallarnn Anlam


Daha nce de akladmz gibi snfn ye ilevleri ve elemanlar snfn istenilen bir blmne yerletirilebilir. Yani programc bu konuda zgrdr. Ancak genellikle snfn darya hizmet veren ye ilevlerinin bildirimleri snfn public blmne, snfn veri elemanlar ise snfn private blmne yerletirilir. Yukarda verilen rnekleri incelediinizde bu ynde hareket edildiini greceksiniz. Snfn darya hizmet veren ye ilevleri snfn public, snfn elemanlar da snfn private blme yerletirilirse, dardan snf nesnesi ya da gstericisi yoluyla ye ilevlere eriilebilir fakat snfn elemanlarna eriilemez. Bu durumda elemanlar dorudan deil ancak snfn ye ilevleri yardmyla kullanlabilir.

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:

C++ Kitabi (86/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

class Date { private: int day, month, year;(mDay, mMonth, mYear) public: Date(); Date(int d, int m, int y); void display(); //Snfn elemanlarnn deerlerini alan ye ilevler int get_day (); int get_month(); int get_year(); //Snfn elemanlarna deer yerletiren ye ilevler void set(int, int, int); void set_day(int); void set_month(int); void set_year(int); // Dier ye ilevler }; Yukarda verilen Date snfnn day, month ve year elemanlar private blme yerletirilmi. Bu yzden bu elemanlara dorudan erimek mmkn olmaz. rnein date, Date trnden bir nesne olsun. date nesnesinin year elemannn deerini almak ya da year elemanna bir deer verilmek istensin. x = date.year; date. year = 2000; //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 (87/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

day = tptr -> tm_mday; month = tptr -> tm_mon + 1; year = tptr -> tm_year + 1900; } Date::Date(int d, int m, int y) { set(d, m, y); } void Date::display() { cout << day << " " << month << " " << year; } int Date::get_day() { return day; } int Date::get_month() { return month; } int Date::get_year() { return year; } void Date::set(int d, int m, int y) { day = d; month = m; year = y; } void Date::set_day(int d) { day = d; } void Date::set_month(int m) { month = m; } void Date::set_year(int y) { year = y; } Bu rnekteki day, month ve year elemanlarnn snfn public blmnde bildirilmi olduunu gryorsunuz. day, month ve year elemanlarna erimek iin ye ilevleri kullanmaya gerek yok, deil mi? Eriim dorudan gerekletirilebilir. imdi eriimin dorudan yapld bir kod rnei verelim:

C++ Kitabi (88/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin int main() { Date date; int d, m, y; d = (date.day > 15) ? date.day 1 : date.day + 1; m = (date.month > 6) ? date.month 1 : date.month + 1; date.set_date (d, m, y); // return 0; }

Burada da Date snf trnden bir nesne tanmlanm. Date snf nesnesinin elemanlarna snfn kurucu ilevi tarafndan o anki tarih bilgisi yerletirilir. Daha sonra bu tarih bilgisinin gn ve ay deerlerine baklm ve duruma gre bu deerler bir artrlm ya da azaltlm. Sonra da elde edilen deerin yeniden snfn veri elemanlarna set_date ye ilevi ile yerletirildiini gryorsunuz. Buradaki kodu ok anlaml bulmayabilirsiniz; rnek yalnzca snf kullanan koda rnek olarak verildi. Tabii siz snf kullanan kodun yalnzca main olduunu dnmemelisiniz. Snfn elemanlarn private blme yerletirip onlara ye ilevler yoluyla eriilmesinin bir faydas olabilir mi? imdi buna da bir rnek verelim: Yukardaki Date snfnn veri elemanlarnn genel yapsnn deitirildiini dnelim. rnein artk tarih bilgisinin int trden day, month year veri elemanlarnda deil "dd/mm/yyyy" biiminde 11 elemanl char trden bir dizi iinde tutulmasna karar verilmi olsun. Bu durumda snfn elemanlarn dorudan kullanan kodun bir geerlilii kalacak m? Hayr! rnein, d = (x.day > 15) ? x.day 1 : x.day + 1; m = (x.month > 6) ? x.month 1 : x.month + 1; Artk Date snfnn day ve month iiminde bir eleman olmadna gre bu kodlar da geersiz hale gelir. O halde snf kullanan btn kodlar yeniden yazlmaldr. te eer bu elemanlar snfn public blmne yerletirmeseydi o zaman bu elemanlara dorudan eriilmeyecekti. Onlara erimek iin zorunlu olarak snfn public ye ilevleri kullanlacakt. rnein, snfn day, month, year elemanlar snfn private blmnde olsayd, kullanc kodlar da yle yazlm olurdu: int main() { Date x; int d, m, y; d = (x.get_day() > 15) ? x.get_day() 1 : x.get_day() + 1; m = (x.get_month() > 6) ? x.get_month() 1 : x.get_month() + 1; x.set_day(d, m, y); return 0; }

imdi snfn veri yaps deitirildiinde snf kullanan kodlarn yeniden dzenlenmesine gerek kalmaz. Tek yaplacak ey, ye ilevlerin yeni veri yapsna uygun olarak yeniden yazlmalardr. rnein, tarih bilgisi karakter dizisi biiminde tutulacak olduunda snfn ye ilevleri aadaki gibi deitirilirse, snf kullanan kodlarn hi biri deitirilmek zorunda kalmaz:

C++ Kitabi (89/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Date { public: // private: char date[11]; }; using namespace std; Date::Date() { time_t timer = time(NULL); struct tm *tptr = localtime(&timer); sprintf(date, "%02d/%02d/%04d", tptr->tm_mday, tptr->tm_mon + 1, tptr>tm_year + 1900); } Date::Date(int d, int m, int y) { sprintf(date, "%02d/%02d/%04d", d, m, y); } void Date::display() { puts(date); } int Date::get_day() { return atoi(date); } int Date::get_month() { return atoi(date + 3); } int Date::get_year() { return atoi(date + 6); } void Date::set_date(int d, int m, int y) { day = d; month = m; year = y; } void Date::set_day(int d) { sprintf(date, "%02d", d); date[2] = '/'; } void Date::set_month(int m) { sprintf(date + 3, "%02d", d); date[5] = '/'; }

C++ Kitabi (90/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void Date::set_year(int y) { sprintf(date + 5, "%02d", d); date[5] = '/'; } int main() { Date date; int d, m, y; d = (date.get_day() > 15) ? date.get_day() -1 : date.get_day() + 1; m = (date.get_month() > 6) ? date.get_month() - 1 : date.get_month() + 1; date.set_day(d, m, y); return 0; }

Gelin sonucu zetleyelim: Snfn isel yaps deitirildiinde snf kullanan kodlarn bu deiimden etkilenmesi istenmiyorsa, snfn elemanlar private blme yerletirilmeli ve eriimler ye ilevlerle yaplmaldr. Bylece snfn veri yaps deitirildiinde yalnzca ye ilevleri yeniden yazmak yeterli olur.

Dinamik Snf Nesneleri


new ileciyle yalnzca C+ n doal veri trleri iin deil, snflar ve yaplar iin de dinamik nesneler yaratlabilir: rnein: class Date { public: Date(); Date (int, int, int); // Dier ye ilevler private: int day, month, year; }; Date *p = new Date;

deyimiyle sizeof(Date) uzunluunda dinamik bir blok elde ediliyor. Elde edilen dinamik bloun balang adresi p gsterici deikenine atanyor. Bylelikle p gsterici deikeni Date nesnesini gsteriyor. Artk p gsterici deikeni ok ileci ile kullanlarak ye ilevler arlabilir. rnein, p->display(); gibi bir ilemde display ye ilevi p gstericisinin iindeki adreste bulunan yani dinamik olarak elde edilmi olan nesnenin elemanlarn kullanr. Snfn kurucu ilevi yalnzca tanmlama yntemiyle nesne yaratldnda deil, dinamik olarak nesne yaratldnda da arlr. rnein, p = new Date; //Kurucu ilev arlr

ilemiyle Date trnden dinamik bir nesne yaratlyor. Yaratlan bu dinamik nesne iin de kurucu ilev arlr. Yani yukardaki ilemde srasyla unlar yaplr: 1. sizeof(Date) uzunluunda free store zerinde dinamik bir blok elde edilir.
C++ Kitabi (91/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

2. Elde edilen dinamik blok iin kurucu ilev arlr. arlan kurucu ileve this gstericisi olarak elde edilen bloun balang adresi geirilir. 3. Yaratlan nesnenin adresi p gstericisine atanr. Peki, dinamik olarak yaratlan snf nesneleri iin hangi kurucu ilev arlr? te eer snf isminden sonra ayra alp parametre listesi yazlmamsa varsaylan kurucu ilevi (default constructor) arlr. rnek verelim: p = new Date; burada yaratlan alan iin varsaylan varsaylan kurucu ilev arlr. Eer snf isminden sonra ayra alp bir parametre listesi yazlrsa, o zaman parametre deikeni listesine uygun olan kurucu ilevi arlr. rnein: p = new Date(2, 2, 1989); gibi bir yer ayrma ilemiyle parametrik yaps Date(int, int, int); biiminde olan kurucu ilev arlr. Peki aadaki gibi bir yer ayrma ileminde Person snfnn hangi kurucu ilevi arlr? p = new Person("Erdem Eker"); Dizgeler const char trden bir adres belirttiklerine gre, parametresi char trden gsterici olan kurucu ilev arlr? Ayrca, snf isminden sonra ayra iine birey yazlmamas da geerli bir durumdur. Bu durum, yaratlan dinamik nesne iin varsaylan kurucu ilevin arlaca anlamna gelir. p = new Date(); ile p = new Date; tamamen ayn anlamdadr. new[] ileci ile birden fazla dinamik nesne yaratlabilir. rnein: p = new Date[n]; burada n tane Date trnden nesnenin sabilecei byklkte bir bellek blou elde ediliyor. Elde edilen bloun balang adresi p gsterici deikenine atanyor.

Bu biimde birden fazla snf nesnesi iin yer ayrldnda her snf nesnesi iin tek tek varsaylan kurucu ilev arlr. Snf nesneleri iin baka bir kurucu ilevi armann yolu yoktur. Aadaki gibi bir szdizimin geerli olabileceini dnebilirsiniz:
C++ Kitabi (92/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin p = new Date[10](10, 10, 20); Ancak byle bir szdizim geerli deildir! C++da dinamik bellek ynetimi iin neden malloc ilevi yerine new gibi bir ilecin kullanldn imdi belki de daha iyi anlayabilirsiniz. Dinamik yer elde etme ileminden sonra derleyicinin kurucu ilevi arabilmesi iin, yer ayrma ileminin hangi trden nesne iin yapldnn derleme zamannda bilinmesi gerekir. Eer byle bir anahtar szck olmasayd, yer ayrma ilemleri yine malloc ileviyle yaplyor olsayd, derleyici bir snf nesnesi iin yer ayrma ilemi yapldn nasl anlayabilirdi? Bir snf nesnesi iin dinamik olarak ayrlan bellek blou delete ileci ile free store alanna geri verilebilir. Bu durumda derleyici, dinamik bellek alann geri vermeden nce snfn sonlandrc ilevini (destructor) arr. rnein p, Date trnden bir adres olsun: delete p; ilemiyle nce p adresindeki nesne iin sonlandrc ilevi arlr. Daha sonra bu nesne iin ayrlm olan alan geri verilir. Eer bir dizi snf nesnesi iin dinamik olarak yer ayrlmsa, derleyici tm dinamik nesneler iin tek tek sonlandrc ilevleri arr: #include <iostream> class A { public: A(); ~A(); }; A::A() { std::cout << "A::A()" << std::endl; } A::~A() { std::cout << "A::~A()" << std::endl; } int main() { A *pd = new A[5]; delete[] pd; 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.

Dinamik Snf Nesneleri Ne Amala Kullanlr


Bildiimiz gibi, kurucu ilevler nesne yaratldnda, sonlandrc ilevler ise nesne bellekten boaltlaca zaman arlr. Bu durumda yerel snf nesnelerinin yaratlmas ve bellekten boaltlmas tanmlama noktasna baldr. Bir snf nesnesinin istenilen bir noktada yaratlmasn ve istenilen bir noktada yok edilmesi ancak dinamik bellek ynetimiyle salanabilir. rnein:

C++ Kitabi (93/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin { p = new X; } delete p; Nesnelerin yaratlp boaltlma yerlerine bakn. Nesne yerel olarak yaratlsayd bu blok yapsyla byle bir sonucu elde edilebilir miydi? Bir snfn bir ye ilevi iinde baka bir snf nesnesinin yaratlmas ve baka bir ye ilev iinde de yok edilmesi durumlarna sklkla rastlanr. rnein: void Wnd::initialize() { pScr = new Scr; //... } void Wnd::close() { //... delete pScr; }

Burada Scr snf trnden nesne Wnd snfnn initialize ye ilevi iinde dinamik olarak yaratlyor, close ye ilevi iinde yok ediliyor. Bu ilem ancak nesnenin dinamik olarak yaratlmasyla gerekletirilebilir. Bu rneklerden de grld gibi nesnenin dinamik olarak yaratlmasnn asl nedeni dinamik bellek kullanmak deildir. Snfn kurucu ilevinin ve snfn sonlandrc ilevin istenilen yerlerde arlmasn salamaktr.

C++ Kitabi (94/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

const ye levler ve const Snf Nesneleri


ncelikle const anahtar szcnn daha nce rendiimiz anlamlarn hatrlayalm: const anahtar szc bir deikenin tanmlanmasnda kullanldnda, tanmlanan deikenin deerinin deitirilemeyeceini gsteriyordu: const int x = 5; x = 10; // x nesnesinin deeri deitirilemez. //Geersiz

Deikenin bir gsterici olmas durumunda ise, const anahtar szcnn verdii anlam anahtar szcn kullanld yere gre deiiyordu: int x = 5; const int *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. int x = 5, y = 10; int * const ptr = &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. int x = 5, y = 10; const int *const ptr = &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 : const char *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. int x = 5; const int &r = x; //Geersiz! r = 20; const char &func(); func() = 5; //Geersiz!

const Anahtar Szcnn Yeni Bir Anlam


C++ dilinde bir ye ilev const anahtar szc ile bildirilebilir. Bunun iin const anahtar szcn hem ilevin bildiriminde hem de ilevin tanmnda, parametre ayracnn sana yerletirmek gerekir. rnein:
C++ Kitabi (95/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Date { public: Date(int, int, int); void set(int, int, int); int get_day()const; int get_mon()const; int get_year()const; void display()const; // private: int day, mon, year; };

Yukardaki rnekte Date isimli snfn get_day, get_mon, get_year ve display isimli ye ilevleri const ye ilevler olarak bildiriliyor. Bu ilevlerin tanmlar da aadaki gibi yaplabilir: #include <iostream> int Date::get_day() const { return day; } int Date::get_mon() const { return mon; } int Date::get_year() const { return year; } void Date::display() const { std::cout << day << " " << mon << " " << year; }

const anahtar szcnn yalnzca ilevin bildiriminde deil, ilevin tanmnda da yazlmas zorunludur. Yalnzca birinde yazlm olmas geersizdir. Ayrca const anahtar szcnn yerletirildii yere de dikkat edilmelidir. Eer bu anahtar szck en sola konulmu olsayd, ilevin geri dn deerinin const olduu, yani deitirilemeyecei anlamna gelirdi.

const ye lev ne Anlama Gelir


const bir ye ilev ait olduu snfn elemanlarn deitiremez. Yukardaki rnekte get_day, get_mon, get_year ve display ilevleri Date snfnn day, mon, year elemanlarnn deerlerini kullanabilir. Ancak ilerindeki deerleri deitiremez. day, mon, year isimli elemanlar snfn public blmnde olsalard da bu ilevler tarafndan deitirilemezlerdi. const anahtar szc bu kullanm ile okunabilirlii artrr. Kodu inceleyen kii bu ilevlerin elemanlarn deitirmeyeceini bilirse, kod hakknda daha fazla bilgi edinmi olur. yi bir tasarmda const anahtar szc kararl bir biimde kullanlmaldr. Eer kararl bir biimde kullanlrsa artk const olmayan ye ilevlerin snfn elemanlarn deitirecei dnlebilir. Yalnzca ye ilevler const olabilir. Global bir ilev const anahtar szc ile tanmlanamaz.

C++ Kitabi (96/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

const ye levlerin Snfn Dier ye levlerini armas


Bir ye ilev iinde baka bir ye ilevin dorudan arlabildiini, bu durumda arlan ye ilevin aran ye ilev ile ayn snf nesnesinin elemanlarn kullandn hatrlayalm. const bir ye ilev iinde const olmayan bir ye ilevin arlmas geerli deildir. Yukardaki rnekte Date snfnn display ye ilevi const bir ye ilevdir. Bu ilevin const olmayan bir ye ilevi armas geersizdir. Eer bu duruma derleyici tarafndan izin verilseydi display ilevi rnein set ilevini ararak snfn elemanlarn dolayl bir biimde deitirebilirdi: void Date::set(int d, int m, int y) { day = d; mon = m; year = y; } int Date::display()const { set(3, 5, 1980); //Gecersiz. cout << day << " " << mon << " " << year; }

Yukardaki rnekte Date snfnn display ilevi iinde yine ayn snfn set isimli ilevine yaplan ar geersizdir. const bir ye ilev const olmayan bir ye ilevi aramaz.

Snf Elemanlarnn Adresine Geri Dnen const ye levler


const bir ye ilev iinde snfn elemanlarnn const olduu varsaylr. Eer const bir ye ilev snfn bir elemann adresi ile geri dnecekse, elemannn const olduu varsayldndan ilevin geri dn deerinin de const bir gsterici ya da referans olmas gerekir. class A { int a; public: //... int *get_adr() const; }; int *A::get_adr() const { return &a; //Geersiz }

Yukardaki rnekte A snfnn get_adr ilevi const bir ye ilevdir. levin tanm iinde, hangi snf nesnesi iin arlmsa, o nesnenin elemanlar da const olarak ele alnr. Bu durumda yukardaki ilevde const bir nesnenin adresi gsterdii yer const olmayan bir gstericiye atanr. Hata durumunun ortadan kaldrlmas iin, ya ilevin geri dn deeri const int * olmal ya da get_adr ilevi const ye ilev olmamaldr. phesiz bir referansa geri dnen ye ilevler iin de yukardaki anlatlan durum geerlidir. Yani const bir ye ilev snfn elamannn kendisi ile dnecekse bu durumda ilevin geri dn deeri const referans olmaldr.

Kurucu ve Sonlandrc levler const Olamaz


Snfn kurucu ve sonlandrc ilevleri const olamaz. Kurucu ilevler snfn elemanlarna ilkdeer vermek amacyla kullanldna gre const olmalarnn zaten bir anlam olmaz.

C++ Kitabi (97/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Benzer biimde sonlandrc ilevlerin de snfn elemanlarn deitirmesi gerekebildii iin const olarak tanmlanmas yasaklanmtr.

Snf Nesnelerinin Bitsel ve Soyut Durumlar


Bir snf nesnesi bellekte bir yer kaplar, bu ynyle nesne bytelar ve bytelar oluturan bitler topluluudur. Ancak snf nesnesi problem dzleminde bir varla karlk gelir. Nesnenin elemanlarnn bitsel deerleri deitiinde nesnenin gzlenebilir durumunda bir deiiklik olmayabilir. Dier taraftan nesnenin elemanlarnn bitsel deerlerini deitirmeksizin, nesnenin gzlenebilir durumunda bir deiiklik yaratmak da olasdr. Bir snf nesnesi bellekte kendi kaplad yerin dnda bir ya da birden fazla kayna kontrol ediyor olabilir. rnein baz snf nesneleri, dinamik olarak elde edilmi bir bellek alann kontrol eder. Aadaki rnei inceleyin: class Name { char *pstr; int len; public: Name(const char *); void set_at(int, int); //... }; #include <iostream> #include <cstring> Name::Name(const char *str) { len = strlen(str); pstr = new char[len + 1]; strcpy(pstr, str); } void Name::set_at(int index, int ch) { pstr[index] = ch; }

Yukardaki rnekte Name snfnn kurucu ilevi, snf nesnesi iin dinamik bir yer elde ediyor. Elde edilen dinamik bloun balang adresi, char trden bir gsterici olan pstr isimli elemanda tutuluyor. set_at ilevi ise elde edilen dinamik bloktaki index indisli nesneye, ikinci parametre deikenine gnderilen deeri atyor. set_at ilevi const olarak tanmlanmal mdr? set_at ilevi snfn elemanlarn, yani len ve pstr elemanlarn deitirmese de, const olarak tanmlanmamaldr. Snf nesnesinin yer ald bellek blouna bir veri yazlmasa da, snf nesnesinin deerinde bir deiiklik yapld iin, set_at ilevinin const olmamas gerekir. Yani bir ye ilevin const olup olmamas kararnda, nesnenin fiziksel durumundaki deiiklik deil, soyut durumundaki deiiklikler dikkate alnmaldr. Nesne yalnzca fiziksel ya da somut durumu ile yani bellekte kaplad yer ile deerlendirilmemelidir. Bazen bir ye ilev snf nesnesinin bellekte kaplad yere bir veri aktarmamasna karn bir snf nesnesinin durumunu deitirebilir. Bir Name nesnesinin grevi bir isim tutmaktr, deil mi? Bir Name nesnesinin tuttuu ismin deimesi demek, nesnenin soyutlanm durumunun da deimesi demektir. Byle bir deiiklik Name nesnesinin fiziksel durumunda bir deiiklik yapmadan gerekletirilebilir. Yukardaki set_at ilevi bu durum iin rnek olabilir. Bu durumda ilevin const olmayan ye ilev olmas gerekir.

const ye levlerin Derleyici Asndan Anlam


Derleyici asndan bakldnda bir ye ilevin const olmas ne anlama gelir? ye
C++ Kitabi (98/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ilevlerin aslnda gizli bir parametre deikenine sahip olduunu biliyorsunuz. Bir ye ilev hangi snf nesnesi ile arlyorsa, ye ilevin gizli parametre deikenine o nesnenin adresi aktarlr, deil mi? Bir ye ilev iinde bu gstericiye this anahtar szc ile eriilebildiini hatrlyorsunuz. class A { int x; public: void member_func(); };

Yukardaki snf bildirimine A snfnn public ye ilevi olan member_func isimli ye ilevin parametre deikenine sahip olmadn gryorsunuz. Oysa derleyici asndan bakldnda bu ilevin bir parametre deikeni vardr: void A::member_func(A *const this); this gstericisinin kendisi const bir gsterici olduunu anmsayalm. Bu durumda this gstericisine ilev iinde bir atama yaplamaz. rnein aadaki kod geersizdir: A a; void A::member_func() { this = &a; //Geersiz! }

Ancak this gstericisi gsterdii yer const olan bir gsterici deildir. Yani this gstericisinin gsterdii nesneye atama yaplabilir. Ya da this gstericisinin gsterdii nesnenin elemanlarna atama yaplabilir. Peki dardan gizlice adresi alnan snf nesnesinin ye ilev iinde atama yoluyla deitirilmesi engellenmek istenseydi, this gstericisinin, gsterdii yer const olan bir gsterici olmas tercih edilirdi, deil mi? void A::member_func(const A*const this); Byle bir durumda this gstericisinin gsterdii nesneye yani *this nesnesine atama yaplmayaca gibi, this gstericisinin gsterdii nesnenin elemanlarna da atama yaplmas engellenmi olurdu. te derleyici asndan bakldnda const ye ilev tam bu anlama gelir. const olarak bildirilmi bir ilevin this gstericisi gsterdii yer const olan bir gstericidir. ye ilev iinde snfn elemanlarna dorudan eriilebilir. Ama gerekte derleyici bu elemanlara ulamak iin this gstericisini kullanr. void A::member_func() { x = 10; //this->x = 10; } ye ilev const olduunda this gstericisi de gsterdii yer const olan bir gsterici olduundan bu atama artk geerli olmaz: void A::foo() const { //Geersiz! x = 10; }

C++ Kitabi (99/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: const int int *p; p = ptr; *cptr; //Geersiz!

mutable Anahtar Szc


Bir ye ilev kendisini aran snf nesnesinin bir elemannn deerini deitirmi olsa da, nesnenin soyutlanm anlam zerinde hibir gzlenebilir deiiklik yapmam olabilir. Bu durumda okunabilirlik asndan phesiz sz konusu ilevin const ye ilev olmas gerekir. Peki, const ye ilev snfn elemanlarn deitiremediine gre bu nasl mmkn olur? Bu durumda snfn elaman mutable anahtar szc ile bildirilir. Snfn bir elemannn mutable anahtar szc ile bildirilmesinin derleyiciye verdii bilgi udur: Bu eleman const ye ilevler tarafndan da deitirilebilir. Aadaki rnei inceleyin: class X { mutable bool flag; //.. public: //... void foo() const; }; void X::foo() const { flag = false; //... }

X snfnn bool trden flag isimli eleman mutable anahtar szc ile bildiriliyor. Snfn foo isimli const ye ilevinin, snfn flag isimli elemanna atama yapmas geerlidir.

const Snf Nesneleri


const bir snf nesnesi deeri hi deimeyecek bir snf nesnesidir. Bir snf nesnesi const olarak tanmlanabilir. const bir snf nesnesinin elemanlar kullanlabilir ama herhangi bir biimde deitirilemez. rnein, const Date birth_date(4, 3, 1964); gibi bir bildirimle birth_date isimli snf nesnesi const olarak tanmlanyor. Artk birth_date nesnesinin elemanlar deitirilemez. Date snfnn day, month ve year elemanlar public blmde olsayd bile, 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:
C++ Kitabi (100/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin const Date birth_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: const A a; 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 (const A *) 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: void display_totaldays(const Date *pDate) { cout << pDate->totaldays(); //totaldays ilevi const deilse geersiz! } int main() { Date date display_totaldays(&date); return 0; }

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:

C++ Kitabi (101/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void display_totaldays(const Date &r) { cout << r.totaldays(); //totaldays ilevi const deilse geersiz! } int main() { Date date; display_totaldays(date); return 0; }

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 Snf Nesneleri in Kurucu levin arlmas


const bir snf nesnesi yaratlrken snfn kurucu ilevleri normal olarak arlr. nk kurucu ilevlerin amac bir snf nesnesine ilkdeer vermektir. Nasl doal trlerinden const bir nesneye ilk deer verilebiliyorsa, const bir snf nesnesine de kurucu ilev yardmyla ilkdeer verilebilir. Yani kurucu ilevin arlmas nesnenin deerini deitirilmesi deil nesneye ilk deer verilmesi anlamna gelir. rnein: const Date d(29, 5, 1992); deyimi ile snfn kurucu ilevi snfn nesnesinin elemanlarna ileve gnderilen argmanlar ile ilkdeerini verir. Yani kurucu ilev, snfn elemanlarn atama yapmaz, onlara ilkdeerini verir. Snfn elemanlarn deitirmeyen ilevler, const ye ilev olarak tanmlanmazlarsa, bu ilevlerin const snf nesneleri tarafndan arlmas engellenmi olur. Yani bu ilevler const snf nesnelerine hizmet veremez.

Snfn const Elemanlar


C++ dilinde const anahtar szcnn bir kullanm daha vardr. Snf bildirimi yaplrken snfn bir eleman da const olarak bildirilebilir: class A { int a; const int b; //... public: void func(); }; void A::func() { b = 10; //Geersiz! }

Yukardaki bildirimde A snfnn b isimli eleman const olarak bildiriliyor. A snfnn ye ilevleri iinde b elemannn deeri deitirilemez.

C++ Kitabi (102/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Ancak bir snfn elemannn const olarak bildirilmesi durumunda, snfn kurucu ilevinin zel bir szdizim kuralna gre tanmlanmas gerekir. M.I.L szdizimi diye isimlendireceimiz bu kurala ileride ayrntl olarak deineceiz.

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: class A { public: A(){} void func(); void func() const; }; #include <iostream> using namespace std; void A::func() { cout << "A::func()" << endl; } void A::func() const { cout << "A::func()const" << endl; } int main() { A a; const A ca; a.func(); ca.func(); return 0; }

C++ Kitabi (103/311)

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Kopyalayan Kurucu lev


Bir snfn kopyalayan kurucu ilevi (copy constructor), o snfa ait bir nesnenin ilkdeer verilerek yaratlmas durumunda arlan, zel bir kurucu ilevdir. Hatrlayacanz gibi, ilkdeer verme bir bildirim deyimidir, atama deyiminden farkl zellikler tar. int x = 10; Yukardaki deyimde int trden x deikeni, 10 ilkdeerini alarak yaratlyor. Bir snf nesnesi de,ayn trden bir baka snf nesnesinden ilkdeerini alarak tanmlanabilir: class A { public: A(); }; int main() { A a1; A a2(a1); return 0; }

//a1 nesnesi iin varsaylan kurucu ilev arlyor. //a2 nesnesi ilk deerini a1 nesnesinden alarak yaratlyor.

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: class A{ // public: A(const A&); };

//Kopyalayan kurucu ilev

Derleyicinin yazd kopyalayan kurucu ilev, arlm olduu snf nesnesinin elemanlarna yani *this nesnesinin elemanlarna, ilkdeer verme ileminde atama ilecinin sa tarafnda bulunan snf nesnesinin ilgili elemanlarnn deerlerini kopyalar. Byle bir kopyalama ingilizcede "memberwise copy" olarak bilinir.

Kopyalayan Kurucu lev Hangi Durumlarda arlr


Bir snf nesnesine ayr biimde ilkdeer verilebilir: 1. Ak ilkdeer verme biiminde A a1; A a2(a1); Yukardaki kod parasnda a2 nesnesi, deerini daha nce tanmlanan a1 nesnesinden alarak yaratlyor. Tanmlanan a2 nesnesi iin derleyicinin yazd kopyalayan kurucu ilev arlr. a1 nesnesi bu ileve referans yoluyla geirilir. Tek parametreli kurucu ilevlerin aadaki biimde de arlabileceini biliyorsunuz: A a2 = a1;

104/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bu durumda da a2 nesnesi iin kopyalayan kurucu ilev arlr. Bu ileve a1 nesnesi referans yoluyla geirilir. 2. Argmanlardan parametre deikenlerine deerle aktarm Bir ileve gnderilen argman, ilgili ilevin parametre deikeni olan nesneye ilkdeerini(ilk deerini) verir. Yani ilev arldnda, parametre deikeni olan nesne, ileve argman olarak gnderilen ifadeden ilkdeerini(ilk deerini) alarak yaratlr: void func(A p) { // } void foo() { A a; func(a); }

Yukardaki kod parasnda func ilevinin parametre deikeni A snf trndendir. foo ilevi iinde arlan func ilevine, yerel a nesnesinin deeri argman olarak gnderiliyor. func ilevi arldnda yaratlan A snf trnden olan parametre deikeni p, ilk deerini a nesnesinden alarak yaratlr. Bu durumda parametre deikeni p iin kopyalayan kurucu ilev arlr. arlan kopyalayan kurucu ileve a nesnesi referans yoluyla geirilir. 3. levlerin geri dn deerlerini, bir geici nesne oluturarak darya aktardn biliyorsunuz. Geri dn deeri reten bir ilevin kodunun altrlmas srasnda return deyimi yrtldnde, ilevin geri dn deeri trnden bir geici nesne ilkdeer verme yoluyla yaratlr. Yaratlan geici nesne ilk deerini return ifadesinden alr. Bir ilev bir snf trne geri dnyorsa, return ifadesi de bir snf nesnesi olmaldr, deil mi? Bu durumda geri dn deerini iinde tayacak geici nesne ilk deerini return ifadesi olan nesneden alarak yaratlr. Yani geri dn deerinin iinde tayacak geici nesne iin kopyalayan kurucu ilev arlr. return ifadesi olan snf nesnesi de kopyalayan kurucu ileve referans yoluyla geirilir. Aadaki kod parasn inceleyin: A func() { A a; // return a; }

Yukardaki ilevde return deyimi yrtldnde yaratlan geici nesne ilk deerini yerel a nesnesinden alr. Yani A temp_object = a; gibi bir ilem sz konusudur. Bildiiniz gibi, bir snfn farkl imzaya sahip birden fazla kurucu ilevi olabilir ama snfn sonlandrc ilevi tektir. Yukardaki durumda da kopyalayan kurucu ilevle yaratlan snf nesneleri iin bu nesnelerin mr bittiinde snfn sonlandrc ilevi arlr. Programcnn bir kopyalama kurucu ilevi yazmamas durumunda derleyici bu ilevi kendisi yazyorsa ve derleyicinin yazd kopya kurucu ilevi tm elemanlar karlkl birbirine atyorsa, neden programc bir kopyalayan kurucu ilevi yazma gerei duysun? Baz durumlarda nesnelerin elemanlarnn karlkl olarak birbirine atanmas istenen bir durum deildir!

105/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Baz snflarda snf nesnelerinin baz elemanlar dsal kaynaklar kontrol eder. Bu elemanlarnn deerlerinin birbirlerine atanmas istenmeyen sonulara yol aabilir. Yazlar tutmak iin tanmlanan ismi String olan bir snf ele alalm. Snfn basitletirilmi tasarm aadaki gibi olsun: //string.h class String { char *m_p; int m_len; public: String(const char *str); ~String(); void make_upper(); void print() const; }; //string.cpp //#include string.h #include <iostream> #include <cstring> #include <cctype> using namespace std; String::String(const char *str) { m_len = strlen(str); m_p = new char[m_len + 1]; strcpy(m_p, str); } String::~String() { delete[] m_p; } void String::make_upper() { for (int k = 0; k < m_len; ++k) m_p[k] = toupper(m_p[k]); } void String::print() const { cout << m_p; }

Tanmlanan String snfn kullanan aadaki main ilevini dikkatle inceleyin: int main() { String s1("Necati"); { String s2 = s1; s2.print(); //... } s1.make_upper(); s1.print(); return 0; }

106/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

s1 nesnesi yaratldnda arlan kurucu ilevin almasyla, 7 karakter uzunluunda bir dinamik alan yaratlyor. Yaratlan dinamik alann balang adresi s1 nesnesinin eleman olan m_p isimli gsterici elemanda tutuluyor. Daha sonra yer alan blok iinde bu kez s2 isimli nesne ilk deerini s1 nesnesinden alarak yaratlyor. Derleyicinin yazd kopyalayan kurucu ilev, s1 nesnesinin elemanlarnn deerlerini s2 nesnesinin elemanlarna atayacana gre, s2 nesnesinin de m_p isimli eleman olan gsterici ayn dinamik alan gsterir, deil mi? s2 nesnesinin tanmland bloun sonunda, s2 nesnesinin mr tamamlanaca iin s2 nesnesi iin snfn sonlandrc ilevi arlr. Sonlandrc ilev daha nce elde edilen dinamik blou free storea geri vermek iin tanmlanmtr. s2 nesnesinin m_p elemannn gsterdii dinamik alan free storea geri verilir. Ancak bu dinamik bloun balang adresi ayn zamanda s1 nesnesinin de m_p elemannn deeridir. s2 nesnesi mrn tamamlamasna karn s1 nesnesi halen hayatn srdrmektedir. Ancak s1 nesnesinin dinamik alan artk serbest braklmtr. Snfn make_upper ve print isimli ye ilevleri ayrlan dinamik alan zerinde ilem yaptklar iin s1.make_upper() ve s1.print() ilev arlar alma zamannda gsterici hatasna neden olur. Tabii bu arada s1 nesnesi mrn tamamladnda s1 nesnesi iin de sonlandrc ilev arlr ve bu ilev de daha nce geri verilen dinamik alan tekrar free storea geri vermeye alr. Bu durum da bir baka alma zaman hatasdr(undefined behavior). Tanmlanan String snf iin benzer hatalar farkl grntlerle de karmza kabilirdi: void func(String s) { // } int main() { String name("Necati"); func(name); name.print(); //alma zaman hatas // return 0; }

main ilevi iinde String snf trnden name isimli bir nesne tanmlanyor. Daha sonra func isimli ileve name nesnesi argman olarak gnderiliyor. arlan func ilevinin parametre deikeni String snf trndendir. Bu durumda ilevin arlmasyla birlikte, parametre deikeni olan s nesnesi iin kopyalayan kurucu ilev arlr. func ilevinin ana blounun sonuna gelindiinde bu kez s nesnesi iin sonlandrc ilev arlr. Nesnenin m_p elemannn gsterdii alan free storea geri verilir. Oysa ilev arsndan sonra name nesnesinin mr halen devam etmektedir. name nesnesinin print ye ilevi arldnda bu ye ilev artk geri verilen bir alana eriir. Yani bir gsterici hatasna neden olur.

Kopyalayan Kurucu levin Yazlmas


Programc bir kopyalayan kurucu ilev yazarsa derleyici artk otomatik olarak bir ilev yazmaz. ou zaman programcnn yazd kopyalayan kurucu ilev, yaratlan nesneyi ilkdeer(ilk deerini) veren nesnenin kayna ile dorudan ilikilendirmek yerine, yaratlan nesne iin farkl bir kaynak yaratr. Bylece ayn kaynan birden fazla nesne tarafndan paylalmasndan doan hatalar ortadan kaldrlr. Aada, daha nce tanmlanm String snf iin bir kopyalayan kurucu ilev bildiriliyor ve tanmlanyor:

107/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class String { //... public: String(const String &); //... }; String::String(const String &r) { m_len = strlen(r.m_p) ; m_p = new char[m_len + 1]; strcpy(m_p, r.m_p); }

Yazlan kopyalayan kurucu ilevi inceleyelim: levin parametre deikeni olan r referans, yaratlan String nesnesine ilk deerini veren nesnenin yerine geiyor. nce yaratlan snf nesnesinin m_len isimli elemanna ilk deer veren nesnenin tuttuu yaznn uzunluunun 1 fazlas atanyor. Daha sonra new[] ileciyle m_len + 1 byte uzunluunda bir blok dinamik olarak elde ediliyor. Dinamik bloun balang adresi yeni yaratlan nesnenin m_p isimli elemannda tutuluyor. Daha sonra standart strcpy ileviyle yeni bloa, ilk deer veren nesnenin dinamik alanda tuttuu yaz kopyalanyor. Yazlan kopyalayan kurucu ilevin kodunun almasyla, yaratlan nesne artk kendi dinamik alann kullanyor hale gelir. Bu nesne iin sonlandrc ilev arldnda da sonlandrc ilev iinde kullanlan delete[] ileci yalnzca nesne iin ayrlm olan blou geri verir. Kopyalayan kurucu ilev dorudan snfn elemanlarnn deerlerini birbirine aktarmam, yaratlan nesne iin yeni bir kaynak yaratm ve ilk deer veren nesnenin kulland kaynaa ulap, yeni elde edilen kaynaa kopyalamay salamtr. Byle kopyalamaya "derin kopyalama" (deep copy) denir.

Kopyalayan Kurucu levin Parametresi Referans Yerine Nesne Olabilir mi?


Yukarda tanmlanan String snfna ilikin kopyalayan kurucu ilevin aadaki biimde bildirildiini dnelim: class String { // public: String(String); // };

String snf trnden bir nesnenin bir ileve deerle gnderildiini dnelim: void foo(String); void func() { String str("Necati"); foo(str); // }

Bu durumda foo ilevinin parametre deikeni olan nesnenin yaratlmas iin, kopyalayan kurucu ilev arlr. Ancak kopyalayan kurucu ilevin de parametresi String snf trnden bir nesne olduu iin bu kez kopyalayan kurucu ilevin de parametresi olan

108/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

String snf trnden nesne iin yine kopyalayan kurucu ilev arlr. Bu durumda zyinelemeli (recursive) olarak arlan kopyalayan kurucu ilev belirli bir sreden sonra yn (stack) alann tketir.

109/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Atama lecini Ykleyen lev


Bir snf nesnesine ayn trden bir snf nesnesi atand zaman da bir ilev otomatik olarak arlr. arlan bu ilevi "atama ilevi" (assignment operator function) olarak isimlendireceiz. Atama ilevi de zel bir ilevdir. Yani programc bu ilevi tanmlamaz ise bu ilev de derleyici tarafndan otomatik olarak yazlr. Derleyicinin otomatik olarak yazd atama ilevi, snfn statik olmayan bir ye ilevidir ve inline olarak bildirilir. rnein A isimli bir snf iin derleyicinin yazd atama ilevinin bildirimi aadaki gibidir: class A{ //... public: A &operator=(const A&); //... };

Derleyicinin yazd atama ilevi atama ilecinin sa tarafndaki snf nesnesinin elemanlarn atama ilecinin sol tarafndaki nesnenin elemanlarna karlkl olarak atar. lev atama ilecinin sol tarafndaki snf nesnesini referans yoluyla geri dndrr. a1 ve a2 A snf trnden nesneler olmak zere a1 = a2; gibi bir atama derleyici tarafndan A snfnn atama ilevine yaplan bir arya dntrlr: a1.operator=(a2); arlan ye ileve this adresi olarak atama ilecinin solundaki nesnenin adresi yani a1 nesnesin adresi geirilir. Snf nesnelerinin karlkl elemanlarnn birbirine atanmasnn uygun olmad durumlarda derleyicinin yazd atama ilevi istenmez. Bu durumda programc atamann istedii gibi yaplmasn salamak iin kendisi bir atama ilevi yazabilir. Programcnn bir atama ile ilevi yazmas durumunda artk derleyici bu ilevi yazmaz. Bir snf iin kopyalayan kurucu ilevin tanmlanmas gerekiyorsa, ounlukla ayn snf iin atama ilevinin de yazlmas gerekir. Daha nce rnek olarak verilen String snfn dnelim: int main() { String name1("Deniz"); { String name2("Yusuf"); name2 = name1; } name1.print(); //... }

Yukardaki kodu inceleyelim: main ilevinin ana blounun banda String snf trnden name1 isimli bir nesne tanmlanyor. arlan kurucu ilev 6 karakterlik bir blou dinamik olarak elde ederek bu bloun balang adresini, eleman olan m_p isimli gstericide saklar. Bu bloa "Deniz" yazsn kopyalar. main ilevi iinde yer alan i blokta bu kez name2 isimli bir nesne yaratlyor. Bu nesne de benzer biimde "Yusuf" yazsn tutar. name2 = name1

110/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

Atama ilevinin Yazlmas


Atama ileminde, atama ilecinin sa tarafnda yer alan nesnenin kulland kaynan, ilecin sol tarafnda yer alan nesne tarafndan paylalmas istenmiyorsa atama ileci iin bir ilev yazlmaldr. Yukarda daha nce bildirilen String snf iin atama ilevi aadaki gibi tanmlanabilir: String &String::operator=(const String &r) { if (this == &r) return *this; delete[]m_p; m_len = r.m_len; m_p = new char[m_len + 1]; strcpy(m_p, r.m_p); }

Yazlan ilevi inceleyelim. levin giriinde yer alan if deyimine daha sonra deineceiz. Atama ilevinin kopyalayan kurucu ilevden nemli bir fark vardr. Kopyalayan kurucu ilev hangi snf nesnesi iin arlyorsa o snf nesnesi ilev arsndan nce yoktur. Nesne kurucu ilevin arlmasyla hayata balar. Ancak atama ilevi sz konusu olduunda durum farkldr. lev hangi nesne iin arlmsa bu nesne zaten daha nce yaratlmtr. Daha nce yaratlm bu nesnenin kullanm iin zaten bir blok elde edilmitir. String snf iin yazlan atama ilevi nce nesne iin ayrlan dinamik blou geri vermelidir: delete []m_p; deyimiyle bu salanr: Daha sonra atama ilecinin sol tarafndaki nesne iin yani *this nesnesi iin m_p = new char[m_len + 1]; deyimiyle yeni bir dinamik blok elde ediliyor ve strcpy(m_p, r.m_p);

111/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

arsyla da, atama ilecinin sa tarafndaki nesnenin dinamik blounda tutulan yaz, yeni elde edilen dinamik bloa kopyalanyor. levin *this deeriyle geri dndn gryorsunuz. lev hangi snf nesnesi iin arlmsa o snf nesnesini referans yoluyla geri dndrr. Bir baka deyile name1 = name2 atamasnn dntrld ilev ars, name1 nesnesini referans yoluyla geri dndryor. Peki buna neden gerek duyulmu olabilir? Doal trler sz konusu olduunda, atama ilecinin rettii deerin nesneye atanan deer olduunu biliyorsunuz. Ayn zelliin bir snf trnden nesne iinde salanmas atama ilecini ykleyen ilevin, *this nesnesini dndrmesi ile salanabilir. Aadaki kodu inceleyin: int main() { String String String String

name1("Deniz"); name2("Yusuf"); name3("Huseyin"); name4("brahim");

name1 = name2 = name3 = name4; name1.print(); name2.print(); name3.print(); name4.print(); return 0; }

Atama ilecinin ncelik ynnn soldan saa olduunu biliyorsunuz. name1 = name2 = name3 = name4; deyimi, derleyici tarafndan aadaki gibi bir ilev ars zincirine dntrlr: name1.operator=(name2.operator=(name3.operator=(name4))); Bir nesnenin kendisine atanmasnn bir szdizim hatas oluturmadn biliyorsunuz. String snf trnden bir nesnenin kendisine atandn dnelim: int main() { String name("Necati"); name = name; //... }

Yazm olduumuz atama ilevi arldnda ne olur? Atama ilecinin sol terimi olan nesne iin arlan atama ilevi, ilk nce m_p gstericisinin gsterdii dinamik blou geri verir. Ancak atama ilecinin sa terimi olan nesne de ayn nesne olduundan, strcpy( m_p, r.m_p);

112/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ifadesi gsterici hatasna neden olur. nk r.m_p nesnesi artk geri verilen bir blou gsteriyor durumundadr. Hata nasl giderilebilir? Atama ilecinin sol terimi olan nesne ile sa tarafndaki nesnenin ayn olmas durumunda hi bir ilem yaplmamaldr: if (this == &r) return *this; Yukardaki deyimi inceleyelim: Atama ilevi iinde kullanlan this gstericisi ilecin sol terimi olan snf nesnenin adresidir, deil mi? r referans da atama ilecinin sa terimi olan nesnenin yerine gemitir. O zaman &r ifadesi, atama ilecinin sa terimi olan nesnenin adresidir. Adresleri ayn olan iki nesne ayn nesnedir. Eer nesne kendine atanyorsa ilev hi bir ilem yapmadan *this nesnesini referans yoluyla dndrr. Atama ile ilevinin kodu iinde nesnenin kendisine atanp atanmadnn snamas yaplmal mdr? yle dnebilirsiniz: Programc durup dururken neden bir snf nesnesini kendine atasn? Bu hata daha ok gstericiler, referanslar ve ilev arlarna ilikindir. *ptr = x; Yukardaki atama deyiminde ptr bir snf trnden gsterici x de ayn snf trnden bir nesne olsun. Bu atama ileminden nce ptr x nesnesini gsteriyorsa, nesne kendisine atanm olur.

113/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Dntren Kurucu lev


Snfn tek argmanla arlabilen kurucu ilevlerine dntren kurucu ilev denir. Byle bir kurucu ilev ya tek bir parametreye sahiptir, ya da dier parametreleri varsaylan deerler alr. class Account { int acc_no; double balance; public: Account(double); //... };

//Dntren kurucu ilev

Yukarda tanmlanan Account snfnn Account(double) ; biiminde bildirilen kurucu ilev, dntren kurucu ilevdir. lev tek parametre deikenine sahiptir. class Complex { double r, i; public: Complex(double r, double i = 0); //... };

//Dntren kurucu ilevi

Yine yukarda tanmlanan Complex snfnn Complex(double r, double i = 0); biiminde bildirilen kurucu ilev, dntren kurucu ilevdir. lev tek bir argmanla arlabilir. Bir snfn dntren kurucu ilevi, baka trden bir ifadenin snf trnden geici bir nesneye otomatik olarak dntrlmesini salar. rnein yukarda tanmlanan Account snf iin aadaki ifadelerden hepsi geerlidir: void func(Account p) { //... } int main() { Account a1 = 200; a1 = 300.; func(200.); return 0; } Account foo() { double x; //... return x; }

114/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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 Account a1 = Account(200.); ya da Account a1 = (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. Yukardaki tr dnmleri otomatik olarak derleyici tarafndan yaplabilecei gibi programc tarafndan tr dntrme ileleri kullanlarak da yaplabilirdi. imdi de main ilevini tr dntrme ilelerini kullanacak biimde deitirelim:

115/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin int main() { Account a1 = Account(200); a1 = Account(300.); func(Account(500.)); return 0; }

// Account a1 = (Account)200; // a1 = (Account)300.; // (Account)500.;

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: int main() { int a; Account ac(400.); func(a); //... return 0; }

levi yazan programcnn func ilevine argman olarak Account snf nesnesi ac yi gndermek yerine, yanllkla int trden olan a nesnesinin deerini gnderdiini dnelim. Derleyici iin func(a)

116/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ars tamamen geerlidir. a ifadesi nce double trne, daha sonra da double trnden yukarda akladmz biimde Account snfna dntrlerek func ilevine argman olarak gnderilir. Yani derleyici tarafndan ilev ar ifadesi func(Account(double(a))); biiminde ele alnr. Snf trnden olmayan bir ifadenin snf trne dntrme kurucu ileviyle dntrlmesi istenmiyorsa bu durum derleyiciye explicit anahtar szcyle bildirilebilir.

explicit Anahtar Szc


Bir snfn kurucu ilevinin bildiriminde explicit anahtar szc kullanlabilir. explicit anahtar szc yalnzca ilevin bildiriminde yazlr, kurucu ilevinin snf dnda tanmlanmas durumunda ilev tanmnda yer almaz. explicit anahtar szc ile bildirilen bir kurucu ilev, otomatik tr dntrme amacyla kullanlamayan bir dntrme kurucu ilevidir. Kurucu ilevin explicit anahtar szcyle bildirilmesi durumunda, halen ak bir biimde yani tr dntrme ileleri kullanlarak dnm gerekletirilebilir ancak otomatik dnme derleyicinin hata mekanizmasyla engel olunur. Yukardaki rneimizi aadaki biimiyle yeniden derlemeye aln: class Account { int acc_no; double balance; public: explicit Account(double); //explicit Dntren kurucu ilev //... }; void func(Account p) { //... } int main() { Account a1 = 200; a1 = 300.; func(200.); return 0; } Account foo() { double x; //... return x; }

//Geersiz. otomatik dnm yaplamaz! //Geersiz. otomatik dnm yaplamaz! //Geersiz. otomatik dnm yaplamaz!

//Geersiz. otomatik dnm yaplamaz!

117/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: class Account { int account_no; double balance; public; //... friend double balance_dif(const Account &, const Account &); //... };

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. class A { int x, int y; public: A(int, int); friend void func(); };

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: void func() { A a; a.x = 10; a.y = 20; //... }

// private blme eriiliyor ama geerli // private blme eriiliyor ama geerli

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:

118/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin double balance_dif(const Account &r1, const Account &r2) { return r1.balance - r2.balance; } levlerin parametre deikenleri ilev snrlar iindedir. Yani arkada bir ilevin parametre deikeni arkada olunan snfa ilikin bir nesne, bir gsterici ya da bir referans ise bu nesne, gsterici ya da referans ile snfn her blmne eriilebilir. Yukardaki balance_dif ilevinin tanmnda, ilevin parametre deikeni olan r1 ve r2 referanslar nokta ileci ile kullanlarak ismi balance olan private elemana eriiliyor. phesiz balance_dif ilevi Account snf iinde friend ilev olarak bildirildikten sonra, aadaki biimlerde de tanmlanabilirdi: double balance_dif(const Account a1, const Account a2) { return a1.balance - a2.balance; } ya da double balance_dif(const Account *p1, const Account *p2) { return p1->balance - p2->balance; } Bir ilev, birden fazla snfn arkada ilevi olabilir. Arkada ilev bildiriminin snfn hangi blmnde yapldnn hibir nemi yoktur. Yukardaki rnekte, Account snfnda balance_dif ilevi public blmde arkada olarak bildirildi. Ancak private ya da protected blmde bildirilseydi de bir farkllk olumazd. Yalnzca global ilevler deil, baka snflarn ye ilevleri de arkada ilev olabilir. rnein: class Myclass { // void myfunc(); }; class Herclass{ // friend void Myclass::myfunc(); };

Yukardaki rnekte Herclass snfn, Myclass snfnn myfunc isimli ilevini arkada olarak bildiriyor. Arkada ilev bildirimi ayn zamanda ilevin bildirimi yerine de geer. Bu durumda ilev bildiriminin bilinirlik alan snf bildiriminin yapl yeriyle belirlenir. rnein: int main() { class X { // friend void f(); }; f(); // Geerli // } void func()

119/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin { // f(); } 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. // Geersiz! lev bildirimi bu blokta tannamaz!

Arkada levler Ne Zaman Kullanlmaldr


Arkada ilevler eriim bakmndan ayrcalkl ilevler olduundan snfn private elemanlarnn korunmasn azaltr. Arkada ilevler snfn her blmne eriebildiine gre, snfn private elemanlara ynelik bir deiiklik yapldnda yalnzca ye ilevleri deil ayn zamanda arkada ilevleri de yeniden yazmak gerekir. Arkada ilevleri fazlaca kullanmak snfn private blmnn nemini azaltmay kabul etmek anlamna gelir. Arkada ilevler karmak durumlarda tasarm ve ilemleri kolaylatrmak iin kullanlmaldr. Kodun daha kolay yazlmasn salamak iin baz ilevlere gereksiz bir ekilde arkadalk vermek iyi bir fikir deildir. Arkada ilevlerin kullanlmasn gerekli duruma getirebilecek en tipik rnek ileleri ykleyen global ilevlerinin yazmdr. Byle ilevleri ileride ele alacaz.

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, friend class <snf ismi>; 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: class Node { int val; Node *next; friend class LList; }; class LList { public: void add(int val); void delete(size_t n); Node *get_head(); // private: Node *phead; Node *pnode; size_t size; };

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 her blmne eriilebilir. Node snfnn elemanlarnn private blmde olduuna ve

120/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

herhangi bir ilev iinde Node snf trnden nesne tanmlanabilecei halde, o nesne yardmyla snfn private elemanlarna eriilemeyeceine dikkat edin. Arkada snflar da arkada ilevlerde olduu gibi eriim kolayl salamak iin kullanlabilir. Tabii arkada snflarn da, private elemanlarnn korunmasn azaltacan sylemeliyiz.

Arkadalk Bildirimi ift Ynl Deildir


Bir snfn bir baka snfa arkadalk vermesi, arkadalk verilen snftan da bir arkadalk alnd sonucunu dourmaz. Aadaki rnei inceleyin: class A{ public: friend class B; void func(); }; class B{ int b; //... }; void A::func() { B object; 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.

Arkadamn Arkada Benim de Arkadamdr


Bu cmle arkadalk bildirimleri sz konusu olduunda doru deildir. Arkadalk bildirimlerinde geime zellii sz konusu deildir. Aadaki rnei inceleyin: class A{ int a; public: friend class B; }; class B{ friend class C; //... }; class C{ public: void func(); }; void C::func() { A object; object.a = 1; }

//Geersiz

121/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki rnekte A snf B snfna, B snf da C snfna arkadalk veriyor. Bu durumdan A snfnn C snfna arkadalk verdii gibi bir sonu kmaz. C snfnn func isimli ye ilevi iinde A snfnn private ksmna erime abas derleme zamannda hata ile sonulanr.

122/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Snfn Statik Elemanlar ve levleri


ncelikle static anahtar szcnn yerel ve global deikenlerle kullanlmas ile ilgili baz noktalar hatrlatalm. static anahtar szc yerel deikenlerle kullanldnda tanmlanan deikenin mr (storage duration) zerinde belirleyici olur. Yani static yerel bir deiken tanmland bloun banda yaratlp, blok sonlandnda bellekten boaltlmaz; global deikenlerde olduu gibi programn yklenmesiyle yaratlr; program sonlanana kadar bellekte kalr. static yerel deikenler blok bilinirlik alanna (block scope) uyan statik mrl deikenlerdir ve ou zaman global deikenlere seenek olarak kullanlrlar. static anahtar szc ile tanmlanan global deikenler, i balantya (internal linkage) aittir. Yani static global deikenler baka bir modlde extern bildirimi yaplsa bile kullanlamaz. Bir ilev de bu anlamyla static olarak tanmlanabilir. static ilevler baka modllerden arlamaz. C++ dilinde bir snfn elemanlar ve ilevleri de static anahtar szcyle bildirilebilir. imdi static anahtar szcnn snflarla ilgili kullanmn inceleyelim:

Snfn Statik Elemanlar


Baz durumlarda bir snf trnden tanmlanan tm nesnelerin global bir nesneye erimesi gerekir. rnek olarak, bir snf trnden ka nesnenin yaratld deerini tutan bir global deikene gereksinim duyulmas durumunu dnebilirsiniz. Ya da yine global bir gsterici deikenin, tm snf iin ayrlm olan dinamik bir bloun balang adresini tuttuunu ve tm snf nesnelerinin bylelikle bu dinamik alana eritiini dnebilirsiniz. Bu bilgilerin snfn bir eleman olarak tutulmas durumunda, hem snf nesneleri gereksiz bir biimde byr, hem de bu deerlerin deitirilmesi durumunda deiikliin var olan tm snf nesneler iin ayr ayr yaplmas gerekir. Bu bilgilerin global deikenlerde tutulmas durumunda ise, dary ilgilendirmeyen yalnzca sz konusu snfa ilikin bir kodlama detay darya szdrlm olur. Global deikenin bir baka sakncas da, bunlara yalnzca snfn ulamasn salayp, snf dnda eriimi engellemenin mmkn olmaydr. Yani bir global deiken bir snf iin public ya da private olamaz. Yine baka bir saknca da, bu amala kullanlacak bir global deikenin global isim alann gereksiz bir ekilde kirletmesidir. Snfn bir eleman snf bildirimi iinde bana static anahtar szc getirilerek bildirilirse bir eit snfa zg global deiken gibi ele alnr. Byle bir elemana snfn static eleman denir. Snfn static eleman ile global deikenler arasnda ama koda yazm biimi bakmndan hibir fark yoktur. Yalnzca, snfn static elemanlar snf isimleri ile kombine edilerek ama kod iinde yazlr. static elemanlar snf nesnesi iinde bir yer kaplamaz. Darda global deikenler gibi saklanr. Yalnzca mantksal adan snfla ilikilendirilirler. Snfn static elemanlar, global deikenlerde olduu gibi darda ayrca tanmlanmak zorundadr. Tanmlama ilemi, bildirim snfn hangi blmnde yaplm olursa olsun gereklidir ve geerlidir. static elemanlarnn tanmlanmas aadaki gibi yaplr: <tr> <snf ismi> :: <deiken_ismi> [ = ilkdeer]; Genel biimden de grld gibi static elemanlar snf ismi ile birlikte belirtilerek tanmlanr. rnein X snfnn int trden s isimli bir static eleman yle tanmlanr: int X::s; Snfn statik elemanlar, yaratlan snf nesnesinin iinde yer almaz. Yani statik elemanlar snf nesnesinin sizeof deerini bytmez. Snfn statik bir elemanndan yalnzca bir tane bulunur. Statik elemanlar aslnda global deikendir. Yalnzca mantksal bakmdan snf ile ilikilendirilmitir. Aadaki rnei inceleyin:

123/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Person { private: char *name; int no; public: Person(const char *, int); ~Person(); void display() const; public: static int count; }; #include <iostream> #include <cstring> using namespace std; int Person::count = 0; Person::Person(const char *nm, int n) { name = new char[strlen(nm) + 1]; strcpy(name, nm); no = n; ++count; } void Person::display()const { cout << name << " " << no; } Person::~Person() { delete [] name; } int main() { Person person("Burak Gencer", 120); cout << sizeof(person) << endl; return 0; } Yukardaki rnekte Person snf trnden person nesnesinin sizeof ileci ile boyutu elde ediliyor. Elde edilen boyut yalnzca sizeof(char *) + sizeof(int) kadar olur. Snfn static eleman olan count aslnda global bir deikendir. Bir snf nesnesinin tanmlanmasna gereksinim duyulmadan statik mrl olacak biimde yaratlr. Bu yzden snf nesnesi iinde yer kaplamaz. Yukardaki programda eer DOS altnda ve yakn modellerde allyorsa 4, DOS altnda ve uzak modellerde alyorsanz 6, UNIX ya da Win32 sistemlerinde alyorsanz 8 deerini elde edersiniz. rneimizde snfn static elemannn ayrca global bir biimde snf ismi belirtilerek aadaki gibi tanmlandn da gryorsunuz. int Person::count = 0; Grdnz gibi statik elemanlara tanmlama srasnda ilkdeer de verilebilir. Ancak ilkdeer verilmeyen doal trlerden statik elemanlarda, global ve static yerel deikenlerde olduu gibi ilerinde sfr bulunur. Snfn statik bir eleman snf dnda tanmlanmaz ise static elemann kullanlmas durumunda derleme zamannda hata olumaz. Hata balama aamasnda oluur. Snfn statik elemanlarna dardan ilgili snf trnden nesne, gsterici ya da referans yoluyla eriilebilir. Tabii statik elemannn public blmde bildirilmi olmas gerekir.

124/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Aadaki main ilevini inceleyin: int main() { Person x("Ali Sere", 25); cout << x.count; Person y("Ahmet Altntart", 32); cout << y.count; return 0; }

// 1 // 2

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: int main() { Person x("Ali Sere"); cout<< Person::count << endl; Person y("Ahmet Altntart"); cout<< Person::count << endl; return 0; } 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(const char *nm, int n) { name = new char[strlen(nm) + 1]; strcpy(name, nm); no = n; ++count; }

// 1 // 2

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: class Date { private: int day, month, year; static const char *week_days[ ]; public: // };

const char *Date::week_days[] = {"Pazartesi", "Sal", "aramba", "Perembe", "Cuma", "Cumartesi", "Pazar"}; Snfn statik elemanlar yalnzca bir kez tanmlanmaldr. Bunun iin tanmlama ilemi bir .cpp dosyas iinde yaplmaldr. Eer tanmlama .h dosyas iinde yaplr, bu dosya da

125/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

proje iinde birden fazla yerde eklenirse balama zamannda hata oluur. Snfn statik bir elemann kullanmak iin o snf trnden nesne tanmlamaya gerek yoktur. Yani snf trnden hibir nesne tanmlanmasa bile snfn statik elemanlar yine de kullanlabilir.

statik Elemanlar Ne Zaman Kullanlmal


Snfn statik elemanlar aslnda mantksal olarak snfla ilikilendirilmi global deikenlerdir. Global deikenlere tm ilevler iinde eriilebilirken, statik elemanlara yalnzca snfn ye ilevleri iinde dorudan eriilebilir. Snfn statik elemanlarna ayrca snf nesnesi, gstericisi ya da referans ile ya da snf ismi ve znrlk ileci ile de dardan eriilebilir. Bir global deiken yalnzca bir snf iin anlaml ise onun normal bir global deiken yerine statik eleman olarak tanmlanmas, algsal bakmdan bilinirlik alann daraltr. Bylece kodu inceleyen kii deikenin kod ile ilgisini daha iyi anlamlandrr. Date snf iin verilen rnekteki 01/01/1900den geen gn saysna ilikin dnmleri yapan totaldays ve revdays isimli ye ilevleri anmsayn. Bu ilevler aylarn ka ektikleri bilgisini mon_days isimli bir global diziden elde ediyordu. mon_days isimli dizi global olmasna karn yalnzca aylarn ka ektiklerinin belirlenmesi amacyla kullanlmaktadr. Yani mon_days yalnzca Date snfnn ye ilevlerinin kulland global bir dizidir. Eer bu dizi snfn normal bir eleman yaplsa, btn Date trnden nesneler iinde gereksiz bir biimde yer kaplard, deil mi? te hem global olarak brakmak hem de snfla ilikilendirmek iin mon_days dizisi statik eleman olarak tanmland. Ayrca snfn display_text isimli ilevi, yeniden dzenlenerek, ay isimlerini de yazyla yazdrabilir. Ay isimleri ve gn isimleri char trden statik gsterici dizilerine yerletirilebilir. Date snfnn biraz daha gelitirilmi biimini ileride vereceiz. Snfn statik elemanlar da, snfn elemanlardr. Ancak baz zellikler asndan, snfn statik elemanlar dier elemanlardan farkllk gsterir: Bir snfn eleman ayn snf trnden olamaz ama bir snfn ayn snf trnden statik bir eleman olabilir. Aadaki snf bildirimini inceleyin: class A { static A sa; A *mptr; A ax; }; //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: class A { int m_x; static int ms_y; public: void func(int = m_x); void foo(int = ms_y); };

//Geersiz //Geerli

Snfn statik ye levleri


Snf bildirimi iinde bir ilev, bana static anahtar szc getirilerek bildirilebilir. Byle ilevlerle snfn statik ye ilevleri denir. Snfn statik ye ilevleri global bir ilev gibidir. Ancak mantksal bakmdan snfla ilikilendirilmitir. Bu ilevlere this gstericisi geirilmez. Snfn statik ye ilevlerine this gstericisinin geirilmemesi, o ilevler iinde snfn elemanlarna ve dier ye ilevlere dorudan eriilemeyecei anlamna gelir. Snfn statik ye ilevi her ne kadar global bir ilev gibiyse de, tanmlanmas ye ilevlerde olduu gibi snf ismi belirtilerek yaplr. Date snfnda baz deiiklikler yapyoruz:

126/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Date { private: int day, month, year; static int mon_days[12]; static char *mon_tab[12]; static char *day_tab[7]; public: enum Days {Sunday , Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; Date(); Date(int, int, int); void display() const; void display_text() const; long totaldays() const; void revdays(long); int weekday() const; static bool isleap(int); // static ye ilev };

nceki biimde bir yln artk yl olup olmadn bulan isleap isimli ilev normal bir global ilevdi. Ancak bu ilevin yalnzca tarih ilemleriyle anlaml bir ilevi vardr. O halde mantksal bakmdan Date snf ile ilikilendirilip alglama kuvvetlendirilmitir. isleap ilevi darda yle tanmlanabilir: bool Date::isleap(int year) { return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; } static anahtar szcnn yalnzca bildirim srasnda yazldna, tanmlama srasnda yazlmadna dikkat edin. isleap iinde snfn day, month ve year elemanlarna dorudan eriilemez. Snfn dier ye ilevleri snf nesnesi olmadan dorudan arlamaz. Grdnz gibi dardaki snf ile mantksal bakmdan ilikili olan ama snfn elemanlarn ve ye ilevlerini kullanmayan global ilevler snfn statik ilevi yaplabilir. Snfn statik ye ilevi bir snf nesnesi, gstericisi ya da referansyla arlabilir. Ya da eer snfn public blmndeyse ar dorudan snf ismi belirtilerek :: znrlk ileci ile de yaplabilir: int main() { Date x; if (x.isleap(1996)) cout << "is a leap year" << endl; else cout << "is not a leap year" << endl; if (Date::isleap(2000)) cout << "is a leap year" << endl; else cout << "is not a leap year" << endl; return 0; } Burada x.isleap ve Date::isleap biiminde iki ar yaplyor. isleap snfn public blmnde olduu iin her iki ar biimi de geerlidir. Ancak yine de statik ye ilevlerin, snf nesnesi kullanmadan snf ismiyle arlmas nerilir. Ne de olsa arda kullanlan snf nesnesinin hibir zel anlam yoktur. Snfn statik ilevi, snfn bir ye ilevi iinde dorudan arlabilir. rnein yukardaki programda totaldays ilevi iinde isleap ilevi dorudan arlyor. Snfn statik bir ye ilevi iinde statik elemanlar dorudan kullanlabilir. Snfn statik elemanlarnn snf nesnesi iinde yer kaplamadn anmsayn. Bu durumda statik

127/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

elemanlara erimek iin this gstericisi gerekmez. Aada bu biimde kullanma ilikin bir rnek veriliyor: class Person { private: char *name_; int no_; static int count_; public: Person(const char *nm, int n); ~Person(); void display() const; static int get_count(); }; #include <iostream> #include <cstring> using namespace std; int Person::count_ = 0; Person::Person(const char *nm, int n) { name_ = new char[strlen(nm) + 1]; strcpy(name_, nm); no_ = n; ++count_; } void Person::display() const { cout << name_ << endl; cout << no_ << endl; } int Person::get_count() { return count_; } Person::~Person() { delete [] name_; } int main() { Person x("Necati Ergin", 25); Person y("Haluk Yetis", 34); cout << Person::get_count(); return 0; } Bu rnekte daha nce vermi olduumuz Person isimli snfta baz deiiklikler yaptk. rnein statik count_ elemann snfn private blmne yerletirdik. Bu elemann deerinin elde edilmesi iin get_count isimli statik bir ye ilev ekledik. get_count ilevinin yalnzca snfn statik eleman olan count_ ile ilikili olduuna dikkat edin. Snfn statik ye ilevi ile ayn isimde ve parametre yapsna sahip global bir ilev bulunabilir. nk snfn statik ilevleri snf ismi ile birletirilerek ama kod iine yazlr. Snfn kurucu ve sonlandrc ilevleri statik ye ilev olamaz. Bu ilevlerin yaratlan ve yok edilen nesne zerinde dorudan ilem yapmak zorunda olduunu dnrsek static ye ilev olmamalar gerektii de aktr. Statik bir ye ilev const olarak da bildirilemez. Zira bir const ye ilev, this gstericisinin gsterdii nesne const olan bir ye ilev anlamna gelir. statik ye ilevler this gstericisine sahip olmadklarna gre, const da olamazlar.

128/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Snfn statik ye ilevleri sonuta snfa ilikin bir ye ilev olduu iin, statik ye ilevleri iinde snfa ilikin bir nesne tanmlandnda o nesne yoluyla dardan snfn her blmne eriilebilir. class Sample { int x; public: static void func(); }; void Sample::func() { Sample sam; sam.x = 20; //Geerli //... }

Bazen de static ye ilevler zel amalar gerekletirmek iin belirli kod kalplarnda kullanlr. rnein bir snf trnden yalnzca dinamik nesnelerin yaratlmasna izin verilmesi istendiini dnelim. DynamicOnly bir snf olmak zere DynamicOnly d; biiminde bir nesne yaratlmasna engel olunsun. DynamicOnly snfna ilikin tm nesnelerin dinamik olarak, yani new ileciyle oluturulmas bir zorunluluk olsun. Bu nasl salanabilir? Snfn kurucu ilevleri snfn private blmne yerletirilirse DynamicOnly d; 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:

129/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class DynamicOnly{ DynamicOnly (); //balang ilevi private blmde. public: static DynamicOnly *create_object(); }; DynamicOnly * DynamicOnly::create_object() { return new DynamicOnly; //Geerli nk ye ilev iinde baka bir private ilev arlabilir. } #include <iostream> using namespace std; int main() { // DynamicOnly d; Geersiz! // DynamicOnly *ptr = new DynamicOnly; Geersiz! DynamicOnly *ptr = DynamicOnly::create_object(); //Geerli nk create_object ilevi public blmde delete ptr; //Yaratlan dinamik nesne yok ediliyor. return 0; }

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.

Snflarn const static Elemanlar


Snfn bir statik eleman const anahtar szc ile bildirilebilir. Bu durumda snfa zg deeri deimeyecek bir eleman yaratlm olur. Snfn bir const static eleman eer bir tamsay trnden ise bu elemana snf iinde ilkdeer verilebilir: class Myclass { const static int size = 100; int a[size]; //... }; Bir deimez ifadesiyle ilkdeerini alm const statik eleman bir deimez ifadesi olarak kullanlabilir. Byle elemanlar yalnzca bir snf ilgilendiren simgesel deimezler olarak grev yapabilir.

130/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

leleri Ykleyen ye levler


leleri ykleyen ye ilevler snfn dier ye ilevleri gibi bildirilip tanmlanr. Bu ilevlerin snf bildirimi iindeki bildirimi aadaki gibi olmaldr: [geri dn deerinin tr] ile <ile simgesi> ([parametreler]); Genel biimde de grld gibi, bu ilevlerin bildirimi dier ye ilevlerde olduu gibi yaplr. le ykleyen ilevler ile snfn dier ye ilevleri arasndaki tek fark isimlendirmeye ilikindir. le ykleyen ilevin isimleri operator anahtar szc ile bir ile simgesinden oluur. rnein: class Myclass{ private: int a; public: bool operator<(int); //... };

131/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki rnekte ilev ismi olarak operator< kullanlyor. operator, C++n bir anahtar szcdr. Hem bildirimde hem de tanmlama srasnda kullanlmas gerekir. operator ile > ayr ayr atomlar olduu iin aralarna istenildii kadar boluk karakteri koyulabilecei gibi bu iki atom bitiik de yazlabilir. le simgesi ancak C++n ilelerinden biri olabilir. C++n ileci olmayan bir simge iin ile ykleyen ilev yazlamaz. rnein aadaki ilevin bildirimi geerli deildir: void operator $ (int); //Geersiz

nk C++da $ atomuyla belirtilen bir ile yoktur. leleri ykleyen ye ilevlerin tanmlanmas ve arlmas dier ye ilevler gibi yaplr. rnein yukarda bildirilen operator> ilevi snfn dnda aadaki gibi tanmlanabilir.

leleri Ykleyen levlerin Parametrik Yaps


Baz ileler hari olmak zere ileleri ykleyen ilevlerin geri dn deerleri herhangi bir trden olabilir. Ancak parametre deikeni saylar zerinde bir koul vardr. Bir snfn yesi olan ile ykleyici ilevler ya hi parametre almaz ya da tek parametre alr. Tek terimli (unary) ilelere ilikin ilevlerin parametresiz olmas, iki terimli (binary) ilelere ilikin ilevlerin ise tek parametre almas zorunludur. Aadaki rnei inceleyin: class Complex { double real, imag; public: Complex(double, double); Complex operator/(double)const; bool operator!()const; //... };

Complex isimli snfn operator/ ilevinin tek bir parametre deikeni var. lev Complex trne geri dnyor. Blme ileci iki terimli olduu iin, ye ilev ile yklendiinde, ilevi tek parametresi olmal. rnein bu ilevin snf iinde Complex operator/(double, double)const; biiminde bildirilmesi geersiz olurdu. Complex snfnn operator! ilevinin ise, parametre deikeninin olmadn bu ilevin bool trne geri dndn gryorsunuz. Tek terimli mantksal deil ileci ye ilev ile yklendiinde, ilevin parametre deikeni olmamaldr. rnein bu ilev bool operator!(int)const; biiminde bildirilseydi, bildirim geersiz olurdu.

132/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Ancak baz ileler hem tek terimli hem de iki terimli olarak kullanlr. rnein * ileci arpma ileci olarak kullanldnda iki terimli iken, ierik ileci olarak kullanldnda tek terimlidir. Bu ile hem binary hem de unary ile olarak yklenebilir. Benzer durum +, -, & ileleri iin de geerlidir. Bu ileler de hem tek terimli hem de iki terimli ile olarak yklenebilir.

leleri Ykleyen ye levlere Yaplan arlar


leleri ykleyen ilevler hem normal bir ilev gibi, hem de bir ilecin kullanlmasyla ksa biimde arlabilir. Zaten bu ilevlerin varlk nedeni ksa arnn verdii okuma ve yazma kolaylndan faydalanmaktr. x bir snf nesnesi, y herhangi bir trden nesne ya da deimez olmak zere, x.operator <ile simgesi> (y); gibi bir ar biimi ile, x <ile atomu> y ar edeerdir. rnein; z = x + y; ile z.operator=(x.operator+(y)); ayn anlamdadr. Ya da rnein, x.operator!(); yerine ksaca !x yazlabilir. phesiz bu ksa yaz biimi daha kolay okunabilir. Ksa ar biimi ile sanki snf nesneleri normal ilelerle ileme sokuluyormu gibi bir alg dzeyi elde edilmi olur. Bylece snf kullanan programclarn iini kolaylar. object bir snf trnden bir nesne ve right_operand iki terimli bir ilecin sa terimi olsun: object + right_operand biimindeki bir ifade derleyici tarafndan aadaki gibi bir ye ilev arsna dntrlebilir: object.operator+(right_operand); Yukardaki rnekte toplama ilecinin solundaki snf nesnesi iin, object nesnesinin ait olduu snfn operator+ ye ilevi arlyor. Bir ye ilevin bir snf nesnesi ile arlmas durumunda snf nesnesinin adresinin ileve gizlice this adresi olarak geirildiini biliyorsunuz. Toplama ileci iin arlacak ilevin her iki nesne zerinde ilem yapabilmesi iin, iki nesneye de ulamas gerekir, deil mi? Soldaki snf nesnesinin adresi this adresi olarak ileve geirildiine gre, ilevin yalnzca sadaki nesnenin deerini almas gerekir. te bu yzden iki terimli bir ile ye ilev olarak yklendiinde, bu ilevin tek bir parametresi olmaldr. !object

133/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

gibi bir ifade de ye ilev arsna dntrlebilir. object.operator!(); Bu ilev zaten object nesnesinin adresini this adresi olarak alacana gre ve ! ilecinin baka da bir terimi olmadna gre ilevin parametre deikeninin olmamas gerekir. Bir ileci ykleyen ilevi yazarken ilecin ald terim says (arity) deitirilemez. rnein ! ilecinin Complex isimli bir snf iin ye ilev biiminde yklenmek istendiini dnelim. ! ileci C++ n tek terimli ileci olduuna gre, bu ilecin grevini slenecek ilevin parametre deikeni olmamaldr.

leleri Ykleyen Global levler


Bir snf nesnesi bir ilecin terimi olduunda global bir ilevin arlmas da salanabilir. Bu durumda arlan ilevlere ile ykleyen gloabal ilev (global operator function) denir. a ve b Myclass isimli bir snfa ait nesneler olmak zere a + b gibi bir ifade, uygun bir global ilevin tanmlanmas durumunda operator+(a, b) gibi bir arya dntrlebilir. Bu durumda byle bir global ilevin iki parametre deikeni olmaldr. levin bildirimi aadaki gibi olabilir: Myclass operator+ (const Myclass &, const Myclass &); Bir ileci ykleyen gobal ilevin ka parametre deikeni olmal? Global ilevler snf nesnelerinin adreslerini gizlice almadklarna gre, ile ka terimli ise ilevin de o kadar parametresi olmaldr. ki terimli bir ilecin sol teriminin bir snf nesnesi olmamas durumunda bir ye ilevin arlamayacan biliyoruz. Myclass m; tanmlanmasndan sonra m + 5 gibi bir ifade iin snfn operator+ ilevi arlabilir. Ancak, 5 + m gibi bir ifade iin snfn operator+ ilevinin arlmas mmkn deildir. Ancak toplama ilecinin deime zellii olduuna gre Myclass snf iin de bu durumun geerli olmas gerekir deil mi? Myclass snf iin global bir operator+ ilevinin tanmlanm olmas durumunda yukardaki ifade aadaki gibi bir arya dntrlebilir: operator+(5, m); leleri ykleyen ilevlere rnek verebilmek iin aada Myint isminde yeni bir snf tanmlyoruz. Myint snf trnden bir nesne C++n nceden tanmlanm tr olan int trnn yerine kullanlabilecek. Snf yalnzca ile ykleyen ilevlere rnek vermek iin tanmlyoruz. Snfn bildirimini inceleyin: class Myint{ int m_val; public: Myint(int = 0); //... };

Myint snf trnden bir nesnenin iinde tamsay deeri tutmas ve nesnenin C++n doal veri tr olan int trnn sokulabilecei her trl ileme sokulabilmesi hedefleniyor. Tabii istenirse snfa doal veri tr olan int trnde olmayan baz zellikler

134/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

de eklenebilir. Snfn private blmnde yer alan m_val isimli eleman, snf nesnesinin temsil ettii deeri tutmak iin bildirildi. Snfn kurucu ilevi varsaylan deer alyor. Kurucu ileve bir argman gnderilmedii zaman nesnenin m_val isimli eleman 0 deerini alr. Yani nesne 0 deerini tutuyor olur. Dier taraftan kurucu ilev tek parametre deikenine sahip olduu iin dntren kurucu ilev olarak da grev yapar. Yani int trden olan bir ifade ya da otomatik dnmle int trne dntrlebilecek doal trlerden bir ifade, otomatik olarak Myint snf trnden geici bir nesneye dntrlebilir. Otomatik dnmn istenmemesi durumunda kurucu ilev explicit anahtar szcyle tanmlanabilirdi. Snfn kurucu ilevi aada tanmlyoruz: Myint::Myint(int val): m_val(val) { } Kurucu ilevin tamamnda MIL szdiziminin kullanldn gryorsunuz. Snf iin kopyalayan kurucu ilevin, atama ilevinin, ve sonlandrc ilevin tanmlanmasnmasna gerek yok, deil mi? Derleyicinin yazaca ilevler yeterli olur. Myint snf trnden iki nesnenin elemanlarnn karlkl olarak biribirine atanmasnda bir saknca olmaz.

Karlatrma lelerinin Yklenmesi


C++n tm karlatrma ileleri iki terimlidir. Karlatrma ilelerini snfn bir ye ileviyle ya da global bir ilevle yklemek mmkndr. Karlatrma ilelerinin bool trden deer rettiini biliyorsunuz. Ortak arayz korumak ve ayn armdan faydalanmak iin, bir snfa ilikin karlatrma ilevlerinin de benzer biimde bool trne geri dnmesi tercih edilir. ye ilevler olarak yazldklarnda tek parametre deikenine sahip olurlar. Global ilevler olarak yazlmalar durumunda ise, iki parametre deikeninin olmas gerekir. Myint snf iin karlatrma ilelerini ye ilevler olarak bildiriyoruz. class Myint { public: //... bool operator==(const Myint &r) const; bool operator!=(const Myint &r) const; bool operator<(const Myint &r) const; bool operator<=(const Myint &r) const; bool operator>(const Myint &r) const; bool operator>=(const Myint &r) const; //... };

Tm ye karlatrma ilevlerinin bool trne geri dndn gryorsunuz. Karlatrma ilelerinin yan etkisi yoktur. Yani karlatrma ileminin sonucunda ilecin sa ya da sol terimi olan nesnelerin deeri deitirilmez. lecin sol terimi olan nesnenin deerinin deitirilmeyecei iin ilevleri const olarak bildirdik. Karlatrma ileleri sa terimleri olan nesneleri de deitirmeyecekleri iin ilevlerin parametre deikenleri Myint snf trnden bir const referans olarak seildi. Bu referans ilgili ilecin sa terimi olan snf nesnesinin yerine geer. levlerin const ye ilevler olmalar ve const referans parametreye sahip olmalar nemlidir. Bylelikle const snf nesneleri de bu ilelerin terimi olabilir. Aada ilev tanmlar yer alyor. bool Myint::operator==(const Myint &r) const { return m_val < r.m_val; }

135/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin bool Myint::operator!=(const Myint &r) const { return r < *this; } bool Myint::operator<(const Myint &r) const { return !(*this < r); } bool Myint::operator<=(const Myint &r) const { return !(*this > r); } bool Myint::operator>(const Myint &r) const { return *this < r || r < *this; } bool Myint::operator>=(const Myint &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 int main() { Myint i(25); if (i == 25) cout << "doru" << endl; return 0; }

gibi bir kod paras geerlidir. nk i == 25 ifadesi i.operator==(25); ifadesine edeer olduundan, derleyici bu ifadeyi de otomatik tr dnmyle i.operator==(Myint(25)); biiminde ele alr.

136/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Aritmetik lelerin Yklenmesi


ki terimli aritmetik ileler, yani toplama, karma, arpma ve blme ileleri, ye ilevler ya da global ilevlere yklenebilir. Bu ileleri ykleyen ilevler ounlukla ilgili snf trne geri dner. ye ilevler olarak yazldklarnda tek parametre deikenine sahip olur. Global ilevlerin ise iki parametresi olur. Myint snfnn aritmetik ilelerini snfn ye ilevleri olarak bildiriyoruz: class Myint { public: //... Myint operator+ (const Myint &r) const; Myint operator- (const Myint &r) const; Myint operator* (const Myint &r) const; Myint operator/ (const Myint &r) const; Myint operator% (const Myint &r) const; };

i1 ve i2 Myint snf trnden nesneler olmak zere i1 + i2 gibi bir toplama ileminden yine Myint snf trnden bir deer elde edilmesi beklenir, deil mi? Bu durumda toplama ilevleri Myint trnden bir deer dndrmelidir. levin neden Myint & ya da Myint * trne geri dnemeyeceini tartn. levimizin Myint snf trnden bir referansa geri dndn dnelim. Geri dn deeri olan referans hangi nesnenin yerine geebilir? Yerel bir nesneyi referans yoluyla geri dndrmenin bir programlama hatas olduunu biliyorsunuz. Dinamik bir nesneyi, ya da statik yerel bir nesneyi geri dndrmek de ilevin kullanmn olduka snrlar. Aritmetik ileler terimleri olan nesneleri deitirmez. Bu ileler ykleyen ye ilevlerin const ye ilev olmas gerekir. Bu ilevlerin hem kendileri const ye parametre deikenleri const referans olmalolan const ye ilevler olmaldr. levlerin tanmlarn inceleyin: Myint Myint::operator +(const Myint &r) const { return Myint(m_val + r.m_val); } Myint Myint::operator -(const Myint &r) const { return Myint(m_val + r.m_val); } Myint Myint::operator *(const Myint &r) const { return Myint(m_val * r.m_val); } Myint Myint::operator /(const Myint &r) const { return Myint(m_val / r.m_val); } Myint Myint::operator %(const Myint &r) const { return Myint(m_val % r.m_val); }

levlerin return ifadelerinin Myint trden bir geici nesne olduuna dikkat edin. operator+ ilevi aadaki gibi de yazlabilirdi:

137/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Myint Myint::operator+(const Myint &r) const { Myint result; result.m_val = m_val + r.m_val; return result; }

Yukardaki ilev tanmnda nce yerel result nesnesi iin varsaylan kurucu ilev arlyor. Daha sonra result nesnesine ilemin sonucu olan deer yerletiriliyor. result nesnesinin deeri ile geri dnlyor. Oysa yazlan ilk kodda dorudan bir geici nesnenin deeri ile geri dnlmt. Bylece hem ilem maaliyeti azaltlm olur hem de derleyiciye daha fazla eniyileme ilemi yapma olana verilir.

lemli Atama lelerinin Yklenmesi


imdi de ilemli atama ilelerini inceleyelim. Toplama ilevinin tanmlanmas += ilevinin derleyici tarafndan yazlaca anlamna gelmez. Bir Myint snf nesnesi += ilecinin terimi olursa, programc tarafndan tanmlanm bir operator+= ilevi olmaldr. lemli atama ileleri de ye ilev ya da global ilevler olarak yklenebilir. Ancak bu ilevler de snf nesnesini deitirdikleri iin ye ilev olarak yklenmeleri ok daha doal olur. lemli atama ilevlerinin snf iinde bildirimlerini yapalm: class Myint { public: //... Myint &operator+= Myint &operator-= Myint &operator*= Myint &operator/= Myint &operator%= };

(const (const (const (const (const

Myint Myint Myint Myint Myint

&r); &r); &r); &r); &r);

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+= (const Myint &r) { m_val += r.m_val; return *this; } Myint &Myint::operator-= (const Myint &r) { m_val -= r.m_val; return *this; } Myint &Myint::operator*= (const Myint &r) { m_val *= r.m_val return *this; } Myint &Myint::operator/= (const Myint &r) { m_val /= r.m_val;

138/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin return *this; } Myint &Myint::operator%= (const Myint &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 amac bir deer tamak deil, yalnzca ileve farkl bir imza kazandrmaktr. Myint snfnn tanm iinde yer alan ++ ve - ilevlerinin bildirimlerini inceleyelim. class Myint { public: //... Myint &operator++ (); Myint &operator-- (); Myint &operator++ (int); //sonek ++ Myint &operator-- (int) ; //sonek -}; nek konumundaki ileleri ykleyen ilevler Myint snf trnden referansa dnerken, sonek konumunda olanlar Myint trne geri dnyor. Acaba neden? Bildiiniz gibi, bu ilelerin terimi doal trden nesneler olduklarnda, ileler yan etki olarak, terimi olan nesnelerin deerlerini 1 arttrr ya da azaltr. le ister sonek ister nek konumunda olsun terimi olan nesnenin deeri yan etki sonucu deiir. Ancak ilecin rettii deer nek ya da sonek konumu iin farkldr. nek konumundaki ++ ileci, terimi olan nesnenin deerinin 1 fazlasn retirken sonek konumundaki ++ ileci terimi olan nesnenin kendi deerini retir. Snf nesneleri iin yazlan ++ ilevlerinde de ilecin bu zelliine ounlukla uyulur. Zaten daha nce de sylendii gibi ile yklemesinin temel amalarndan biri ilelerin doal trler iin verdii arm ve soyutlamadan snf nesneleri iin de faydalanmaktadr. Myint snf iin ++ ilevleri aadaki gibi yazlabilir. nce nek konumundaki ++ ileci iin bir ilev tanmlayalm. Myint &Myint::operator++() { return *this += 1; } levi birlikte inceleyelim. levin return deyiminde daha nce yazlan += ilevi arlyor. *this += 1 Yukardaki ilev ars ile operator+= ilevi *this nesnesinin m_val elemannn deerini 1 arttryor. Daha nce yazlan += ilevi *this nesnesini geri dndrdne gre, ++ ilevi de *this nesnesini geri dndrm oluyor. Bylece ++ ilecinin terimi olan snf nesnesinin artm deerini kullanma olana getiriliyor.

139/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Myint a, b(9); gibi bir tanmlamadan sonra a = ++b; atama deyimi a.operator=(b.operator++()); gibi bir arya dntrlr deil mi? imdi de sonek konumundaki ++ ilecini ykleyen ilevi tanmlayalm. Myint Myint::operator++() { Myint ret_val(*this); ++*this; return ret_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> using namespace std; int main() { Myint x1(10), x2(20); Myint y1(++x1), y2(x2++); cout cout cout cout } << << << << "y1 "y2 "x1 "x1 = = = = " " " " << << << << y1 y2 x1 x1 << << << << endl; endl; endl; endl;

return 0;

leleri Ykleyen Global levlerin Yazlmas


lelerin ou global ilev biiminde de yklenebilir. Yukardaki rnekte Myint snf iin toplama ileci ye ilev ile yklenmiti. imdi ayn ileci global ilevle ykleyelim. Myint operator+(const Myint &, const Myint &);

140/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Byle bir ilevin var olmas durumunda Myint a(10), b(20); a + b gibi bir ifade, daha nce sylendii gibi operator+(a, b) gibi bir ilev arsna dntrlr. leleri ykleyen global ilevler snfn ye ilevler olmad iin bu ilevler iinde normal olarak snfn private blmne eriilemez. Myint operator+(const Myint &r1, const Myint &r2) { return Myint(r1.m_val, r2.m_val); //Geersiz! } Yukardaki rnekte operator+ ilevi iinde snfn private m_val elemanna eriilemez. lev snfn yesi olmad iin, r1 ve r2 nesnelerinin private elemanlarna erimesi sz konusu deildir? Ancak uygun bir geri dn deeri retmek iin bu private elemanlarn deerlerine gereksinim duyuluyor. Bu durumda ileci ykleyen global bir ilev, iini nasl yapabilir? levin kendisinden beklenen ii yapabilmesi iin farkl yntemler kullanlabilir: Snfn public arayznde bulunan bir get ilevi ile m_val elemannn deeri elde edilebilir. Ancak byle bir get ilevinin bulunmas, her snf iin istenen bir durum deildir. class Myint{ public: int get_val() const {return m_val;} //... };

Global operator+ ilevi get_val public ye ilevini ararak m_val deerini elde edebilir. Myint operator+(const Myint &r1, const Myint &r2) { return Myint(r1.get_val(), r2.get_val()); } Myint snf, bu ileve arkadalk bildirimi ile private elemanlarna zel eriim hakk verebilir. class Myint{ //... friend Myint operator+(const Myint &, const Myint &); }; Global ilev iini grebilmek iin snfn bir public ilevini arabilir. Aadaki kodu inceleyin: Myint operator+(const Myint &r1, const Myint &r2) { return Myint (r1) += r2; }

141/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

levin ana blou iinde yaratlan Myint snf trnden geici nesne, ilkdeerini ilevin parametre deikeni olan r1 nesnesinden alyor. Daha sonra geici nesne iin snfn += ilevi arlyor. Bu ye ileve r2 nesnesi argman olarak gnderiliyor. leci ykleyen global ilev, ye += ilevinin geri dn deeri ileri geri dnyor. Ayn ilecin hem ye hem de global ilev olarak yklenmesi, ou durumda algsal karmakla neden olur. Bu nedenle programclar tarafndan pek tercih edilmez. Ayrca baz durumlarda, ileve yaplan ar yznden ift anlamllk hatas oluabilir. Yani derleyicinin ye ilevin mi global ilevin mi arlmak istendiini anlayamamas sonucu bir hata durumu oluabilir. phesiz bu hata durumu rnein operator+ ilevinin ilev ar ileciyle arlmasyla engellenebilir: class Myclass { public: Myclass operator+(Myclass, Myclass); }; Myclass operator+ (Myclass, Myclass); void func() { Myclass a, b, c; //... c = a + b; //ift anlamllk hatas - ye ilev mi global ilev mi? c = operator+ (a, b); //ift anlamllk hatas yok global ilev c = a.operator+(b); //ift anlamllk hatas yok ye ilev // }

le Ykleyen levlere likin Kstlamalar


C++n baz ileleri yklenemez. Bu ileler unlardr: znrlk ileci (::) sizeof ileci (sizeof) koul ileci ?: . ileci .* ileci (C dilinde yer almayan bu ileci ileride ele alacaz) Yukarda listesi verilen ilelerin snfn ye ilevleriyle ya da global ilevlerle yklenmesi geersizdir. Aadaki ileler ise yalnzca snfn ye ilevleriyle yklenebilir: keli ayra ileci ilev ar ileci atama ileci ok ileci Bu ilelerin global ilevler biiminde yklenmeleri geersizdir. leleri ykleyen ilevler varsaylan argman alamaz. Bu kurala uymayan tek ilev, ilev ar ilecini ykleyen ilevdir. Bu ilevin yklenmesini ileride ele alacaz.

le Ykleyen levlerin arlma Sras


le ykleyen ilevler C++ dilinde ilelerin belirlenmi ncelik srasna gre arlr. Aadaki kod parasn inceleyin:

142/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin Myint a(1), b(5), c(7), d; d = ++a * b c % 2; Yukardaki ifade derleyici tarafndan yle bir koda dntrlr: d.operator=(a.operator++().operator*(b).operator-(c.operator%(2))); lelerin dil tarafndan belilenmi ncelik seviyeleri deitirilemez.

Keli Ayra lecini Ykleyen lev


Keli ayra ileci snfn ye ileviyle yklenebilir. levin yalnzca bir parametre deikeni olmaldr. a bir snf nesnesi olmak zere a[b] gibi bir ifade, keli ayra ilevinin tanmlanm olmas durumunda a.operator[](b) gibi bir ilev arsna dntrlr. Keli ayra ilevi genellikle bir referans trne geri dner. Bylece, ilev ar ifadesi dorudan bir nesne olarak kullanlabilir. int trden dinamik bir dizinin kullanmn kolaylatrmak iin, Array isimli bir snfn tanmlandn dnelim. operator[] ilevinin uygun biimde tanmlanmas durumunda, Array snf trnden a gibi bir nesne ile a[5] = 20; gibi bir atama yaplabilir. Konumuzla dorudan ilgisi olmayan ilevleri kartarak, Array snfn tanmlayalm. class Array { int *m_p; size_t m_size; public: Array(int size = 0, int val = 0); ~Array(); int operator[](int) const; int &operator[](int); // }; Array::Array(int size, int val) { m_size = size; if(m_size == 0){ m_p = 0; return; } m_p = new int[m_size]; for (int k = 0; k < m_size; ++k) m_p[k] = val; } Array::~Array() { delete [] m_p; } int &Array::operator[](int index) { return m_p[index]; }

143/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

int Array::operator[](int index)const { return m_p[index]; } #include <iostream> int main() { Array a(20); const Array ca(10, 1); a[3] = 5; //ca[2] = 10; //Geersiz Std::cout << ca[5] << std::endl; return 0; } 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.

Snf Nesnelerinin Deerlerinin Yazdrlmas ve Klavyeden Snf Nesnelerine Deer Alnmas


Cden farkl olarak C++ dilinde giri k ilemlerinin ablon bazl snflar yardmyla yapldn biliyorsunuz. int i = 10; double d = 3.5; cout << i <<d; Yukardaki kod parasnda yer alan cout, aslnda ostream isimli bir snf trnden global bir nesnedir. ostream snfnn bir seri operator << ilevi vardr. C++ dilinin doal trlerinden parametre deikenlerine sahip olan bu ilevler, yine ostream snf trnden referansa geri dner.

144/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

cout << i << d gibi bir ifade derleyici tarafndan cout.operator<<(i).operator<<(d) biimine dntrlr. cout nesnesi iin arlan ilk ykleyici ilev i deikenini referans yoluyla alr. i deikeninin deerini ekrana yazdrdktan sonra cout nesnesini referans yoluyla geri dndrr. Geri dndrlen cout nesnesi iin ikinci kez arlan operator<< ilevi, bu kez d nesnesini referans yoluyla argman olarak alr. d nesnesinin deeri ekrana yazdrldktan sonra cout nesnesi yine referans yoluyla geri dndrlr. Nasl doal trlerden ifadelerin deerleri ostream snfnn ye operator<< ilevleriyle ekrana yazdrlabiliyorsa, programc tarafndan tanmlanan bir snf trnden nesnenin deeri de, global<< ilevi yardmyla ekrana ya da bir dosyaya yazdrlabilir. Bylece k ilemleri iin doal veri trleri ve programc tarafndan tanmlanan trler arasnda ortak bir arayz salanm olur. Myint snf trnden nesnelerin deerlerinin, aadaki biimde ekrana yazdrlmak istendiini dnelim. Myint a(10), b(20); cout << a << " " << b << endl; ostream snfnn kodlarna dorudan ekleme yaplamaz. Ama global bir operator<< ilevi yazlabilir: cout << a gibi bir ifadenin global bir ilev arsna dntrlmesi iin ilevin iki parametre deikenine sahip olmas gerekir. Birinci parametre cout nesnesini alrken ikinci parametre Myint snf trnden a nesnesini alr. levin geri dn deeri ostream snf trnden bir referans yaplrsa, ilevin yine cout nesnesini geri dndrmesi salanabilir. Aadaki ilevi inceleyin: ostream &operator<<(ostream &os, const Myint &r) { return os << r.m_val; } ilevimiz operator<<(cout, a) biiminde arldna gre, ilevin parametre deikeni olan referans os, cout nesnesinin yerine geer. kinci parametre olan r referans da a nesnesinin yerine geer. levin tanm iindeki os << r.m_val ifadesi, bu kez ostream snfnn ye ilevini ararak m_val isimli elemann deerini ekrana yazdrr. levimiz os.operator<<(r.m_val) arsnn geri dn deeriyle geri dner. Bu da cout nesnesinin kendisidir. Yalnz ufak bir sorunumuz var. Global ilev iinde

145/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin r.m_val eriiminin geerli olmas iin Myint snfnn, global ileve arkadalk vermesi gerekir, deil mi? class Myint { //... friend std::ostream &operator<<(std::ostream &os, const Myint &r); }; C++n standart ostream snf std isimalan iinde tanmlandndan, arkadalk bildiriminde bu snfn ismi, std::ostream olarak yazlyor. imdi de Myint snf trnden bir nesneye standart giri biriminden (klavyeden) deer alan bir ilev tanmlayalm: int i = 10; double d = 3.5; cin >> i >> d; Yukardaki kod parasnda yer alan cin, istream isimli bir snf trnden global bir nesnedir. istream snfnn bir seri operator>> ilevi vardr. C++ dilinin doal trlerinden parametre deikenlerine sahip olan bu ilevler, yine istream snf trnden referansa geri dner. cin >> i >> d gibi bir ifade derleyici tarafndan cin.operator>>(i).operator>>(d) biimine dntrlr. arlan ilk ilev, i deikenini referans yoluyla alr. lev i deikenini klavyeden ald deerle set ettikten sonra cin nesnesini referans yoluyla geri dndrr. Geri dndrlen cin nesnesi iin ikinci kez arlan operator>> ilevi bu kez d nesnesini referans yoluyla argman olarak alr. d nesnesini klavyeden alnan deerle set ettikten sonra cin nesnesi yine referans yoluyla geri dndrlr. Myint a(10), b(20); cin >> a >> b; istream snfna dorudan ekleme yaplamaz ama global bir operator>> ilevi yazlabilir. cin >> a gibi bir ifadenin global bir ilev arsna dntrlmesi iin ilevin iki parametre deikenine sahip olmas gerekir. levin birinci parametresi cin nesnesini referans yoluyla alrken ikinci parametre Myint snf trnden a nesnesini yine referans yoluyla alabilir. levin geri dn deeri istream snf trnden bir referans yaplrsa, yeniden cin nesnesinin geri dndrlmesi salanabilir. Aadaki ilev tanmn inceleyin. istream &operator<<(ostream &is, Myint &r) { return is >> r.m_val; }

146/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ilevimiz operator>>(cin, a) biiminde arldna gre, ilevin parametre deikeni olan referans is, cin nesnesinin yerine geiyor. kinci parametre olan r referans da a nesnesinin yerine geiyor. levin tanm iindeki is >> r.m_val ifadesi ile bu kez istream snfnn ye ilevi arlarak m_val isimli eleman klavyeden alnan deerle set ediliyor. levimiz is.operator>>(r.m_val) arsnn geri dn deeriyle geri dnyor. Yani ilev cin nesnesini geri dndryor.

Numaralandrma Trleri in lelerin Yklenmesi


Bir ile, bir numaralandrma tr iin de yklenebilir. rnein tipik bir kullanm, ++ ve - ilelerinin yklenmesiyle numaralandrma deerlerinin dolalmasn salamaktr. Aadaki program inceleyin: #include <iostream> enum Months {January, February, March, April, May, June, July, August, September, October, November, December}; Months &operator++ (Months &m) { if (m == December) return m = January; int temp = m; return m = Months(++temp); } std::ostream &operator<<(std::ostream &os, const Months &r) { switch (r){ case January : return os << "January"; case February : return os << "February"; case March : return os << "March"; case April : return os << "April"; case May : return os << "May"; case June : return os << "June"; case July : return os << "July"; case August : return os << "August"; case September : return os << "September"; case October : return os << "October"; case November : return os << "November"; case December : return os << "December"; } } int main() { Months m = January; for (;;) { std::cout << m << std::endl; ++m; if (m == January) break; } return 0; }

147/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

le ykleyen ilevlerin kullanmna rnek vermek amacyla, aada Date isimli bir snfn kodlarn veriyoruz. Date isimli snf trnden bir nesne bir tarih bilgisi tutmaktadr. ///////////////////// #include <iosfwd> #include <stdexcept> date.h /////////////////////////////

class Date{ int m_d, m_m, m_y; int m_totaldays; static bool is_valid (int, int , int); static bool is_leap (int y){ return y % 4 == 0 && 100 != 0 || y % 400 == 0; } static int ms_daytabs[][13]; static int ms_yeartabs[2]; static const char *ms_days[]; static const char *ms_mons[]; const static int msc_yearbase = 1700; const static int msc_factor = 0; void set_totaldays(); void set(int, int, int); Date &revdate(int totaldays); public: Date(int, int, int); Date(); int get_year_day() const; int get_week_day() const; int get_mday() const {return m_d;} int get_mon() const {return m_m;} int get_year() const {return m_y;} Date &operator+=(int n); Date &operator-=(int n); Date &operator++(); Date operator++(int n); //postfix Date &operator--(); Date operator--(int n); //postfix //friend functions friend std::ostream &operator<<(std::ostream &, const Date &); friend std::istream &operator>>(std::istream &, Date &); friend bool operator<(const Date &, const Date &); friend int operator-(const Date &, const Date &); }; class BadDate:public std::out_of_range{ public: BadDate(const char *pmsg):std::out_of_range(pmsg){} }; //global functions bool operator>(const Date &, const Date &); bool operator>=(const Date &, const Date &); bool operator<=(const Date &, const Date &); bool operator==(const Date &, const Date &); bool operator!=(const Date &, const Date &); Date operator+(const Date &, int); Date operator+(int, const Date &); Date operator-(const Date &, int); /////////////////////////////////////////////////////////////////////////// //date.cpp file #include <iostream> #include <iomanip> #include <ctime> using namespace std; int Date::ms_daytabs[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},

148/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, }; int Date::ms_yeartabs[] = {365, 366}; const char *Date::ms_mons[] = {"", "Ocak", "Subat", "Mart", "Nisan", "Mays", "Haziran", "Temmuz", "Agustos", "Eylul", "Ekim", "Kasim", "Aralik"}; const char *Date::ms_days[] = {"Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi", "Pazar"}; /////////////////////////////////////////////////////////////////////////// void Date::set(int d, int m, int y) { if (!is_valid(d, m, y)) throw BadDate("gecersiz tarih\n"); m_d = d; m_m = m; m_y = y; set_totaldays(); } /////////////////////////////////////////////////////////////////////////// void Date::set_totaldays() { m_totaldays = 0; for (int k = msc_yearbase; k < m_y; ++k) m_totaldays += ms_yeartabs[is_leap(k)]; m_totaldays += get_year_day(); } /////////////////////////////////////////////////////////////////////////// Date &Date::revdate(int totaldays) { m_totaldays = totaldays; m_y = msc_yearbase; 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(int d, int m, int y) { set(d, m, y); } /////////////////////////////////////////////////////////////////////////// Date::Date() { time_t timer = time(0); tm *tp = localtime(&timer); set(tp->tm_mday, tp->tm_mon + 1, tp->tm_year + 1900); } /////////////////////////////////////////////////////////////////////////// int Date::get_year_day()const { int yearday = m_d; for (int k = 1; k < m_m; ++k) yearday += ms_daytabs[is_leap(m_y)][k]; return yearday; } ///////////////////////////////////////////////////////////////////////////

149/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin int Date::get_week_day()const { return (m_totaldays + msc_factor) % 7; } /////////////////////////////////////////////////////////////////////////// Date &Date::operator+=(int n) { return revdate(m_totaldays + n); } /////////////////////////////////////////////////////////////////////////// Date &Date::operator-=(int n) { return revdate(m_totaldays - n); } /////////////////////////////////////////////////////////////////////////// Date &Date::operator++() { return *this += 1; } /////////////////////////////////////////////////////////////////////////// Date Date::operator++(int) { Date retval(*this); ++*this; return retval; } /////////////////////////////////////////////////////////////////////////// Date &Date::operator--() { return *this -= 1; } /////////////////////////////////////////////////////////////////////////// Date Date::operator--(int) { Date retval(*this); --*this; return retval; } /////////////////////////////////////////////////////////////////////////// bool Date::is_valid(int d, int m, int y) { if (y < msc_yearbase) return false; if(m < 1 || m > 12) return false; return m > 0 && d <= ms_daytabs[is_leap(y)][m]; } /////////////////////////////////////////////////////////////////////////// ostream &operator<<(ostream &os, const Date &r) { return os << setw(2) << r.m_d << " " << Date::ms_mons[r.m_m] << " " << r.m_y << " " << Date::ms_days[r.get_week_day()]; } /////////////////////////////////////////////////////////////////////////// istream &operator>>(istream &is, Date &r) { int d, m, y; is >> d >> m >> y; r.set(d, m, y); return is; } /////////////////////////////////////////////////////////////////////////// bool operator<(const Date &d1, const Date &d2) { return d1.m_totaldays < d2.m_totaldays;

150/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin } /////////////////////////////////////////////////////////////////////////// bool operator>(const Date &d1, const Date &d2) { return d2 < d1; } /////////////////////////////////////////////////////////////////////////// bool operator>=(const Date &d1, const Date &d2) { return !(d1 < d2); } /////////////////////////////////////////////////////////////////////////// bool operator<=(const Date &d1, const Date &d2) { return !(d2 < d1); } /////////////////////////////////////////////////////////////////////////// int operator-(const Date &d1, const Date &d2) { return d1.m_totaldays - d2.m_totaldays; } /////////////////////////////////////////////////////////////////////////// Date operator+(const Date &d, int n) { return Date(d) += n; } /////////////////////////////////////////////////////////////////////////// Date operator+(int n, const Date &d) { return d + n; } /////////////////////////////////////////////////////////////////////////// Date operator-(const Date &d, int n) { return Date(d) -= n; } /////////////////////////////////////////////////////////////////////////// int main() { Date today; Date next_year_today(today + 365); while (today <= next_year_today) cout << today++ << endl; return 0; }

151/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: class C_ve_Sistem_Programcilari_Array { // }; void display(const C_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: namespace CDernek{ class CDString{ /* ... */}; void push_back(CDString &); }

152/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

CDernek programc tarafndan bildirilen bir isim alandr. Programc tarafndan bildirilen her isim alan, ayr bir bilinirlik alan belirler. Programc tarafndan bildirilen bir isim alan, baka isim alan bildirimlerini, ilevlerin nesnelerin ablonlarn, trlerin, bildirimlerini ya da tanmlarn ierebilir. Global isim alannda olduu gibi , programc tarafndan bildirilen isim alanndaki her isim de tek bir varla ilikin olmaldr. Yani bildirilen bir isim alannda ayn isim birden fazla kez bulunamaz. Ama farkl isim alan bildirimleri farkl bilinirlik alanlar belirledii iin, farkl isim alanlarnda ayn isimler kullanlabilir. sim alannda kullanlan her isme "isim alan eleman" (namespace member) denir.

sim Alan Tanm


Bir isim alan tanm namespace anahtar szc ile balar. namespace anahtar szcn isim alannn ismi izler. sim alan ismi isimlendirme kurallarna uygun olarak seilmelidir. sim alan ismini izleyen blok iinde isim alan elemanlarnn bildirimleri ve/veya tanmlamalar yer alr. sim alan tanm sonunda sonlandrc atom yer almaz: namespace CDernek { class CDString { // }; // }

sim alan ismi global dzeyde baka bir isimle akmamaldr. sim alan ismi de, tanmn yapld isim alannda tek olmaldr. Yani global isim alan kirlenmesi sorunu tam olarak ortadan kaldrlm olmaz. Ancak isim alanlarnn kullanlmasyla sorun en aza indirilir. Bir bildirimin ya da bir tanmn, bir isim alan iinde yaplmas bildirimin ya da tanmn anlamn deitirmez. sim alan iinde yaplm bir bildirimi ya da tanm dier bildirim ya da tanmlardan ayran tek nokta, bildirilen ya da tanmlanan isimlerin isim alan iinde gizlenmi olmasdr. Bu isimlere dardan ancak nitelendirilmi isimler olarak yani znrlk ileciyle eriilebilir. Bir isim alan tanm ya global dzeyde ya da baka bir isim alan tanmnn iinde yaplmaldr. Yerel dzeyde, yani bir ilevin tanm iinde bir isim alan tanmlanamaz.

sim Alan Bir Bilinirlik Alandr


Programc tarafndan bildirilen her isim alan, yeni bir bilinirlik alan oluturur. Bir isim alan iinde baka isim alan bildirimleri, ilev bildirimleri ve tanmlamalar tr bildirimleri nesne bildirim ve tanmlamalar yer alabilir. Ayn bilinirlik alannda ayn isim birden fazla kez kullanlamaz. sim alan da yeni bir bilinirlik alan oluturduuna gre bir isim alan iinde de ayn isim bulunamaz. Farkl isim alanlar farkl bilinirlik alan belirttiklerine gre, farkl isim alanlar iinde ayn isim kullanlabilir. rnein yukarda bildirilen CDernek isim alanna ek olarak Project isimli bir isim alann daha tanmlanm olsun: namespace CDernek { class CDString { // }; // } namespace Project { class CDString { // }; }

153/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Her iki isim alannda da, tanmlanan snfn ismi CDString olarak seilmitir. Bilinirlik alanlar farkl olduuna gre iki snfn isminin ayn olmas geerlidir. C++'da C'de olmayan iki bilinirlik alan kuralnn daha yer aldn hatrlatalm. Bu bilinirlik alanlarndan birincisinin snf bilinirlik alan olduunu daha nceki konularda deinilmiti. sim alan bilinirlik alan da C dilinde olmayan C++ dilinin ekledii yeni bir bilinirlik alandr.

znrlk leci ve Nitelenmi sim


Programc tarafndan bildirilen bir isim alannn elemanlarna, isim alan dnda erimek iin iki terimli araek konumunda znrlk ileci kullanlr. Bu biimde yazlm isme "nitelenmi isim" (qualified name) denir. Bir isim alan iinde bildirilmi isme, isim alan dnda ancak nitelenmi isimle eriilebilir: namespace Cdernek { class CDString { // } }; void foo(CDString &r); void func(Cdernek::CDString &r) // 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: class CDString { // }; namespace CDernek { class CDString { // }; } void foo(CDString &); void func(CDernek::CDString &) 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.

Global sim Alan


Tm bloklarn dnda kalan blge C++da "global isim alan" (global namespace) olarak isimlendirilir. Global isim alannda bildirilen isimler dier dorudan kullanlabilir. Aslnda

154/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

znrlk ilecinin tek terimli biimi, global isim alanndaki bir isme erimeyi salar. Normal olarak global isim alanndaki bir isme dorudan eriilebilecei iin, bu ileci kullanmak zorunlu deildir. Ancak baz durumlarda global isim alanndaki isim, i bilinirlik alanlarndaki bir isim tarafndan maskelenir. Bu durumda global isim alanndaki isme eriebilmek iin, tek terimli znrlk ilecini kullanmak zorunludur. Aadaki rnei inceleyin: #include <iostream> const int max = 1000; namespace CDernek { const int max = 500; }; int main() { int max = 100; // global isim alanndaki max //Cdernek isim alanndaki max

//blok bilinirlik alanndaki max

std::cout << max << std::endl //blok bilinirlik alanndaki max std::cout << ::max << std::endl; //global isim alanndaki max std::cout << Cdernek:: max << std::endl; //Cdernek isim alanndaki max return 0; }

sim Alanlar Eklemeli Bir Yapya Sahiptir


Daha nce bildirilen bir isim alanna, ayn kaynak dosya iinde ya da baka bir kaynak dosya iinde ekleme yaplabilir. Yani isim alan elemanlar, isim alan iinde bir defada bildirilmek zorunda deildir. Aadaki rnei inceleyin: namespace CDernek { class CDString { // }; } namespace CDernek { class CDVector { // }; }

Ayn dosya iinde yaplan yukardaki tanmlamalar geerlidir. Derleyici bir isim alan tanm grnce, nce namespace anahtar szcn izleyen ismin, daha nceden tanmlanm olan bir alana ilikin olup olmadn aratrr. Eer bu isimli bir isim alan daha nce tanmlanmsa, ikinci bildirimdeki isimleri otomatik olarak ilk isim alan tanmyla birletirir. Yani daha nceden tanmlanm olan bir isim alanna, kaynak kod iinde baka bir noktada ekleme yaplabilir. Yukardaki rnekte hem CDString snf hem de CDVector snf CDernek isim alanndadr. Hata oluturan bir durum sz konusu deildir. sim alanlarnn eklemeli bir yapya sahip olmas zellikle ktphane olutururken programcnn iine yarar. Bir modle ilikin bildirimler, yani modln ara yz bir balk dosyas iinde tanmlanan bir isim alan iinde yer alrken, modln kodlamas (implementasyonu) baka bir kaynak dosyada, yine ayn isim alanna eklenebilir:

155/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin //cdstring.h dosyas namespace CDernek { class CDString { // public: String operator+(const CDString &); // }; } //cdstring.cpp dosyas #include <string.h> namespace CDernek { CDString CDString:: operator+(const CDString &) { } }

sim alanlarnn eklemeli bir yapya sahip olmas zelliinden faydalanlarak, bir isim alan iinde sunulan bir ktphanenin arayz birden fazla balk dosyasna blnebilir. Buna en iyi rnek std isim alandr.

std sim Alan


C++n standart ktphanesinin btn bileenlerinin bildirimleri ve tanmlamalar std ismi verilen bir isim alan iinde yaplmtr. iostream, vector, string gibi standart balk dosyalarnn herhangi birinde bildirilen snflar, ilevler, nesneler, ablonlar std isim alan iinde bildirilmitir. Standart ktphane iinde bildirilen bir ileve ya da snfa erimek iin, normal olarak snfn ya da ilevin ismi, yer ald isim alannn ismiyle nitelenmelidir: std::cout std::string std::cin Bu isimleri nitelemeden kullanabilmek iin using bildirimi ya da using namespace komutu kullanlaca gibi argumana bal arama da (Koenig lookup) yaplabilir. std isim alan bildirimleri iinde deiiklik yapmak, yani bu isim alanna eklemeler yapmak ya da bu isim alanndan baz bildirimleri silmek tanmsz davrantr.

sel sim Alanlar


Bir isim alan iinde baka bir isim alan bildirilebilir. Bylelikle isel (nested) isim alanlar oluturulabilir. sel isim alanlar ktphanedeki kodlarn dzenlenmesini iyiletirmek iin kullanlabilir. namespace CCernek { namespace CStringLib { //... } namespace CMathLib{ //... } }

sel bir isim alanndaki elemanlarn isimleri, o isim alan iinde gizlenir. sel isim alanndaki isimlere kapsayan isim alan ismi :: isel isimalan ismi :: isimalan eleman ismi

156/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

biiminde eriilir. sel bir isim alan, baka bir isim alannda yer alan yeni bir bilinirlik alan yaratr. Derleyicinin isimleri zmlemesi srasnda, isel isim alanlar isel bloklar gibi ele alnr. sel isim alannda bir isme rastlandnda, bu ismin bildirimi, nce isel isim alannda, daha sonra srasyla iten da doru kapsayan isim alanlarnda aranr. sim kapsayan isim alanlarnda da bildirilmemise son olarak isim global isim alannda aranr.

sim Alan Elemanlarnn Tanmlamalar


Bir isim alan elemannn tanmnn yine ayn isim alan iinde yaplabilir. Tanm isim alan dnda da yaplabilir. Bu durumda tanmlanacak isim alan eleman, tanmlama srasnda nitelenmi ismiyle kullanlmaldr: namespace CDernek { namespace CDStringLib { class CDString { int size; public: int get_size() const {return size;} }; CDString operator+(const CDString &, const CDString &); } }

Yukardaki rnekte CDernek isimli isim alan iinde CDStringLib isimli baka bir isel isim alan tanmlanyor. CDStringLib isim alan iinde tanmlanan CDString isimli snfn get_size isimli ye ilevi yine ayn isim alan iinde tanmlanyor. CDString snfnn iinde operator+ ilevi bildiriliyor. Ancak bu ilevin tanm CDStringLib isimli isim alan iinde yaplmyor. Bu ilevin global isim alannda aadaki biimde tanmlandn dnelim: Cdernek::CDStringLib::CDString Cdernek::CDStringLib::operator+(const CDString &r1, const CDString &r2) { CDString result; //... return result; }

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.

157/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: namespace Cdernek { //... namespace CDStringLib { class CDString { int size; //... public: int get_size() const {return size;} //... }; CDString operator+(const CDString &, const CDString &); } CDStringLib::CDString CDStringLib::operator+(const CDString &r1, const CDString &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+ biiminde yazldna dikkat edin.

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. using CDernek::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.

158/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: namespace Mynamespace { int a = 1, b = 2, c = 3; //diger bildirimler } int b = 0; int main() { using Mynamespace::a; a = 10; using Mynamespace::b; b = 20; int c; //using Mynamespace::c; } void func() { int y = a; } //Geersiz!

main ilevi iinde yaplan using Mynamespace::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 using Mynamespace::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. using Mynamespace::a; bildirimi yerel dzeyde, yani main ilevinin iinde yapldndan yalnzca main ilevi iinde

159/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: namespace Mynamespace{ int a = 1, b = 2, mk = c; //dier bildirimler } using Mynamespace::mi; int main() { a = 10; return 0; } void func() { int y = a; }

using bildirimi ile, isim alan elemannn kullanlmas daha kolay hale gelir. using bildirimi ile, bir defada yalnzca isim alannn bir eleman ilgili bilinirlik alanna katlabilir. using namespace komutuyla bir defada birden fazla ismin nitelenmeden kullanlmas salanabilir.

using namespace Komutu


Bir isim alan iinde tantlan isimlerin her birine nitelenmemi isimle eriebilmek amacyla, her isim iin ayr bir using bildirimi yaplabilir. Ancak ok sayda isim iin using bildiriminin teker teker yaplmas zaman alcdr. Byle bildirimler programn okunabilirliini de bozar. Bir isim alanndaki isimlerin hepsinin tek bir komutla nitelenmemi isim olarak kullanlabilmeleri mmkn klnmtr. Bir using namespace komutu, using ve namespace anahtar szckleriyle balar. Bu szckleri daha nce tanmlanm bir isim alannn ismi izler: using namespace CDernek; using namespace std; Daha nce tanmlanm bir isim alanna ilikin olmayan bir ismin kullanlmas geersizdir. using namespace komutuna konu isim alanndaki tm isimler, nitelenmemi isimler olarak kullanlabilir hale gelir. using namespace komutu, isim alan elamanlarnn, sanki bu elemanlar isim alannn tanmnn bulunduu yerde, ancak isim alannn dnda tanmlanm gibi grlmesini salar. Aadaki rnei inceleyin: namespace Mynamespace{ int a = 1, b = 2, c = 3; //diger bildirimler } void func(); int b = 0; int main() {

160/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin using namespace Mynamespace; a = 10; //b = 20; //Geersiz Mynamespace::b = 100; ::b = 178; int c; c = 30; //yerel c return 0; } void func() { //int y = 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.

161/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin //header1.h namespace HDR1 { void foo(); //... } //header2.h namespace HDR2 { void foo(); //... } //app.cpp #include "header1.h" #include "header2.h" using namespace HDR1; using namespace HDR2; int main() { foo(); //ift anlamllk hatas //... }

Bir Ktphanenin sim Alan ine Alnmas


sim alanlar C++ dilinin standartlatrlmas srecinde dile katlan son aralardan biridir. Standartlar ncesi gelitirilen derleyiciler isim alanlarn desteklemedikleri iin eskiden yazlm bir ok ktphane bir isim alan iinde bildirilmemitir. sim alan iine alnmam ktphanelerin kullanm isim akmas riskini artrr. Bir isim alan iinde yaplmam bildirimlerin, daha sonradan bir isim alan iine alnmak istendiini dnelim. rnein daha nce yazlan cdate.h isimli balk dosyasndaki bildirimlerin CDernek isim alanna katlmak istendiini dnelim: //cdate.h namespace CDernek { class CDate { //... }; //dier bildirimler }

Ancak bu durumda CDate snfn kullanan kodlarn bulunduu, rnein app1.cpp isimli bir dosyada cdate.h isimli balk dosyas yeni biimiyle kaynak dosyaya include nilemci komutuyla eklendiinde artk cdate.h iinde bildirilen isimler dorudan kullanlamaz. cdate.h iinde yaplan her bildirim artk CDernek isim alan eleman durumuna geldiine gre, bu elemanlar ancak nitelenmi isimleriyle kullanlabilir. app1.cpp dosyasnda fazlaca bir deiiklik yapmamak iin bu dosyann bana bir using namespace komutu yerletirilebilir: //app.cpp #include "cdate.h" using namespace CDernek; //...

sim Alan Eismi


sim alan isimleri de global blgede tek olmak zorundadr. Bu yzden isim alan isimleri genellikle uzun seilir. Nitelenmi isim kullanlrken, isim alan elemann niteleyen ismin

162/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

srekli olarak yazlmas istenmeyebilir. Daha ksa isimler kullanabilmek iin, isim alan ismi yerine kullanlabilecek baka -daha ksa-isimler yaratlabilir. Bu isimalan eismi bildirimi ile salanr. sim alan eismi bildirimi (namespace alias) namespace anahtar szc ile balar. Anahtar szc, isim alan isminin yerine geecek szck izler. Daha sonra atama ileci yer alr. Atama ilecinin sa tarafna daha nce tanmlanan bir isim alannn ismi yazlr. Bildirimi her zaman olduu gibi sonlandrc atom tamamlar. namespace CSD = C_ve_Sistem_Programcilari_Dernegi; Bu bildirimden sonra artk C_ve_Sistem_Programcilari_Dernegi isim alan isminin kullanlabileei yerlerde CSD ismi kullanlabilir. Bir isim alan eismi, isel bir isim alan iin de kullanlabilir: namespace CSDSTR = C_ve_Sistem_Programclar_Dernegi::CDStringLib; Biri isim alannn birden fazla eismi olabilir. sim alan eismi, projelerde srm denetiminde de kullanlr. Baarl yazlm projelerinin mr rn piyasaya verildikten sonra da srer. Baz rnlerin srekli yeni srmleri retilir. Bir programn yeni ve eski srmleri sz konusu olduunda, iki srmn de ortak olarak kulland kod bileenleri vardr. Ancak baz kod bileenleri ise her srm iin farkldr. sim alan eisimleri farkl srmler iin dinamik isim alanlarnn yaratlmasnda kullanlabilir: namespace Ver_1_0 { class File { // }; class Usb { // }; } namespace Ver_2_0 { class File { // }; class Usb { // }; } int main() { namespace current = Ver_2_0; using current::File; using current::Usb; File f; Usb u; // return 0; }

Yukardaki kodu inceleyelim: Ayr srmlerde kullanlmak zere Ver_1_0 ve Ver_2_0 isimli iki ayr isim alan tanmlanyor. main ilevi iinde yaplan isim alan eismi bildirimiyle, current isminin Ver_2_0 isim alannn yerine gemesi salanyor. Daha sonra yaplan using bildirimleriyle current isim alannn File ve Usb snf isimlerinin nitelenmeden kullanlmas salanyor. main ilevi iinde yer alan

163/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

namespace current = Ver_1_0; bildiriminin namespace current = Ver_2_0; bildirimi ile deitirilmesiyle, farkl File ve Usb snflarnn kullanlmas salanabilir.

simsiz sim Alan


Her kaynak dosya iinde, isimsiz bir isim alannn bulunduu varsaylr. Bu isim alanna programc isterse ekleme yapabilir: namespace { void func(); int g; class A{ // }; // }

Bu isim alanndaki isimlere nitelenmemi isimlerle, kendi tanmlandklar kaynak dosya iinde dorudan eriilebilir. Yukardaki rnekte isim alan bildiriminin grlebildii bir kaynak kod noktasnda int main() { A a; //.. return 0; }

Yukardaki main ilevi iinde A ismi nitelenmeden (unqualified) kullanlyor. A snf isimsiz isim alan iinde tanmlanmtr. simsiz isim alannn her kaynak dosya iin tek olaca gvence altna alnmtr. Yani byle bir isim alan iinde bildirilen isimler dorudan i balantya (internal linkage) sahiptir. simsiz isim alan ne ie yarar? C dilinde global varlklarn i balantya sahip olmas iin static anahtar szcyle bildirilmeleri gerektiini hatrlayn. Oysa C++ da i balantya sahip yani yalnzca kendi modlnde bilinen global varlklar iin, static anahtar szcnn kullanlmas eskimi (deprecated) olarak kabul edilmitir. nerilen ara, isimsiz isim alan oluturulmas ve i balantya sahip global varlklarn bu isim alan iinde bildirilmesidir. Global dzeyde, static anahtar szc ile ilevler ve deikenler iin kullanlabilir. Ancak isimsiz isim alan iinde programc tarafndan yaratlan trlerin de tanm yaplabilir.

Koenig sim Aramas


Bir ilev arsyla karlaan derleyici, hangi ilevin arldn anlamak iin ilev arsnn yapld noktada grlebilir ilevleri aratrr. int main() { func(object) }

164/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki kod iinde bir using bildirimi ya da using namespace komutu kullanlmadna gre, normal olarak func ilevinin bir isim alan iinde aranmamas gerekir. Standartlarla dile eklenen Koenig isim aramas (Andrew Koenig isminden) bu kural deitirir. leve gnderilen arguman olan object eer bir isim alan iinde bildirilen snfa ilikin bir nesne ise, bu snfn bildirimini kapsayan isim alan iinde de func isimli uygun bir ilevin varl aratrlr. Bu kurala Koenig isim aramas ya da argumana bal isim arama denir. Aadaki kodu derleyerek altrn: #include <iostream> using namespace std; namespace NMSPC { class A { //... }; void foo(A a) { cout << "NMSPC::foo(A)" << endl; } } int main() { NMSPC::A den; foo(den); return 0; }

Koenig isim aramas otomatik olarak uygulanr. Yani Koenig isim aramasn etkin klmak iin bir bildirim ya da komut kullanlmak gerekmez. Bu zellii devre d brakmak da mmkn deildir. Koenig isim aramasnn yaplmas, baz durumlarda ift anlamllk hatasnn olumasna da yol aabilir: namespace NMSPC { class A { //... }; void foo(A); } void foo(NMSPC::A); int main() { NMSPC::A a; foo(a); //ift anlamllk hatas return 0; }

Yukardaki kod parasnda main ilevi iinde arlan foo ilevi, hangi ilevdir? NMSPC isim alan iinde bildirilen mi, global isim alan iinde bildirilen mi? Koenig isim aramas sz konusu olmasayd, phesiz arlan ilev global isim alannda bildirilen foo ilevi olurdu. Ancak Koenig isim aramas NMSPC isim alan iindeki foo ilevini de grlebilir kld iin, derleme zamannda ift anlamllk hatas oluur.

sim Alanlarnda Yaplan Arkadalk Bildirimleri 165/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Arkadalk bildirimi ayn zamanda arkadalk verilen ilev iin bir prototip bildirimidir. Bir isim alan iinde bir ileve arkadalk verildii zaman arkadalk bildirimi isim alan iinde yaplrsa, ilev de isim alannda bildirilmi olur. Bylece arkadalk verilen ilev de isim alannn bir eleman olur. namespace CDernek{ class Sample { int x; friend void foo(); }; //... } void foo() { CDernek::Sample sam; sam.x = 10; //Geersiz }

Yukarda Cdernek isim alan iinde tanmlanan Sample isimli snf iinde yaplan friend void foo(); bildirimi ayn zamanda bu ilevin bildirimi olduundan arkadalk verilen ilev void Cdernek::foo() ilevidir. Eer Cdernek isim alan iinde global isim alannda bildirilmi bir ileve arkadalk verilmek istenseydi arkadalk bildirimi friend void ::foo(); biiminde yaplmal ve isim alan tanmndan nce global foo ilevinin bildirimi yaplmalyd. Global isim alannda tanmlanan foo isimli ilev iinde Sample snfnn private elemanna eriilmeye alldn gryorsunuz. CDernek::Sample snf arkadal global func ilevine vermemitir. Bu nedenle sam.x eriimi geerli deildir.

sim Alanlar Ne Zaman Kullanlmal


sim akmas olaslnn byk projeler sz konusu olduunda daha da artaca aktr. Yzlerce kaynak dosyann olduu bir projede hem isim akmas riski hem de isim akmasnn neden olaca ek maliyet yksektir. Byk projelerde isim alanlarnn kullanlmas kanlmazdr. Ancak tek bir programcnn gelitirdii ok fazla dosyaya sahip olmayan projelerde isim alanlarnn kullanlmas fazla bir getiri salamaz. Ne de olsa isim akmas olmas durumunda programc kolayca baz isimleri deitirerek akma durumunu ortadan kaldrabilir.

sim Alanlarnn Maliyeti


sim alan kullanm programn alma zaman sz konusu olduunda, alan programn hz ya da kulland bellek asndan ek bir yk getirmez. sim alanlarna ilikin kontroller programn derleme zamannda yaplr.

166/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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

167/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

dosyas deil de yalnzca snfn arayz yani balk dosyas varsa byle bir deiiklik yapmak mmkn olmaz. Bir baka yntem olarak, bileik nesne oluturma yolu seilebilir. rnein nce aadaki gibi bir A snf tanmlanr: class A { //Var olan ilevler ve elemanlar }; Sonra B snf iinde A snfn bir eleman olarak kullanlr: class B { private: A a; public: //Yeni eklenen ilevler ve elemanlar };

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.

Tretme leminin Genel Biimi


Bir snf baka bir snftan tretilir. Kendisinden tretme yaplan snfa taban snf (base class), tretilmi olan snfa da tremi snf (derived class) denir. Tretme ileminin genel biimi yledir: <class> <struct> <tremi snf ismi :> [tretme biimi] <public> <protected> <private> <taban snf ismi>

{ snf elemanlarnn bildirimi }; 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. class A { // }; class B : public A { // };

168/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Burada B tremi snf A ise taban snftr. Tretme biimi olarak public tretmesi seilmitir. Aklamay ve anlatm kolaylatrmak amacyla snflarn tretilmesi ekilsel olarak da gsterilebilir. Byle bir ekilde snflar daire, elips ya da dikdrtgenlerle gsterilir. Taban snf tremi snfn daha yukarsnda gsterilir ve tremi snftan taban snfa bir ok izilir. Okun yanna istee bal olarak tretme biimi yazlabilir. rnein B snf A snfndan tretilmise, bu durumu ekilsel olarak aadaki gibi gsterilebilir:

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.

Tremi Snf Nesnelerinin Bellekte Yerleimi


Bir tremi snf nesnesi kendi iinde taban snfn elemanlarn da ierir. rnein: class A { private: int a, b; public: // }; class B : public A { private: int c, d; public: // };

gibi bir tretme ileminden sonra B snf trnden bir nesne tanmlanrsa, bu nesne hem A snfnn hem de B snfnn elemanlarn ierir. Yani, sizeof(B), drt tane sizeof(int) toplam kadardr (DOS altnda 8, UNIX ve Win32 sistemlerinde 16). Tremi snf elemanlar ile taban snf elemanlar blok olarak bitiik bir biimde yerletirilir. Ancak taban snf elemanlarnn m yoksa tremi snf elemanlarnn m daha dk adrese yerletirilecei standart olarak belirlenmemitir. Derleyiciden derleyiciye deiebilir. Taban snf ile tremi snf elemanlarnn tremi snf iindeki yerleimi standart bir biimde belirlenmemi olsa da, popler derleyicilerin tmnde taban snf elemanlar daha

169/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

dk saysal adreste olacak biimde yerleim kullanlr. Yerleim biiminin standart bir biimde belirlenmemi olmas herhangi bir tanabilirlik problemine yol amaz. Tremi snf nesnesinin taban snfn elemanlarn da ierdiini grmek iin aadaki rnei yazarak altrn: class Base { public: int b1, b2; }; class Der : public Base { public: int d1, d2; }; #include <iostream> using namespace std; int main() { Der der_object; cout << "sizeof(der_object) = " << sizeof(der_object) return 0; }

<< endl;

Yukardaki programda Der snf trnden der_object isimli bir nesne tanmlanyor. Bu nesne Base taban snfnn elemanlarn da ierir. Nesnenin uzunluu DOS altnda 8 byte, Win32 ve UNIX sistemlerinde ise 16 byte olur. Yani 2 * sizeof(int) + 2 * sizeof(int) .

Tremi Snflarda Eriim Kural


Tremi snf yalnzca kendi elemanlarna ve ilevlerine deil, ayn zamanda taban snfn elemanlarna ve ilevlerine de eriebilir. ncelikle vereceimiz rnekler iin bir snf tanmlamas yapalm: #include <iostream> class Base { private: int b; protected: void base_pro_func(); public: void set_base(int); void display_base()const; }; class Der : public Base { private: int d; protected: void der_pro_func(); public: void set_der(int); void display_der()const; }; using namespace std; void Base::set_base(int x) { b = x;

170/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin } void Base::base_pro_func() { cout << "base_pro_func()" << endl; } void Base::display_base() const { cout << b << endl; } void Der::set_der(int x) { d = x; } void Der::der_pro_func() { cout <<"der_pro_func()" << endl; } void Der::display_der() const { cout << d << endl; }

public Tretmesinden kan Sonular


1. Snf bilinirlik alan dnda, tremi snf nesnesi ya da gstericisi yoluyla dardan nokta ya da ok ilecini kullanarak taban snfn public blmne eriilebilir. Ancak taban snfn protected ve private blmlerine eriilemez. Aadaki rnei inceleyelim: int main() { Der d; x.set_der(20); // Geerli. x.set_base(10); // Geerli. //x.der_pro_func(); Geersiz. Tremi snfn protected blm. //x.base_pro_func(); Geersiz. Taban snfn protected blmn. //x.d = 20; Geersiz. Tremi snfn private blm. //x.b = 10; Geersiz. Taban snfn private blm. return 0; }

2. Tremi snf bilinirlik alan iinde taban snfn public ve protected blmlerine dorudan eriilebilir. Ancak private blmne dorudan eriilemez. Yani tremi snf ye ilevleri, taban snfn public ve protected blmlerindeki deikenleri dorudan kullanabilir, taban snfn ye ilevlerini de dorudan arabilir. Taban snfn private blm tam olarak korunmutur. Bu blme herhangi bir biimde eriilemez. rnein Der tremi snfnn der_pro_func ye ilevi iinde taban snfn protected ve public blmlerine eriilebilir.

171/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void Der::der_pro_func() { display_base(); base_func(); d = 100; //b = 200; Geersiz! }

Tremi Snf ye levlerinin Taban Snf ye levlerini armas


Bir tremi snf ye ilevi, taban snfn public ve protected blmmne erierek taban snfn ye ilevlerini arabilir. Tremi snf nesnesiyle taban snf ye ilevi arldnda, ya da tremi snf ye ilevi iinde taban snfn ye ilevi dorudan arldnda, taban snf ye ilevine this gstericisi olarak tremi snf nesnesi iinde yer alan taban snf alt nesnesinin adresi geirilir. Yani taban snfn ye ilevine aslnda fiziksel olarak bir taban snf nesnesinin adresi geirilmektedir. Aadaki rnei inceleyin: class Base { public: int b; void base_func(); }; #include <iostream> using namespace std; void Base::base_func() { cout << "base_func() icinde this adresi = " << this << endl; } class Der:public Base { public: int d; void der_func(); }; void Der::der_func() { cout << "der_func() icinde this adresi = " << this << endl; } int main() { Der der_object; cout << "&der_object = " << &der_object << endl; cout << "&der_object.d = " << &der_object.d << endl; cout << "&der_object.b = " << &der_object.b << endl; der_object.der_func(); der_object.base_func(); return 0; }

rnek bir ekran kts: &der_object = 0012FF78 &der_object.d = 0012FF7C &der_object.b = 0012FF78 der_func() icinde this adresi = 0012FF78 base_func() icinde this adresi = 0012FF78

172/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Taban Snf Kurucu ve Sonlandrc levlerinin arlmas


Tremi snf nesnesi kendi iinde taban snfa ilikin bir alt nesneyi de ierir. Taban snf elemanlarnn taban snfn private blmnde olduunu dnelim. Bu durumda tremi snfn ye ilevleri oradaki veri elemanlara eriip onlara ilkdeer verebilir mi? Tremi snfn kurucu ilevi iinde yalnzca tremi snf elemanlarna ilkdeer verilebilir. Peki tremi snfn private blmde yer alan taban snf elemanlarna nasl ilkdeer verilebilir? Tremi snf nesnesine ait taban snf elemanlarna ilkdeer verilmesi, tremi snfn kurucu ilevinin ana blounun banda taban snfn varsaylan kurucu ilevine yaplan gizli aryla olur. Yani tremi snf kurucu ilevi kendi iinde taban snf varsaylan kurucu ilevini arr. Aadaki program derleyerek altrn: class Base { int b; public: Base(); Base(int); void display_base() const; }; #include <iostream> using namespace std; Base::Base() { cout << "Base::Base()" << endl; b = 0; } Base::Base(int val) { cout << "Base::Base(int)" << endl; b = val; } void Base::display_base() const { cout << "b = " << b << endl; } class Der: public Base { int d; public: Der(); }; Der::Der() { cout << "Der::Der()" << endl; d = 0; } int main() { Der der_object; der_object.display_base(); return 0; }

173/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

main ilevi iinde tremi snf olan Der snf trnden der_object simli bir nesne tanmlanyor: Der der_object; Bu nesne iin Der snfnn varsaylan kurucu ilevinin arlacan biliyorsunuz. Der snfnn varsaylan kurucu ilevinin ana blounun hemen banda da, derleyici tarafndan yerletirilen gizli bir ar kodu ile taban snf olan Base snfnn varsaylan kurucu ilevi arlr. Der::Der() { //Derleyici tarafndan buraya Base::Base() ar kodu ekleniyor! cout << "Der::Der()" << endl; d = 0; }

Tremi snfn btn kurucu ilevlerinde bu gizli ar ilemi yaplr. Taban snfn varsaylan kurucu ilevi yerine, taban snfn parametreli baka bir kurucu ilevin arlmas istenebilir. Bu durumda tremi snfn kurucu ilevi MIL (Member Initialization List) szdizimi ile tanmlanmaldr. MIL szdiziminde taban snfn hangi kurucu ilevinin arlaca aadaki gibi belirlenir. B::B(parametre deikenleri) : taban snf ismi(parametre deikenleri) { //... } Grld gibi tanmlama, bir snfn baka bir snf trnden elemanlara sahip olmas durumunda olduu gibi MIL szdizimi kullanlarak yaplr. Ancak bu kez ':' atomunu elemannn ismi deil taban snf ismi izler. Taban snf isminden sonra ayra iinde bir parametre deikeni listesi yazlabilir. Bu durumda derleyici parametre listesini inceleyerek parametre listesine uygun olan taban snf kurucu ilevini arr. rnein yukardaki tretme ileminde Der snfnn kurucu ilevi yle yazlm olabilirdi: Der ::Der() : Base(1) { cout << Der ::Der() << endl ; } Bu durumda Base taban snfnn int trden parametre deikeni olan kurucu ilevi arlr. Tremi snf kurucu ilevine geilen argmanlar taban snf kurucu ilevine aktarlabilir. rnein tretilen snf olan Der snfnn Der(int, int); biiminde bir kurucu ilevi olsun. Der::Der(int x, int y) : Base(x), d(y) { } Burada Der snfnn kurucu ilevinin x parametre deikeni, taban snfn kurucu ilevi iin kullanlyor. Bylece rnein : Der der_object(10, 20);

174/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

biiminde bir nesne tanmlamas ile 10 deeri taban snfn kurucu ilevi iin 20 deeri ise tremi snfn kurucu ilevi iin kullanlr. Tremi snfn kurucu ilevinin tanmlamasnda eer MIL szdizimi kullanlmamsa, taban snf iin varsaylan kurucu ilevi arlr. rnein Y snf X snfndan tretilmi olsun. Y::Y() { //... } Bu durumda kurucu ilevin ana blounun banda X snf iin varsaylan kurucu ilev arlr. Yani bu biimde bir tanmlama aadaki gibi tanmlamayla edeer kabul edilebilir: Y::Y() : X() { //... } Eer taban snf olan X snfnn herhangi bir kurucu ilevi varsa, ama varsaylan kurucu ilevi yoksa bu durum derleme aamasnda hata oluturur.

Kurucu levin Snfn private Blmnde Olmas


Taban snf kurucu ilevlerinin tremi snfn kurucu ilevi iinden bu biimde otomatik olarak arlabilmesi iin, taban snf kurucu ilevinin kendi snfnn protected ya da public blmnde bildirilmi olmas gerekir. Bir snfn kurucu ilevi snfn protected blmne koyulursa global bir ilev iinde o snf trnden nesne tanmlanamaz. nk snfn protected kurucu ilevine eriilemez. Ancak o snftan tretme yapldnda tremi snf kurucu ilevi taban snfn protected kurucu ilevini arabilir. class Base { protected: Base(); public: //... }; class Der: public Base { //... public: Der(); }; int main() { Der der_object; Base base_object; return 0; }

//Geerli //Geersiz!

Taban Snfn Sonlandrc levinin arlmas


Bir snf nesnesinin mr sona erdiinde o snf nesnesi iin sonlandrc ilevin arldn hatrlyorsunuz. Tremi snf trnden bir nesne kendi iinde taban snf alt nesnesini ierdiine gre, tremi snf nesnesinin mr tamamlandnda, ierilen taban snf nesnesinin de mr tamamlanm olur. Peki bu durumda, ierilen taban snf nesnesinin sonlandrc ilevi nasl arlr?

175/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

erilen taban snf nesnesi iin sonlandrc ilevin arlmas derleyicinin ekledii bir kod ile olur. Derleyici tremi snf sonlandrc ilevinin sonuna taban snfn sonlandrc ilevine yaplan ar kodunu ekler. Aadaki rnei derleyerek altrn. #include <iostream> class Base { public: Base() {std::cout << "Base::Base()" << std::endl;} ~Base() {std::cout << "Base::~Base()" << std::endl;} }; class Der: public Base { public: Der() {std::cout << "Der::Der()" << std::endl;} ~Der(); }; Der::~Der() { std::cout << "Der::~Der()" << std::endl; } void func() { Der der_object; } int main() { func(); return 0; }

Programn ekran kts Base::Base() Der::Der() Der::~Der() Base::~Base() eklinde olur. Der::~Der() { cout << "Der::~Der()" << endl; //Derleyici buraya Base ::~Base arsn ekliyor! } Bu durumun anlam udur. Bir tremi snf nesnesi yok edilirken nce tremi snfn ekledii ksm yok edilir. Daha sonra taban snf ksm yok edilir. Kurucu ve sonlandrc ilevlere ilikin daha nce aklanan kural anmsayalm. Kurucu ilevler ile sonlandrc ilevler ters srada arlr. Yani kurucu ilevi en nce arlan nesnenin sonlandrc ilevi en sonra arlr. Taban snfn kurucu ve sonlandrc ilevlerinin arlmas durumunda da bu kural geerlidir.

Tretme le Bileik Nesne Oluturmann Karlatrlmas


Mantksal ve ilevsel karlklar farkl olsa da, tretme ile bileik nesne oluturmann

176/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ortak noktalar vardr. Her iki arata da bir snf nesnesi bir baka snf nesnesini fiziksel olarak ierir. class Member { //... }; class Owner { Member m; //... };

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: class Base{ //... }; class Der: public Base { //... };

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.

Snfn protected Blm


Snfn protected blm yalnzca tretme yapldnda anlam kazanan bir blmdr. Bu blm tpk private blm gibi darya kapaldr. Ancak bir tretme yapldnda tremi snfn ye ilevleri tarafndan taban snfn protected blmne eriilebilir. Yani private blm ile protected blm arasndaki fark udur. Tremi snfn ilevleri taban snfn private blmne eriemezken taban snfn protected blmne eriebilir. Bu durumda bir snfn - public blm tm kodlarn her zaman eriebilecei bir blmdr.

177/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

- private blm yalnzca snfn kendi ye ilevlerinin ve arkada ilevlerin eriebilecei bir blmdr. - protected blm snfn kendi ye ilevlerinin ve arkada ilevlerinin yan sra tremi snf ye ilevlerinin de eriebilecei bir blmdr. Tremi snf ye ilevlerinin eriebilmesi onu private blmden ayrr. Peki hangi elemanlar ve ye ilevler snfn protected blmne yerletirilmelidir? te buna karar verebilmek iin nce sorulmas gereken soru udur: "Tasarlanan snftan tretme yaplacan dnlyor mu?". Eer dnlyorsa treten kod paras hangi elemanlarndan ve ilevlerinden faydalanabilir? te tretmeyi yapan kod parasnn dorudan kullanabilecei, onun ilerini kolaylatraca dnlen ye ilevler ve veri elemanlar snfn protected blmne yerletirilmelidir. Bylelikle bu elemanlar ve ye ilevler darnn algsndan uzaklatrlarak yalnzca tretme ilemini yapacak kodlarn kullanmna sunulur. rnein tarih ilemlerini yapan Date snfna bakalm. Bu snftan bir snf tretip bu snfa yeni ilevler eklemek istenirse day, month, year elemanlar dorudan kullanllmas iin bu elemanlar snfn protected blmne yerletirilebilir. Bir snfn protected arayz kendisinden tretme yapan snflar ilgilendiren bir arayzdr. class Date { public: Date(); Date(int day, int month, int year); // protected: int day, month year; }; class SpecialDate : public Date{ // Yeni ye ilevler ve elemanlar };

Bir snfn protected blm koruma bakmndan private blmden gevek public blmden sk olduuna dikkat edin. imdi yerleim bakmndan bir zet yapalm.

Blm public protected private

Neler Yerletirilmeli? Dardan tm kodlarn arabilecei ye ilevler, tm kodlarn kullanabilecei elemanlar ve bildirimler Dardan eriilmesi istenmeyen, yalnzca snftan tretme yapacak kodlarn erimesi istenilen ye ilevler, elemanlar ve bildirimler Yalnzca snfn kendi kodlarnn erimesi istenilen ye ilevler, elemanlar ve bildirimler.

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.

178/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Tretme ve Bileik Nesnelerin Birlikte Kullanlmas


Baka bir snftan tretilen bir snf, yine bir baka snf trnden elemana sahip olabilir. Bu durumda kurucu ilevler hangi sraya gre arlr? Tremi bir snfn baka snf trnden bir nesneye sahip olmas durumunda, nce taban snfn kurucu ilevi arlr. Taban snfn (ya da snflarn) kurucu ilevleri arldktan sonra bu kez eleman olarak ierilen snf nesneleri iin kurucu ilevleri arlr. Aadaki program derleyerek altrn: #include <iostream> class Member { public: Member() {std::cout << "Member::Member()" << std::endl;} ~Member() {std::cout << "Member::~Member()" << std::endl;} }; class Base { public: Base() {std::cout << "Base::Base()" << std::endl;} ~Base() {std::cout << "Base::~Base()" << std::endl;} }; class DerComp: public Base { Member mem; public: DerComp() {std::cout << "DerComp::DerComp()" << std::endl;} ~DerComp() {std::cout << "DerComp::~DerComp()" << std::endl;} }; void func() { DerComp d; } int main() { func(); return 0; }

Programn ekran kts : Base::Base() Member::Member() DerComp::DerComp() DerComp::~DerComp() Member::~Member() Base::~Base()

Tremi Snflarda Bilinirlik Alan


Bilinirlik alan (scope) bir ismin derleyici tarafndan tannabildii program araldr. C ve C++ da bir blok iinde ayn isimli birden fazla deiken biliniyorsa o blok iinde ayn isimli deiken kullanldnda dar bilinirlik alanna sahip olana eriilir. Yani dar bilinirlik alanndaki isim kendisini kapsayan daha geni bilinirlik alannda bulunan ismi maskeler. Onun grlmesini engeller. Ayn kural C++ da tretme ilemlerini kapsayacak biimde geerlidir. nce u soruyu soralm: Tretme durumunda taban snf tremi snf tarafndan eriilebildiine gre taban snftaki bir isim mi daha dar bilinirlik alanna sahiptir, tremi snftaki bir isim mi? Tremi snftaki isim deil mi? nk taban

179/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

snftaki isimler hem kendi snf iinde hem de tremi snf iinde kullanldna gre daha geni bir bilinirlik alanna sahiptir. Bu durumda hem taban snf iinde hem de tremi snf iinde ayn isimli bir deiken varsa tremi snf ye ilevleri iinde o deiken kullanldnda, dar bilinirlik alanna sahip olan yani tremi snfta tanmlanm olan anlalr. Aadaki kodu inceleyelim: class Base { protected: int b; public: Base():b(0){} void display()const; }; #include <iostream> void Base::display()const { std::cout << "b = " << b << std::endl; } class Der: public Base { int b; public: Der():b(1){} void set(int); void display() const; void func() const; }; void Der::set(int val) { b = val; //Der::b = val; } void Der::display()const { std::cout << "b = " << b << std::endl; } void Der::func()const { display(); }

Burada Der snfnn set ye ilevi iinde kullanlan b ismi, daha dar bilinirlik alanna sahip olan Der snfnn b isimli elemanna ilikindir. Der snfnn b isimli eleman Base snfnn b isimli elemann maskeler yani grnmesini engeller. Tabii eer Der snfnn b isimli bir eleman olmasayd burada kullanlan b, Base snfna ilikin olurdu. Ayrca Base snfnn display ye ilevi iinde kullanlan b eleman Base snfna ilikin olandr. Zaten taban snf tremi snfa eriemediine gre hibir biimde bunun Base snfna ilikin olmas sz konusu deildir. Tremi snf iinde ayn isimli taban snf elemanlarna ve ilevlerine znrlk ileci ile eriilebilir. rnein bu kez Der snfnn iki parametreli set isimli bir ye ilevi daha olduunu dnelim:

180/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void Der::set(int x, int y) { b = x; Base::b = y; } levde dorudan kullanlan b, bilinirlik alan kurallarna gre Der snfna ilikin olandr. Ancak znrlk ileci ile, yani Base::b ifadesi ile kullanlan b, Base snfna ilikin olandr. ki terimli znrlk ilecini taban ve tremi snflarda ayn isimli elemanlar ya da ilevler varken taban snfn elemanlarna erimek amacyla bu biimde kullanlabilir. Bilinirlik alan kurallar ilev isimleri iin de geerlidir. Tremi ve taban snfta ayn isimli ilevler varsa dar bilinirlik alan kuralna gre tremi snftaki anlalr. rnein Der snfnn func ilevi iinde arlan display ilevi Der snfna ilkin olandr. znrlk ileci, ayn amala ye ilevler iin de kullanlabilir. rnein tremi snf olan Der snfnn func isimli ye ilevi iinde bu kez taban snfn display isimli ilevi arlmak istenseydi bu aadaki gibi yaplabilirdi: void Der::func() { Base::display(); } Dar bilinirlik alan kural dardan snf elemanlarna eriirken de geerlidir. Yukarda verdiimiz tretme ilemi iin aadaki kod yazlm olsun: int main() { Der der_object, *der_ptr; der_object.display(); der_object.Base::display(); der_ptr = &der_object; der_ptr->display(); der_ptr->Base::display(); Base base_object; base_object.display(); return 0; } //1 //2 //3 //4 //5

Yukardaki main ilevinde yorum satrlaryla iaretlenen ilev arlarna bakalm: //1 //2 //3 //4 //5 Der snfnn display ilevi arlr. Base snfnn display ilevi arlr. Der snfnn display ilevi arlr. Base snfnn display ilevi arlr. Base snfnn display ilevi arlr. Taban snf tremi snfa eriemez!

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:

181/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Base{ public: int x; }; class Der: public Base { int x; }; void func() { Der der_object; der_object.x = 0; }

//Geersiz!

C++'a yeni balayanlar func ilevi iindeki der_object.x = 0; atamasnn geerli olmas gerektiini dnrler. Hem tremi snfta hem de taban snfta x isimli bir eleman olduuna gre, nce tremi snfta bulunan x isimli elemana eriilmeye allr. Tremi snfta bulunan x snfn private blmnde bulunduu iin bu eriim gereklemez. Ancak taban snfn public blmnde bulunan xe eriilir. Ancak durum byle deildir. nce isim aramas yaplr. Tremi snf olan Der snfnn bilinirlik alannda x ismi bulunduunda isim arama sona erdirilir. Aranan isim bulunmu ve x isminin tremi snf bilinirlik alanndaki x olduu anlalmtr. Artk ne nedenle olursa olsun yeniden bir isim aramas yaplmaz. imdi bu x'e eriimin geerli olup olmad sorgulanr. Tremi snfn x isimli eleman tremi snfn private blmnde olduu iin eriim geersizdir. lev yklemesi (function overloading) ayn bilinirlik alanndaki ayn isimli ilevler iin geerli bir aratr. Farkl bilinirlik alanndaki ayn isimli ilevler yklenemez. Yine, dar bilinirlik alanndaki ismin daha geni bilinirlik alanndaki ismi gizlemesi sz konusudur. Bu kez aadaki kod parasn inceleyin: class Base{ public: void foo(); void foo(int, int); }; class Der:public Base { public: void foo(int); void foo(int, int); }; int main() { Der der_object; //der_object.foo(); der_object.foo(10); der_object.foo(5, 8); der_object.Base::foo(); der_object.Base::foo(5, 8); return 0; }

//Geersiz //void Der::foo(int) //void Der::foo(int, int) //void Base::foo() //void Base::foo(int, int)

main ilevi iinde yaplan ilev arlarn teker teker inceleyelim:

182/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin der_object.foo(); ars geersizdir. nk tremi snf bilinirlik alannda foo ismi bulunduunda isim arama sona erer. Yalnzca Der snf iinde bildirilmi olan foo ilevleri, arlan ilev iin aday olur. Der snf bilinirlik alan iinde parametresi olmayan foo isimli bir ye ilev bildirilmediine gre, ar derleme zamannda hata olarak deerlendirilir. phesiz tremi snf iinde foo isimli hi bir ilev tanmlanmam olsayd bu kez taban snfn parametre deikeni olmayan foo isimli ilevi arlrd: der_object.foo(10); ars ile tremi snfn int parametreli foo ilevi arlr. Der snfnn bu ar iin iki aday ilevi vardr. Ancak seilecek ilev int parametreli olandr: der_object.Base::foo(); //void Base::foo() der_object.Base::foo(5, 8); //void Base::foo(int, int) arlarnda ilev ismi snf ismi ile nitelendirildiinden isim aramas tremi snfn bilinirlik alanndan balamaz. Arama dorudan Base snfnn bilinirlik alanndan balar. Bu durumda Base snfnn ayn ismli iki ilev iinden ilev ykleme kurallarna gre uygun olan ilevler arlr. der_object.foo(5, 8); ile bu kez arlan Der snfnn iki parametreli foo ilevi iken der_object.foo(10); ile Der snfnn tek parametreli foo ilevi arlr. Benzer durum tremi snfn ye ilevi iinde taban snfn bir elemanna eriirken ya da taban snfn ye ilevi arlrken de geerlidir. Aadaki program inceleyin: class Base{ int b; public: void foo(); void func(int); }; class Der:public Base { int b; public: void foo(); void func(); }; void Der::foo() { b = 10; //Der::b = 10; //Base::b = 50; Taban snfn private blmne eriilemez. Geersiz foo(); //lev kendi kendini arr. Base::foo(); //Base::foo() arlr //func(10); //Geersiz! func(); //Der::func() arlr. Base::func(10);//Base::func(int) arlr. }

Tremi Snf inde Yaplan using Bildirimi

183/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Tremi ve taban snflarn farkl bilinirlik alan oluturduklarn biliyorsunuz. Tremi snf bilinirlik alan iinde bildirilen bir isim, taban snf iinde de kullanlmsa, taban snftaki isim maskelenir. Maskelenme istenmiyorsa tremi snf iinde using bildirimi yaplabilir. Snf ii using bildirimiyle taban snflara ilikin maskelenen isimler grnr klnr. Bylece taban snf ve tremi snf ilevleri farkl bilinirlik alannda olsalar bile yklenebilir. Aadaki kodu inceleyin: #include <iostream> class Base { public: void foo(){std::cout << "Base::foo()" << std::endl;} }; class Der: public Base { public: using Base::foo; void foo(int){std::cout << "Der::foo(int)" << std::endl;} }; int main() { Der der_object; der_object.foo(); der_object.foo(25); return 0; } Der snfnn tanm iinde yaplan using bildirimiyle Base snfnn foo isimli ilevi Der snf iinde grlebilir hale getiriliyor. Bylece der_object.foo(); ars sz konusu olduunda void Base::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:

184/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> class Base { public: void foo(){std::cout << "Base::foo()" << std::endl;} }; class Der: public Base { public: using Base::foo; void foo(){std::cout << "Der::foo()" << std::endl;} }; int main() { Der der_object; der_object.foo(); return 0; }

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.

Snflarda sim Arama


sim arama (name lookup) bilinirlik alannn derleyici bak asyla ele alnma biimidir. Bir deiken kullanldnda derleyici o deikeni srasyla dardan genie doru eitli bilinirlik alanlarnda arar. Eer ismi bir bilinirlik alannda bulursa aramay keser. Deikeninin o bilinirlik alanndaki isim olduuna karar verir. Derleyici eer arad ismi hibir bilinirlik alannda bulamazsa bu durum hata olarak deerlendirilir. rnein Cde derleyici bir blok iinde bir deiken ile karlatnda srasyla onu u bloklarda arar. 1. Deikenin kullanld blok iinde 2. lev iinde onu kapsayan bloklarda 3. Global isim alannda imdi ayn isimli hem bir yerel hem de global deiken tanmlanm olduunu dnelim. Derleyici onu yerel blokta bulduu iin arama ilemini srdrmez. sim arama srasnda aranan isim bulunduu zaman, isim ister eriilir olsun isterse olmasn ilem sonlandrlr. Eriim geerlilii yukarda da belirtildii gibi isim aramas bitirildikten sonra yaplr. Grdnz gibi aslnda isim arama ilemi dar bilinirlik alan kuralnn derleyici bak asyla yorumlanm biimidir. C++da snflar sz konusu olduunda bilinirlik alanna ilkin karmak durumlar sz konusu olabildii iin anlatmda isim arama kavramndan da faydalanlr. Bir isim ya nitelenmemi olarak (unqualified) ya da znrlk ileci ile nitelenmi olarak (qualified) kullanlr. Snflarda isimlerin aranmasn nitelenmemi isimler asndan inceleyeceiz.

Snf Bildirimi indeki simlerin Aranmas


Bir snf bildirimi iinde bir eleman, ye ilev ismi, typedef ismi, enum deimezleri gibi isimlerin aranma srasn belirler. rnein: class A { public: // void putstr(PCSTR); // };

185/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Burada putstr ye ilevin parametre deikeninin tr olarak kullanlan PCSTR ismi snf bildirimi iindedir. Bu isim nerede aranr? Snf bildirimi iinde kullanlan bir isim srasyla u bilinirlik alanlarnda aranr: 1. sim kendi snf bildiriminin bandan kullanm yerine kadar olan blgede aranr. Aadaki rnei inceleyin: class A { public: Days get_day(Str); private: typedef char *Str; enum Days {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; public: Str func(Days); //... };

Yukardaki snf tanmnda get_day ye ilevinin bildiriminde Days ve Str isimlerinin kullanlmas geersizdir. nk derleyici bu noktaya geldiinde bu isimlerle henz karlamamtr. Ancak func isimli ye ilevin geri dn deerinin tr olarak Str ismi kullanlabilir. nk bu trn bildirimi snf iinde daha nce yaplmtr. 2. sim snfn taban snf bildirimlerinin her yerinde aranr. Bir dizi tretme sz konusu olabilir. Bu durumda ayn zellik tm taban snflar iin geerli olur. rnein aada tremi snf iindeki SIZE isminin kullanlmas geerlidir. class A { protected: enum {SIZE = 50 }; }; class B : public A { private: int a[SIZE]; };

Tabii burada SIZE taban snfn public ya da protected blmnde bildirilmi olmasayd, kullanm bilinirlik alan bakmndan geerli fakat eriim bakmndan geersiz olurdu. 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.

186/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class A { public: typedef char *Str; class B { private: Str pStr; }; }; 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: class A { public: enum { SIZE = 50 }; }; class B : public A { class C { private: int x[SIZE]; // geerli eriim // ... }; };

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.

namespace X { enum {SIZE = 50 }; class B { class C { private: int x[SIZE]; // ... }; }; }

// geerli eriim

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.

Snfn ye levi inde Kullanlan simlerin Aranmas


Snfn ye ilevi iindeki isimlerin aranma sras tanmlamann snf iinde (yani inline olarak) yaplp yaplmadna gre deiiklik gstermez. ye ilev ister snfn iinde tanmlanm olsun isterse darda tanmlanm olsun arama ilemi aada aklanan srada yaplr: 1. sim, ye ilev kendi yerel bloklar ve parametre bildirim ayralar iinde aranr. 2. sim ye ilevin ilikin olduu snf bildiriminin her yerinde aranr. rnein:

187/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

class A { public: void set(int x) { a = x; } int get() const { return a; } private: int a; };

Burada set ve get ye ilevlerinin iinde a ismi daha sonra bildirildii halde kullanlyor. Snfn her yeri arand iin kullanm geerlidir. Grld gibi arama ileminde ye ilevin snf iinde tanmlanp tanmlanmadnn bir nemi yoktur. 3. sim ye ilevin ilikin olduu snfn taban snf bildirimlerinin her yerinde aranr. Arama ilemi ilk taban snftan balanarak yukarya doru srasyla yaplr. Ayrca, eriim geerlilii iin ismin taban snfn public ya da protected blmlerinde bulunmas gerekir. rnein: class A { public: int x; }; class B : public A { public: int x; }; class C : public B { public: void func(); }; void C::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. 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:

188/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class A { public: class B { public: void func() { int a[SIZE]; for (int i = 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: class A { public: typedef char *Str; }; class B : public A { public: class C { public: void func() {Str pStr = "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: namespace X { typedef char *Str; class A { public: class C { public: void func() { Str pStr = "Deneme"; // ... } }; }; }

189/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Burada Str isminin kullanm geerlidir. 7. sim, snfn ya da kapsayan snfn iinde bulunduu isim alann kapsayan isim alannda ve sonra da global isim alannda (global namespace) aranr. rnein aadaki isimlerin hepsi doru kullanlmtr: enum {SIZE = 10 }; namespace X { typedef int *Pint; namespace Y { typedef char *Str; class A { void func() { Str pStr = "Deneme"; Pint pInt = 0; int a[SIZE]; // ... } }; } }

Tretme likisinde Arkadalk Bildirimleri


Arkadalk tretme yoluyla devir alnmaz. Taban snfn arkada tremi snfn da arkada deildir. Aadaki rnei inceleyin: class Base { int b; friend void func(); public: Base(int val):b(val){} }; class Der:public Base { int d; public: Der(int val1, int val2):Base(val1), d(val2){} }; void func() { Der der_object(10, 20); //der_object.d = 34; der_object.Base::b = 13; }

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

Tremi Snf Nesne Adresinin Taban Snf Gstericisine Atanabilmesi


C++ dilinde kat bir tr denetiminin esas alndn biliyorsunuz. Bir gstericiye ancak ayn trden bir adres atanabilir. Bir referans da ancak ayn trden bir nesne ile ilkdeerini alabilir. public tretmesi bu kuraln bir istisnasdr. public tretmesiyle amalanan "is a" ilikisini modellemektir. Tretilmi bir snf trnden nesne ayn zamanda taban snf trnden bir nesne olduuna gre, taban snf trnden nesne gereken her yerde tremi snf trnden bir nesne kullanlabilir.

190/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

1. Tremi snf nesnesi taban snf nesnesine dorudan atanabilir. 2. Tremi snf trnden bir nesnenin adresi taban snf trnden bir gsterici deikene atanabilir. 3. Taban snf trnden bir referans tremi snf trnden bir nesne ile ilkdeerini alabilir. Aadaki kod parasn inceleyelim: class Base { public: //... }; class Der:public Base { public: //... }; int main() { Base base_object; Der der_object; Base &base_ref = der_object; Base *base_ptr = &base_object; base_object = der_object; return 0; }

"is a" ilikisinin anlam dilin kurallar ve aralar tarafndan da desteklenir. Tremi snf nesnesinin taban snf nesnesi gereken yerde kullanlabilmesinin anlam udur. Bir snf hiyerarisi iinde bir ilevin parametresi en tepedeki taban snf trnden bir gsterici ya da bir referans ise, bu ilev hiyerari iinde herhangi bir snf trnden nesnenin adresi ile (ya da kendisi ile) arlabilir. Bylece ortak zellikleri olan snflarn hepsi zerinde genel ilem yapan ilevler yazlabilir. void process(Base &); Yukarda bildirimi verilen ilevin parametre deikeni Base trnden bir referanstr. Bu ilev Base trnden bir nesne ile arlabildii gibi Base snfndan treyen herhangi bir snf trnden nesne ile de arlabilir. Tremi snf nesnesinin taban snf nesnesiymi gibi kullanlabilir. Ama bunun tersi doru deildir. Bir tremi snf nesnesine taban snf nesnesinin atanmas ya da tremi snf gsterici deikene taban snf trnden bir adresin dorudan atanmas geersizdir.

Tretmede Kopyalayan Kurucu levin Durumu


Tremi snf nesnesi kendi iinde bir taban alt nesnesini de ierir. Bir tremi snf nesnesi ilkdeerini bir baka tremi snf nesnesinden alarak yaratlrsa, tremi snf nesnesi iindeki taban snf nesnesinin durumu ne olur? Eer tremi snf nesnesi iin bir kopyalayan kurucu ilev yazlmamsa, derleyici yazaca kopyalayan kurucu ilevin bana, taban snfn kopyalayan kurucu ilevin ars kodunu ekler. Yani ilkdeer alan tremi snf nesnesinin taban snf ksm iin de taban snfn kopyalayan kurucu ilevi arlr. Burada dikkat edilmesi gereken durum udur: Eer tremi snfn kurucu ilevi programc tarafndan yazlrsa, taban snf nesnesine ilkdeer verilmesinden de programc sorumlu olur. Eer taban snf ksmna ilkdeer verilmez ise, tremi snf nesnesinin taban snf ksm iin varsaylan kurucu ilev arlr. Aadaki rnei inceleyin:

191/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> class Base { public: Base(const Base &r){std::cout << "Base(const Base &)" << std::endl;} Base(){std::cout << "Base()" << std::endl;} }; class Der: public Base { public: Der() {std::cout << "Der()" << std::endl;} Der(const Der &) {std::cout << "Der(const Der &)" << std::endl;} }; int main() { Der der1; Der der2(der1); return 0; }

Der der2(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(const Der &r) :Base(r) {cout << "Der(const Der &)" << endl;} Tremi snf nesnesinin taban snf ksm iin hangi kurucu ilev arlr? Taban snfn kopyalayan kurucu ilevin parametre deikeni Base trnden referans olduuna gre, bu referans ilkdeerini tremi snfn kopyalayan kurucu ilevin parametre deikeni olan r referansndan alabilir deil mi? Sonuca taban snf trnden bir referansa tremi snf trnden bir nesne ile ilkdeer veriliyor.

Tretmede Atama levinin Durumu


Benzer durum atama ilevini ykleyen ilev iin de geerlidir. C++ da ayn snf trnden iki nesnenin birbirine atanmas durumunda, atama ilecinin sol tarafndaki snf nesnesi iin atama ilecini ykleyen ilevin arldn biliyorsunuz. Snf iin atama ilevi yklenmemise, atama ilecini ykleyen ilevi derleyici yazar. Derleyicinin yazd atama ilevi snfn statik olmayan public inline ye ilevidir. Bu ilev snf elemanlarn karlkl olarak birbirine atayarak referans yoluyla atamann yapld nesneye geri dner. rnein Myclass isimli bir snf iin, derleyici tarafndan otomatik olarak yazlan atama ilevinin bildirimi aadaki gibi olur: class Myclass { public: Myclass &operator=(const Myclass &); }; Tremi snf iin bir atama ilevi yazlmazsa tremi snf iin ilevi derleyici yazar. Derleyicinin yazd atama ilevi snf nesnesinin elemanlarn karlkl olarak birbirine

192/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

atamakla kalmaz, nesnelerin taban snf alt nesnelerini de birbirine atar. Bu atama iin yine taban snfn atama ilevi arlr. Burada yine dikkat edilmesi gereken nokta udur: Tremi snf iin atama ilecini ykleyen bir ilev yazlrsa, artk derleyici bu ileve herhangi bir kod eklemez. Yani taban snf nesnelerinin birbirine atanmas da, tremi snfn atama ile ilevinin sorumluluundadr. Aadaki kodu inceleyin: class Der : public Base { public: Der &operator=(const Der &); }; Der &Der::operator=(const Der &r) { *(Base *)this = r; //taban snf ksm iin yaplan atama return *this; }

Der snf iin yazlan atama ilevi iindeki *(Base *)this = r; atamasn inceleyelim. lev iinde kullanlan this adresi atama ilecinin sol tarafna gelen tremi snf nesnesinin adresidir. Bu adres, tr dntrme ileci ile nce taban snf trnden bir adrese dntrlyor. Byle bir dnmle elde edilen adresin tremi snf nesnesinin iindeki taban snf nesnesinin adresi olduunu biliyorsunuz. Bu adresin ierik ilecinin terimi olmasyla elde edilen nesne, tremi snf nesnesinin taban snf alt nesnesidir. Bu durumda taban snfnn atama ilevi arlr. Bu ileve argman olarak tremi snf nesnesi gnderilmi olur. Taban snf nesnesinin atama ilevinin parametresi taban snf trnden refarans olduundan bu ileve tremi snf nesnesinin gnderilebileceini biliyorsunuz. Derleyicinin bu deyim iin aadaki gibi bir kod rettiini dnebilirsiniz: ((Base *)this)->operator = (r); Ayn ii gerekletirmek iin aadaki gibi bir ar da yaplabilirdi: Base::operator=(r);

SANAL LEVLER ve OKBMLLK


Sanal ilevler nesne ynelimli programlarn gelitirilemesini ve bytlmesini byk lde kolaylatran, C++ dilinin ok nemli bir aracdr. Sanal ilevler, "nesne ynelimli programlama tekniinin olmazsa olmaz zelliklerinden biri olan, alma zamanna ilikin ok biimlilii salayan temel aratr. Bu ara ile bir taban snftan dorudan ya da dolayl biimde tretilmi tm alt snflara ilikin nesneler, yani bir snf hiyerarisi iinde kullanlabilecek tm nesneler, taban snf nesneleriymi gibi dnlerek genel programlar yazlabilir. Programn gelitirilmesi srasnda henz var olmayan snflar bu ara ile daha sonra programn genel yapsna eklenebilir. Bir snf hiyerarisi iinde bir snf nesnesi iin bir ilev yardmyla bir iin yaptrlmas durumunu inceleyelim. rnein bir snf nesnesinin elemanlarnn deerlerinin ekrana yazdrlmas gerektiini dnelim. Bu i snflarn display isimli ye ilevler tarafndan yaplsn:

193/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class A { // public: void display() const; }; class B:public A { // public: void display() const; }; class C:public A { // public: void display() const; };

// A snfnn elemanlarn yazdracak.

//

B snfnn elemanlarn yazdracak.

// C snfnn elemanlarn yazdracak.

Yukardaki rnekte A snfndan tretilmi B ve C snflarnda A snfnda bulunan display() ilevi yeniden tanmlanyor. imdi tremi bir snf nesnesi yoluyla yani nokta ileci kullanlarak display ilevi arlrsa, dar bilinirlik alanndaki isim geni bilinirlik alanndaki ayn ismi maskeleyecei iin, tremi snfn display ilevi arlr. int main() { A a; B b; C c; a.display(); b.display(): c.display(); return 0; }

//A snfnn display ilevi arlr. //B snfnn display ilevi arlr. //C snfnn display ilevi arlr;

ar snf nesnesi yoluyla deil de snf trnden gsterici ile yaplm olsayd da sonu ayn olurdu: int main() { A a, *pa = &a; C c, *cp = &c; B b; *bp = &b; pa->display(); pb->display(); pc->display(): return 0; }

//A snfnn display ilevi arlr; //B snfnn display ilevi arlr; //C snfnn display ilevi arlr;

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

194/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

trnden bir gsterici olabilir. Global olarak tanmlanan display isimli ilevin bildirimi yle olabilir: void display(const A *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}; class A { //... int type; 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. void display(const A *ptr) { switch (ptr->type) { case CLASSA: ((A *)ptr)->display(); break; case CLASSB: ((B *)ptr)->display(); break; case CLASSC: ((C *)ptr)->display(); } //.. } Ancak yukardaki kod parasnn ok nemli bir dezavantaj programn gelitirilmesi srasnda ortaya kar. Taban snf olan A snfndan ya da tretilmi snflar olan B ve C snflarndan yeni bir snf tretildiinde yukardaki display ilevinin bu yeni snf trnden bir nesnenin de deerlerini yazdrmas istendiinde, display ilevinin kaynak kodunu deitirmek gerekir. Bu da phesiz switch deyiminin iine yeni bir case daha eklemekle mmkn olur. Ayrca yukardaki gibi bir kodu okumak zorlar. te sanal ilevler ve alma zamanna ynelik ok biimlilik bu konuda yeni ufuklar aacak bir aratr.

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.

195/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

class Base { // public: virtual int vfunc(int, int); // }; class Der: public Base { // public: public: virtual int vfunc(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.

Sanal levlerin arlma Biimleri


Sanal ilevler eitli biimlerde arlabilir. Bu biimleri gsterebilmek iin nce aadaki snflar tanmlayalm: class Base { //... public: virtual void vfunc(); }; class Der: public Base { public: virtual void vfunc(); }; 1. Tremi snf nesnesinin adresinin ak bir biimde taban snf gstericisine atanmas yoluyla: int main() { Der der_object; Base *base_ptr; base_ptr = &der_object; base_ptr->vfunc(); (*base_ptr).vfunc(); return 0; }

Yukardaki rnekte

196/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin base_ptr->vfunc(); (*base_ptr).vfunc(); arlaryla Der snfnn vfunc ilevi arlr. 2. Bir ilevin parametre deikeni taban snf trnden bir gstericidir. lev de bir tremi snf nesnesinin adresiyle arlr. Parametre deikeni olan gsterici yoluyla sanal ilev arlabilir. void func(Base *ptr) { ptr->vfunc(); } int main() { Der der_object; func(&der_object); return 0; }

Yukardaki rnekte func(&der_object); deyimiyle arlan func ilevi iinde arlan vfunc ilevi Der snfnn vfunc ilevidir. 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. int main() { Der der_object(10, 20); Base &base_ref = der_object; base_ref.vfunc(); return 0; } 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.

197/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void foo(Base &r) { r.vfunc(); } int main() { der der_object(10, 20); foo(der_object); return 0; }

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: #include <iostream> using namespace std; class Base { //... public: virtual void vfunc() {cout << "Base::vfunc()" << endl;} void nfunc(); }; class Der: public Base { public: void vfunc(){cout << "Der::vfunc()" << endl;} }; void Base::nfunc() { cout << "Base::nfunc()" << endl; vfunc(); } int main() { Der der_object; der_object.nfunc(); return 0; }

Base snfndan Der isimli bir snf tretiliyor. Der snf Base snfnn vfunc isimli sanal ilevini eziyor. Taban snfn sanal olmayan nfunc isimli bir ilevi olduunu gryorsunuz. nfunc ilevi iinde sanal vfunc ilevi arlyor. main ilevi iinde Der snf trnden der_object isimli bir nesnenin yaratldn gryorsunuz. Bu nesne ile taban snfn nfunc ilevi arlyor. Bu durumda nfunc ilevi iinde arlan Der snfnn vfunc ilevi olur. Taban snf sanal ileve sahip olduu halde tremi snf sanal ileve sahip olmayabilir. Yani tremi snfn taban snf olarak ald snftaki sanal ilevi ezmesi (override) zorunlu

198/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

deildir. Bu durumda taban snf gstericisine bir tremi snf nesnesinin adresi atanarak sanal ilevi arlrsa, taban snfn ye ilevi arlr. Sanal ileve sahip olmayan tremi snfa ilikin bir snf nesnesinin adresi taban gstericisine atanr, bu gsterici yoluyla sanal ilev arlrsa tremi snfn sanal ileve sahip ilk taban snfnn sanal ilevi arlr. #include <iostream> using namespace std; class A{ public: virtual void foo(){cout << "A::foo" << endl;} }; class B: public A{ public: void foo(){cout << "B::foo" << endl;} }; class C: public B{ //... }; class D: public C{ //... }; int main() { D d; A *aptr = &d; aptr->foo(); return 0; }

Yukardaki rnekte D snf trnden d nesnesinin adresi A snf trnden aptr gsterici deikenine atanyor. aptr gstericisi ile sanal foo ilevi arldnda, B snfnn foo ilevi arlr. nk D snf ve D snfnn dorudan taban snf olan C snf sanal ilev olan foo ilevini ezmitir. Hiyerari iinde ilevi ilk ezen snf B snf olduu iin arlan ilev de B snfnn ilevidir.

private Sanal levler


Sanal ilev arlabilmesi iin tretme biiminin public olmas gerekir. Bir sanal ilev arldnda gerekte arlacak olan tremi snfn sanal ilevi tremi snfn herhangi bir blmnde olabilir. Ancak ar rnein taban snf trnden bir referans ya da gsterici ile yaplyorsa, arlan ilev taban snfn sanal public blmde olmak zorundadr. rnein: Base *base_ptr Der der_object; baseptr = &derObject; baseptr->vfunc(); Burada vfunc sanal bir ilev olsun, gerekte arlacak olan ilevin hangi snfn vfunc ilevi olduu programn alma zamannda belirlenir. Ancak vfunc ilevinin taban snfn hangi blmnde bildirildii derleme zamannda aratrlr. Der snfnn vfunc sanal ilevi Der snfnn herhangi bir blmnde bildirilmi olabilir, ancak arnn geerli olabilmesi

199/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

iin Base snfnn vfunc sanal ilevinin Base snfnn public blmnde bildirilmi olmas gerekir.

Sanal lev armann Nedenleri


Sanal ilev armann balca iki faydal nedeni vardr: 1. Bir snfn ilevini deitirmek 2. Trden bamsz ilemler yaplmasna olanak salamak rnein A gibi bir snf varsa, bu snf belirli ilemleri yapyorsa, bu snfa hi dokunmadan snfn yapt ilemler zerinde deiiklik yaplmas salanabilir. Snf hiyerarisi iinde snfa ilikin bir i yapan ilevin, nesnesinin hangi snftan olduunu saptayarak i yapmasna dayanan tasarmlar kt tekniktir. Bu tr kodlar yerine sanal ilev mekanizmasn kullanmak daha iyi tekniktir. Snf hiyerarisine zaman iinde yeni snflar katlabilir. Snf hiyerarisine yeni bir snf eklendiinde yalnzca eklenen snfn kodlar yazlr, genel ilem yapan ilevlerin kodlarnda bir deiiklik yaplmas gerekmez. Sanallk mekanizmasnn yeni snf da kapsamas iin daha nce yazlm kaynak kodlarda da bir deiiklik yaplmas gerekmez. Byle bir kolaylk olmasayd bakasnn tasarlad ve yazd snf hiyerarilerine yeni tretmelerle ekleme yapld zaman, faydalanlan snflarn yalnzca balk dosyalarna deil kaynak kodlarna da sahip olmak gerekirdi. Sanal ilevler kullanlarak bir snf hiyerarisi iinde yer alan snflarn ortak zelliklerine dayanarak kod yazlabilir.

Sanallk Devreden kartlmas


Sanal bir ilevin arlmas hakkndaki kararn programn alma zamannda deil de (late binding) derleme zamannnda verilmesi (early binding) isteniyorsa, sanal ilev snf ismi ve znrlk ileci yardmyla arlabilir: #include <iostream> class Base { int b; public: Base(int val = 0): b(val) {} virtual void display()const {std::cout << b << std::endl;} }; class Der: public Base { int d; public: Der(int val1 = 0, int val2 = 0): Base(val1), d(val2) {} void display()const {std::cout << d << std::endl;} }; int main() { Base *base_ptr; Der derObject(10, 20); base_ptr = &derObject; base_ptr->Base::display(); //Base snfnn display ilevi arlr! return 0; }

Sanal lev Kullanlmasna likin rnekler


1. ngilizce yazlar zerinde ilem yapan bir Cstring snf olsun. Bu snfn yazlar karlatran, byk harf ya da kk harfe dntren ye ilevleri olsun. Yazlarn karlatrlmas ve harf dnmnn yaplmas dile bal bir durumdur. Cstring snfnn compare isimli karlatrma ilevi karlatrma ilemini yaparken iki karakteri

200/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

karlatran sanal cmpchr ilevini aryor olsun. Cstring snfndan bir snf tretilir. cmpchr sanal ilevi tretilmi snf iin yeniden yazlrsa, artk compare ilevi tremi snfn cmpchr ilevini arr. Bylece ilemler baka bir dilde de doru bir biimde yaplabilir. 2. Bir dizinin Array isimli bir snf ile temsil edildiini dnelim. Bu snfn sraya dizme ilemini yapan sort isimli sanal bir ilevi olsun. Baz ye ilevler de bu ilevi ararak sralama ilemini gerekletiriyor olsunlar. Sralama algoritmalar ok eitli olabilir. Array snfndan bir tretme yaplarak, tretilen snfn sanal ilevi baka bir sralama algoritmasn kullanacak biimde yeniden yazlabilir. Bu durumda sralama ilemi istenilen algoritmayla yaplabilir. 3. MFC snf sisteminde her trl pencere ilemleri CWnd snf tarafndan yaplmaktadr. Diyalog penceresi de zel bir tr penceredir. Diyalog penceresi ilemleri CWnd snfndan tretilen CDialog snf ile yaplmaktadr. Her diyalog penceresi dierinden farkl zelliklere sahip olabilir. O ilemler de CDialog snfndan tretilen snfla temsil edilir. Diyalog penceresini grnr hale getirmek iin CDialog snfnn DoModal ilevi arlr. CDialog snfnn OnOk ve OnCancel sanal ilevleri de vardr. CWnd snfndan tretilen bir snfa ilikin bir nesne tanmlandnda CWnd snfnn kurucu ilev ile yaratlan nesnenin adresi MFC sistemi tarafndan global bir biimde saklanr. Ne zaman bir diyalog penceresinde OK ya da Cancel tularna baslrsa MFC saklam olduu adresle OnOk ya da OnCancel ilevlerini arr. Eer bu ilevler yeniden yazlrsa yeniden yazlan arlr. Tabii orjinal OnOk ve OnCancel ilevleri kritik baz ilemleri de yapmaktadr. Bu durumda bu ilevlerin dorudan arlmalar da gerekebilir. void MyDialog::onOk() { CDialog::OnOk(); //... }

Sanal lev Mekanizmasnn Oluturulmas


Bir tremi snf nesnesinin adresi taban snf gstericisiyle ilevden ileve dolatrlm olabilir. En sonunda sanal ilev arlm olsa bile nesnenin ait olduu snfa ilikin sanal ilev arlr. Peki derleyici bu olay derleme srasnda belirleyebilir mi? Bu olayn derleme srasnda saptanmas mmkn deildir. levlerin kodunun yrtlmesi programn alma zamannda gerekleir. Gerekte hangi sanal ilevin arlacan belirlemek ancak programn alma zaman srasnda kurulacak bir mekanizmayla mmkn olabilir. Bu mekanizmann alma zamannda kurulmasna ingilizce "late binding" ya da "dynamic binding" denir. Hangi ilevin arlacana programn alma zamannda deil de derleme zamannda karar verilmesine ise ingilizcede "early binding" ya da "static binding" denir. Aadaki kodu inceleyin:

201/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> #include <cstdlib> #include <ctime> class Calisan { public: virtual void kendini_tanit() {std::cout << "Ben calisanim" << std::endl;} }; class Mudur:public Calisan { public: virtual void kendini_tanit(){std::cout << "Ben mudurum" << std::endl;} }; class GenelMudur:public Mudur{ public: virtual void kendini_tanit(){std::cout << "Ben genel mudurum" << std::endl;} }; class Sekreter: public Calisan { public: virtual void kendini_tanit(){std::cout << "Ben sekreterim" << std::endl;} }; class Sofor: public Calisan { public: virtual void kendini_tanit(){std::cout << "Ben soforum" << std::endl;} }; class Isci: public Calisan { public: virtual void kendini_tanit(){std::cout << "Ben isciyim" << std::endl;} }; Calisan *calisan_yarat() { Calisan *pd; switch (rand() % 5) { case 0: pd = new Mudur; break; case 1: pd = new GenelMudur; break; case 2: pd = new Sekreter; break; case 3: pd = new Sofor; break; case 4: pd = new Isci; } return pd; } int main() { srand(time(0)); for (int k = 0; k < 20; ++k) { Calisan *pd = calisan_yarat(); pd->kendini_tanit(); delete pd; } return 0; }

202/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki rnekte Calisan snfndan Mudur snf, Mudur snfndan GenelMudur snf tretiliyor. Calisan snfndan ayrca Sekreter, Sofor ve Isci snflar tretiliyor. Tretilen tm snflar Calisan snfnn sanal kendini_tanit isimli ilevini eziyor. Global calisan_yarat ilevi iinde, tretme hiyerarisi iinde yer alan snflardan birine ait rastgele bir dinamik nesnenin yaratldn gryorsunuz. rand ilevinin rettii rastgele deere gre yaratlan dinamik nesnenin tr Mudur, GenelMudur, Sekreter, Sofor, Isci olabilir. Dinamik nesnenin hangi trden olaca programn alma zamannda belli olur. calisan_yarat ilevi yaratilan dinamik snf nesnesinin adresini dndryor. main ilevi iinde bir dng iinde nce calisan_yarat ilevi arlarak elde edilen adres Calisan snf trnden bir gsterici deikende tutuluyor. Sonra bu gsterici deikenle sanal ilev kendini_tanit arlyor. arlan kendini_tanit ilevi hangi snfn sanal ilevidir? Peki hangi ilevin arlacann alma zamannda belirlenmesi nasl mmkn oluyor? Sanal ilev ar mekanizmasnn derleyiciler tarafndan ne ekilde salanaca standart olarak belirlenmemitir. Yani derleyiciler bu mekanizmay istedikleri gibi oluturabilir. Ancak derleyicilerin ou bu mekanizmay kurabilmek iin her snfa ilikin bir sanal ilev tablosu yaratr. Bu tablolar ksaca vtable (virtual table) olarak isimlendirilir. Bu sanal ilev tablolarnda ilgili snfn sanal ilevlerin adresleri bulunur. Sanal ileve sahip bir snfa ilikin bir nesne tanmlandnda o nesne iin bir gizli ilev gstericisi kadar daha ek yer ayrlr. Bu gizli gstericide snfn sanal ilev tablosunun adresi tutulur. Bu gizli gstericinin nesnenin neresinde tutulduu standart olarak belirlenmemitir. Ancak popler derleyicilerin hemen hemen hepsinde nesnenin en dk anlaml adresinde tutulur. Base Snfnn Sanal lev Tablosu Sra No Adres 1 &Base::func1() 2 &Base::func2() Der1 Snfnn Sanal lev Tablosu Sra No Adres 1 &Der1::func1() 2 &Der2::func2() Der2 Snfnn Sanal lev Tablosu Sra No Adres 1 &Der2::func1() 2 &Der2::func2() Bu durumda bir sanal ilev arldnda aa seviyeli u ilemler yaplr: 1. Sanal ilev gstericisi (vpointer) alnr. Sanal ilev tablosunun yeri (vtable) bulunur. 2. Sanal ilev tablosunda ilgili sanal ilevin adresi bulunur. 3. Adresi bulunan sanal ilev arlr.

Sanal levlerin Maliyeti


Sanal ilev mekanizmas phesiz ek bir bellek kullanmna ve daha fazla ilemci zamanna neden olur. Ek bir bellek kullanm sz konusudur. nk her okbiimli snf iin bir sanal ilev tablosu ve her okbiimli snf nesnesi iin sanal ilev tablo gstericisi bellekte fazladan bir yer kaplar. Sanal ilevin arlabilmesi iin nce sanal ilev tablosunun adresinin alnarak tabloya eriilmesi ve tablodan da arlacak ilevin adresinin alnmas gerekir. Yani fazladan iki "ierik alma" (indirection) ilemi yaplr. Ancak ortalama bir uygulama programnda sanal ilevlerin getirdii ek bellek ve zamansal maliyet yok saylabilir. Sanal ilevlerin kazandrd faydann yannda getirdii ek maliyetler gerekten nemsenmeyecek boyuttadr.

203/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Sanal Sonlandrc levler


Bir snf hiyerarisi iinde dinamik olarak yaratlan snf nesneleri iin alma zamanna ilikin okbiimliliin uygulanmasnda baz istenmeyen durumlar ortaya kabilir. Daha nceki blmlerden hatrlayacanz gibi new ileciyle snf trnden bir nesne yaratld zaman ilgili snfn kurucu ilevi arlr. delete ileciyle dinamik olarak yaratlan nesneye ilikin blok free storea geri verildiinde ilgili snf nesnesi iin sonlandrc ilevi arlr. Aadaki kodu inceleyin: class Base { char *base_ptr; public: Base(); ~Base(); }; #include <iostream> using namespace std; Base::Base() { cout << "Base::Base()" << endl; base_ptr = new char[1000]; cout << "Base::Base()icinde 1000 byte'lk blok elde edildi!" << endl; } Base::~Base() { cout << "Base::~Base()" << endl; delete[] base_ptr; cout << "Base::~Base icinde 1000 byte'lik blok geri verildi!" << endl; } class Der: public Base { char *der_ptr; public: Der(); ~Der(); }; Der::Der() { cout << "Der::Der()" << endl; der_ptr = new char[2000]; cout << "Der::Der()icinde 2000 byte'lk blok elde edildi!" << endl; } Der::~Der() { cout << "Der::~Der()" << endl; delete[] der_ptr; cout << "Der::~Der icinde 2000 byte'lik blok geri verildi!" << endl; } int main() { Base *ptr = new Der; delete ptr; return 0; }

204/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

altrlan programn ekran kts: Base::Base() Base::Base()icinde 1000 byte'lk dinamik blok elde edildi! Der::Der() Der::Der()icinde 1000 byte'lk dinamik blok elde edildi! Base::~Base() Base::~Base icinde 1000 byte'lik blok geri verildi!

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: class Base { 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 Der snfnn sonlandrc ilevi arld!.. Base snfnn sonlandrc ilevi arld!.. 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. class Myclass { public: virtual void func(); virtual ~Myclass() {} };

205/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Saf Sanal levler


Bir snf hiyerarisi oluturulmasnn ou zaman amac, mantksal olarak iliki halindeki snflarn ortak olarak paylalabilecek kodlarn bir taban snf iinde toplamaktr. Bylece genel ilem yapan kodlar en tepedeki taban snfa gre yazlabilir. Daha nce yazlan kodlar, daha sonra yazlacak kodlar yani tretilmi snflara ilikin kodlar arabilir. Ancak bazen taban snflar, kendilerinden tretilecek snflara yalnzca bir arayz salamak amacyla oluturulur. Ama taban snfn salad arayze ilikin tremi snflara sunaca bir kodun (implementasyon) varl sz konusu olmayabilir. Baka bir deyile taban snf, kendisinden tretilecek snflara kendi arayzn vermekte ancak bu arayze ilikin kodlar tremi snfn kendisinin tanmlamasn istemektedir. rnek olarak Shape isimli bir snf tanmlandn ve bu snftan Triangle, Square, Circle snflarnn tretildiini dnelim. Shape snfnn draw isimli bir sanal ye ilevi olsun: class Shape { public: //... virtual void draw () const; //... };

Bir ilevin sanal olmas ne anlama gelir? Sanal bir ilev tremi snf tarafndan ezilebilir (override edilebilir). Ancak bu bir zorunluluk deildir. Tremi snf taban snfn tanmlad sanal ilevi ezmez ise tremi snfa ilikin bir nesne ile sanal ilev arldnda, taban snfn ye ilevi arlr. Tasarm asndan bakldnda bu durumun anlam udur: Sanal bir ilev, hem arayz hem de default kodu (default implementation) devir alnan bir ilevidir. Sanal bir ilevin tremi bir snfa verdii ileti udur: "Beni istediin gibi tanmlayabilirsin. Tanmlarsan, senin tanmladn arlr, ancak tanmlamaz isen arlan ben olacam!" Yine Shape snfna dnelim. Shape snfndan treyecek snflar draw ilevini ezmezlerse taban snfn tanmlad draw ilevi arlr. Peki Shape snfnn draw ilevi ne yapabilir? Bir karenin ya da bir genin izilmesi mantksal olarak bir anlam tar. Ama bir "ekil" in izilmesi ne demektir? Shape snfnn draw ilevi iin salayabilecei bir kod paras yoktur. Shape snf draw ilevini yalnzca tremi snflar iin bir arayz oluturmas amacyla bildirmitir. Evet baz durumlarda bir taban snf sanal ilevinin bildirilmesinin tek nedeni kendisinden tretilecek snflara uygun bir arayz sunmaktr. Bu nedenle taban snf sanal ilevinin tanmnn yaplmas mantkl deildir. C++'da bu durum saf sanal (pure virtual) ilevlerle gerekletirilir. Bir ilevin saf sanal olarak bildirilmesi ilevin bildiriminde ileve atama ilecinin kullanlarak 0 deerinin atanmasyla gsterilir. Bu gerek bir atama ilemi deildir. Yalnzca bildirilen ilevin saf sanal olduunu gsteren bir szdizim kuraldr. class Shape { public: virtual void draw() = 0; //... };

//saf sanal ilev

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

206/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

derleme zamannda hata oluur. Aadaki kodu inceleyin: class Shape { public: virtual void draw() = 0; };

//saf sanal ilev

int main() { Shape myshape; //Geersiz! Shape *shape_ptr = new Shape; //Geersiz! return 0; }

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: class Shape { public: virtual void draw() const = 0; virtual void write_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: void process(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.

207/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bir saf sanal ilevin tanmlanmamas, derleme ya da balama aamasnda bir hata oluturmaz. Ancak saf sanal ilevler istenirse tanmlanabilir. Programc tretme hiyerarisi iin ortak olan bir kod parasn isterse taban snfn saf sanal ilevi iinde toplayabilir. Bu durum az da olsa baz kod kalplarnda karmza kar.

Saf Sanal Sonlandrc levler


Sonlandrc ilevler de saf sanal olarak bildirilebilir. Ancak saf sanal sonlandrc ilevlerin dier saf sanal ilevlere gre nemli bir fark vardr. Bu ilevler tanmlanmak zorundadr. Eer tanmlanmazlar ise bu snftan tremi bir somut snf trnden bir nesne yaratldnda balama zamannda hata oluur. Tremi snf nesnesinin sonlandrc ilevinin kodu sonunda taban snfn sonlandrc ilevinin arldn hatrlayalm. Taban snfn sonlandrc ilevi saf sanal da olsa, ilevin tanm yoksa, ilev ars balayc tarafndan hata olarak iaretlenir. Aadaki kod parasnda saf sanal sonlandrc ilevin de normal sonlandrc ilevler gibi arld gsteriliyor: #include <iostream> class Shape { public: virtual ~Shape() = 0; }; Shape::~Shape() { std::cout << "Shape::~Shape()" << std::endl; } class Square:public Shape { public: ~Square() {std::cout << "Square::~Square()" << std::endl;} }; int main() { { Square s; } Shape *ptr = new Square; delete ptr; return 0; }

Programn ekran kts: Square::~Square() Shape::~Shape() Square::~Square() Shape::~Shape() Taban snfn saf sanal bir ilevinin tremi snf tarafndan ezilmemesi durumunda tremi snfn da soyut snf olur. Ancak bu kural saf sanal sonlandrc ilev iin geerli deildir. Yani taban snfn saf sanal sonlandrc ilevini tremi snf ezmese de soyut snf olarak ele alnmaz. Halen tremi snf trnden bir nesne tanmlanabilir. Soyut bir snfn tek saf sanal ilevinin saf sanal sonlandrc ilev olmas durumu zel bir durumdur. Zira bu durumda tremi snf bu ilevi yeniden tanmlamasa da soyut bir snf olarak ele alnmaz. Yani taban snftan tretilen btn snflar somut snflar olarak ele alnr.

Kurucu levler inde Yaplan Sanal lev arlar

208/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bir snfn kurucu ilevi iinde sanal bir ilev arlrsa sanallk mekanizmas devreye girmez. Aadaki kodu inceleyin: #include <iostream> class Base { //... public: Base(); virtual void vfunc(); }; using namespace std; Base::Base() { cout << "Base::Base()" << endl; vfunc(); } void Base::vfunc() { cout << "Base::vfunc()" << endl; } class Der:public Base { public: Der(); void vfunc(); }; Der::Der() { cout << "Der::Der()" << endl; } void Der::vfunc() { cout << "Der::vfunc()" << endl; } int main() { Der der_object; return 0; }

Yukardaki rnekte Base snfndan tretilen Der snf Base snfnn vfunc ilevini eziyor. main ilevi iinde Der snf trnden bir nesne tanmlandn gryorsunuz. Der snfnn kurucu ilevinin banda taban snf olan Base snfnn kurucu ilevi arlr. Base snfnn kurucu ilevi iinde sanal vfunc ilevi arlyor. vfunc(); ars aslnda this->vfunc(); arsdr.

209/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Normal olarak this gstericisi iinde Der snf trnden bir adres olduuna gre arlan vfunc ilevini tremi snfn vfunc ilevi olmas gerekir. Ancak arlan Der snfnn deil Base snfnn vfunc ilevidir. Bu C++ dilinin bir kuraldr. Peki kurucu ilev iinde neden sanallk mekanizmas devreye girmiyor? Tremi snf nesnesi yaratldnda nce taban snf ksm oluturulur. Bu da taban snfn kurucu ilev tarafndan yaplr. Taban snfn kurucu ilevi iinde arlan sanal ilev tremi snfn sanal ilevi olsayd, bu durumda bu ilev ilkdeerini almam elemanlar zerinde ilem yapmak durumunda kalabilirdi. Tremi snf nesnesinin baz kaynaklar kullandn dnelim. Bu kaynaklar tremi snf nesnesine tremi snf nesnesinin kurucu ilevi tarafndan balanr. vfunc ilevi iinde bu kaynaklarn kullanldn dnelim. Eer sanallk mekanizmas devreye girseydi, tremi snfn vfunc ilevi bu kaynaklar henz nesneye balanmadan bu kaynaklar kullanmaya alrd. Bu da alma zaman hatalarna neden olurdu.

Sonlandrc levler inde Yaplan Sanal lev arlar


Benzer durum sonlandrc ilevler iin de geerlidir. Bir sonlandrc ilev iinde sanal bir ilev arldnda sanallk mekanizmas devreye girmez. Aadaki rnei inceleyin:

210/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Base { public: ~Base(); virtual void vfunc(); //... }; #include <iostream> using namespace std; Base::~Base() { std::cout << "Base::~Base()" << std::endl; vfunc(); } void Base::vfunc() { std::cout << "Base::vfunc()" << std::endl; } class Der:public Base { public: ~Der(); void vfunc(); //... }; Der::~Der() { std::cout << "Der::~Der()" << std::endl; } void Der::vfunc() { std::cout << "Der::vfunc()" << std::endl; } int main() { Der der_object; return 0; }

main ilevi iinde yaratlan der_object nesnesinin mr sona erdiinde bu nesne iin sonlandrc ilev arlr. Der snfnn sonlandrc ilevinin kodunun sonunda derleyici tarafndan eklenen kod ile taban snf olan Base snfnn sonlandrc ilevi arlr. Base snfnn sonlandrc ilevi iinde yaplan vfunc arsnda sanallk mekanizmas devreye girmez. Taban snfn sonlandrc ilevi arldnda artk tremi snf nesnesi ksm yok edilmitir. Eer sanallk mekanizmas devreye girseydi, yani tremi snfn vfunc ilevi arlsayd, bu ilev artk var olmayan kaynaklar zerinde ilem yapmaya alabilirdi. Bu da alma zaman hatalarnn olumasna neden olurdu.

Taban Snf Nesnesiyle Yaplan Sanal lev ars


Sanal ilev mekanizmasnn devreye girmesi iin sanal ilev arsnn bir gsterici ya da bir referans yoluyla yaplmas gerekir. Bir tremi snf nesnesi bir taban snf nesnesine atandktan sonra taban snf nesnesiyle bir sanal ilev arlrsa sanallk mekanizmas devreye girmez. Aadaki rnei inceleyin:

211/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> class Base { public: virtual void vfunc()const {std::cout << "Base::vfunc()" << std::endl;} }; class Der: public Base { public: void vfunc()const {std::cout << "Der::vfunc()" << std::endl;} }; void func1(Base b) { b.vfunc(); } void func2(Base *ptr) { ptr->vfunc(); } int main() { Der der; func1(der); func2(&der); return 0; }

main ilevi iinde yaplan func1(der); arsnda sanallk mekanizmas devreye girmez. func1 ilevi deerle arlyor. Argman olan der nesnesi ilevin parametre deikeni olan Base trnden b nesnesine atanrken nesne dilimlenir (object slicing). levin parametre deikeni olan b nesnesi ile yalnzca Base snfnn vfunc ilevi arlabilir. Oysa func2(&der); arsnda sanallk mekanizmas devreye girer, nk func1 ilevi adresle arlmtr. Programn alma zamannda *ptr nesnesi hangi trden ise, o snfn vfunc ilevi arlr.

Kurucu levler Sanal Olabilir mi


Bir snfn kurucu ilevi sanal olamaz. virtual anahtar szcnn bir kurucu ilevin bildiriminde kullanlmas bir szdizim hatasdr. Ancak baz uygulamalarda "sanal bir kurucu ileve" gereksinim duyulur. Aadaki gibi bir ilevin varln dnelim: void func(Base *base_ptr) { Base *d_object = //new (base_ptr'nin iinde hangi snf trnden varsa o snf trnden bir dinamik nesne yaratlacak) //.. }

//adres

Yukardaki func ilevinin taban snf trnden bir gstericiye ya da referansa sahip olduunu dnelim. Taban snf trnden bir gstericiye herhangi bir tremi snf trnden nesnenin adresinin atanabileceini biliyorsunuz. func ilevi snf hiyerarisi

212/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

iinde hangi snf trnden bir nesnenin adresi ile arlrsa ilev iinde o snf trnden dinamik bir nesne yaratlmak istendiini dnelim. Bu nasl yaplabilir? Adeta sanal bir kurucu ileve gereksinim duyuluyor. Bu durumu salamak iin kullanlan idiyoma "sanal kurucu ilev idiyomu" (virtual constructor idiom) denir. Aadaki kod parasn inceleyin: #include <iostream> class Base { public: virtual Base *create_similar() const = 0; virtual Base *clone() const = 0; virtual ~Base() {} //... }; class Der1: public Base { public: Der1() {std::cout << "Der1::Der1()" << std::endl;} Base *create_similar()const {return new Der1;} Base *clone() const {return new Der1(*this);} //... }; class Der2: public Base { public: Der2() {std::cout << "Der2::Der2()" << std::endl;} Base *create_similar() const {return new Der2;} Base *clone()const {return new Der2(*this);} //... }; class Der3: public Base { public: Der3() {std::cout << "Der3::Der3()" << std::endl;} Base *create_similar()const {return new Der3;} Base *clone()const {return new Der3(*this);} }; void func(Base *ptr) { Base *ptr1 = ptr->create_similar(); Base *ptr2 = ptr->clone(); delete ptr1; delete ptr2; } using namespace std; int main() { cout << "Der1, Der2 ve Der3 nesneleri yaratiliyor." << endl; Der1 der1; Der2 der2; Der3 der3; cout << "Her bir nesne sirasiyla func ilevine gonderiliyor" << endl; func(&der1); func(&der2); func(&der3); return 0; } Taban snf olan Base snf iinde isimleri create_similar ve clone olan iki saf sanal ilev bildiriliyor. Base snfndan treyen her snf create_similar ve clone sanal ilevlerini ezer. Base snfndan treyen her snf -rneimizde Der1, Der2 ve Der3 snflar- ezdikleri create_similar ilevlerini kendi snflar trnden dinamik bir nesnenin adresiyle geri dndrr. clone ilevinin tek fark ise adresi dndrlen dinamik nesnenin varsaylan

213/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

kurucu ilev ile deil kopyalayan kurucu ilevle ilkdeerini almasdr. Yani clone ilevi hangi snf nesnesi iin arlma o nesnenin deerine eit deerde dinamik bir nesnenin adresini dndrr. Bylece taban snf trnden bir gsterici ya da referans ile sanal create_similar ya da clone ilevleri arldnda, sanal ilev mekanizmas devreye girer. Taban snf gstericisi iinde hangi nesnenin adresi varsa o snfn sanal ilevinin arlmasyla o snf trnden bir dinamik nesne yaratlm olur.

Sanal levler ve Varsaylan Argmanlar


Sanal ilevler de varsaylan argman alabilir. Taban snfn sanal ilevini ezen tremi snf ilevi farkl bir varsaylan argmanla bildirilebilir: #include <iostream> class Base { public: virtual void func(int val = 20) {std::cout << "val = " << val << std::endl;} }; class Der : public Base{ public: virtual void func(int val = 100) {std::cout << "val = " << val << std::endl;} }; int main() { Der der; Base *bptr = &der; bptr->func(); return 0; }

Yukardaki rnekte bptr gstericisine Der snf trnden der isimli nesnenin adresi atanyor. Daha sonra bptr gstericisi ile sanal func ilevi argman gnderilmeden arlyor. leve varsaylan argman olarak hangi deer geilir? 20 mi 100 m? Sanal ilevin balanmas programn alma zamannda olsa da, varsaylan argman degerlendirilmesi programn derleme zamannda yaplr. Yukardaki program altrldnda ekrana 20 deeri yazdrlr.

Ezilen Bir Sanal levin Parametrik Yaps


Taban snfn bir sanal ilevinin tremi snf tarafndan ezilmesi iin tremi snfn tamamen ayn parametrik yapya sahip bir ilev bildirmesi gerekir. Tremi snfn bildirdii ilevin imzasnda bir farkllk olursa, tremi snf taban snfn sanal ilevini ezmi olmaz. Yeni bir ilev bildirmi olur. Tremi snfn bildirdii ilevin imzas taban snfn sanal ilevi ile ayn fakat geri dn deeri tr farkl ise bu durum derleme zamannda hata olarak deerlendirilir:

214/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Base { public: virtual int func1(int); virtual int func2(int); virtual int func3(int); }; class Der: public Base{ public: int func1(int); int func2(double); double func3(int); };

//Geersiz

Yukardaki bildirimleri inceleyelim: Base snfndan tretilen Der snf sanal func1 ilevini eziyor. Ancak sanal func2 ilevini ezmiyor. nk bildirilen func2 ilevinin imzas taban snfn bildirdii sanal func2 ilevinin imzasndan farkldr. Tremi snf iinde yaplan func3 ilevi bildirimi ise derleme zamannda hataya neden olur. Tremi snfn taban snfn sanal ilevini ezebilmesi iin, tamamen ayn parametrik yapya sahip bir ilev bildirmesi gerekir. Ancak bu durumun bir istisnas vardr (variant return type): Taban snfn sanal ilevi bir snf trnden adrese ya da referansa geri dnyorsa, tremi snfn ilevinin geri dn deeri, taban snfn geri dndrd snftan treyen bir snfa ilikin bir trden olabilir. Aadaki rnei inceleyin: class B { //... }; class D : public B { //... }; class Base { public: virtual B *vfunc1(); virtual B &vfunc2(); }; class Der: public Base { public: D *vfunc1(); D &vfunc2(); };

Yukardaki rnekte Base snf iinde iki ayr sanal ilev bildiriliyor. Sanal vfunc1 ilevinin geri dn deeri B* trndendir. Oysa Base snfndan tretilmi Der snf bu ilevi ezerken ilevin geri dn deerinin trn D* olarak bildiriyor. D snf B snfndan tretildii iin bu geerli bir durumdur. Benzer durum referans ile geri dnen sanal ilevler iin de geerlidir. Base snfnn sanal vfunc2 ilevi B& trne geri dnerken bunu ezen Der snfnn vfunc2 ilevi D& trne geri dnyor. Bu kural neden getirilmi olabilir? Der snf trnden bir nesne ile vfunc1 ya da vfunc2 ilevinin arldn dnelim:

215/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin Der der_object; D *ptr = der_object.vfunc1(); D &r = der_object.vfunc2(); Eer tremi snfn vfunc1 ve vfunc2 ilevleri A* ve A& trlerine geri dnselerdi yukardaki ilev arlar geerli olmazd.

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.

216/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: int get_max(const int *ptr, size_t size) { int max = *ptr; for (size_t k = 1; k < size; ++k) if (ptr[k] > max) max = ptr[k]; return max; }

lev double trden bir dizi iin yazlmak istendiinde double get_max(const double *ptr, size_t size) { double max = *ptr; for (size_t k = 1; k < size; ++k) if (ptr[k] > max) max = ptr[k]; return max; }

Bu i nasl daha kolay bir hale getirilebilir? levlerin yazm nilemci programa yaptrlabilir. Makrolar konusunu hatrlayalm: #include <string> #define generate_get_max(T) T get_max(const T *ptr, int size) { \ T max = *ptr; for (int k = 1; k < size; ++k)\ if (ptr[k] > max) max = ptr[k]; return max;} generate_get_max(int) generate _get_max(double) generate _get_max(std::string)

217/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki kod parasnda ismi generate_get_max olan bir makro tanmlanyor. Argman olarak farkl tr isimlerinin kullanlmasyla alan makrolarla, farkl trlere gre i gren ilevler tanmlanm olur. Ancak bu ama iin makrolar tanmlanmasnn baz sakncalar da vardr. Makrolar birden fazla satra yayldnda satr sonlarnda '\' karakteri kullanmak gerekir. Ayrca baz durumlarda gvenlik nedeniyle fazladan ayralar kullanlr. Makro ilevler gerek ilevlere gre farkl bir szdizime sahiptir. Makrolar nilemci program tarafndan alr. Makro-lev kullanlmas durumunda program yazan, hangi makronun alp almamas gerektiini dnmek zorundadr. Ayn tr iin bir makro-ilevin iki kez almamasn gvence altna almak programcnn sorumluluundadr. Makro-ilevlerde hata olumas durumunda kaynak kodda hatann olutuu yerin derleyici tarafndan belirlenmesi daha zor olur. Derleyiciler gerek ilevler iin bu konuda ok daha iyi destek verebilir. Baka bir zm, her tr iin doru ekilde alacak, trden bamsz bir ilevin yazm olabilir. C dilinin standart ilevlerden qsort ilevinin herhangi trden bir diziyi sralayabildiini anmsayn. Trden bamsz ilevler void trden gsterici parametreleri ile yazlabilir. Ancak bu yntem, kat bir tr denetiminin hedeflendii C++ dili iin pek uygun deildir. Ayrca trden bamsz yazlan ilevler, tre bal olarak yazlan ilevlere gre daha kark olma eilimindedir. Trden bamsz ilevlerin snf nesneleri iin arlmasnda baz sorunlar oluabilir. Bir baka zm de btn trlerin tek bir taban snftan tretilmesi olabilir. stenen algoritma en tepedeki taban snfa gre yazlrsa, bu ileve argman olarak btn trler gnderebilir. Bu durumda yine kat tr denetimi yaplmas engellenmi olur. Ayrca ilgili ilevden faydalanabilmek iin mutlaka bir tretme ilemi yapmak gerekir. ablonlar C++ a grece olarak ge eklenmi bir aratr. ablonlar dier programlama dillerinde ya yoktur ya da kstl lde destek verecek ekilde vardr. C++ diline son dnemde yaplan eklemelerin byk bir blm ablonlar konusunda yaplmtr. Programc ayn algoritmaya gre farkl trler iin i gren ok sayda ilev tanmlamak yerine yalnzca tanmlayaca ilevlere ilikin bir ablonu derleyiciye bildirir. Derleyici bu ablondan faydalanarak gerektii zaman istenilen trden bir ilevin tanmn kendi yapar. Yani derleyici ilevin kodunu yazar. lev ablonu tanmlanma szdiziminin genel biimi aadaki gibidir: template<class T> <geri dn deeri tr> <ilev ismi>([parametre deikenleri]) { } rnek: template <class T> void func(T &a) { //... } template bir anahtar szcktr. template anahtar szcnden sonra asal ayra < > gelmelidir. Asal ayra iinde yer alan class anahtar szcnn normal snf ilemleriyle bir ilgisi yoktur. class anahtar szc yerine typename anahtar szc de kullanlabilir. class ya da typename anahtar szcnden sonra isimlendirme kurallarna uygun herhangi bir isim (identifier) seilebilir. Seilen bu isim, yani yukardaki rnekteki T ismi bir tr belirtir. Byle ilevler birer ablondur. Yani kendi balarna alan kodda yer kaplamaz. Bir ilev ablonuna ilikin bir ilev arldnda derleyici nce ar ifadesi olarak kullanlan ifadenin trn saptar. Bu tre uygun olarak ablonda belirtilen ilevin kodunu programc iin yazar:

218/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> template <class T> void print(T a) { std::cout << a << std::endl; } int main() { print(3.5); print(30L); print('a'); print("Ahmet"); return 0; }

Yukardaki print ilev ablonunun tanmnda typename anahtar szc de kullanlabilirdi: template <typename T> void print(T a) { 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: void print(double a) { std::cout << a << std::endl; } print(30L); ar ifadesi iin derleyicinin yazd ilev: void print(long a) { std::cout << a << std::endl; } print('a'); ar ifadesi iin derleyicinin yazd ilev: void print(char a) { std::cout << a << std::endl; }

219/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

print("Ahmet"); ar ifadesi iin derleyicinin yazd ilev: void print(const char *a) { std::cout << a << std::endl; } lev ablonlarnda tr belirten isme ablon parametresi (template parameter) denir. lev ablonunda kullanlan parametre deikenlerine ilev ar parametreleri (call parameters) denir. Ayn ablon parametresinin asal ayra iinde birden fazla yer almas geersizdir: template<class Type, class Type> Type max(Type a, Type b) { //... } //Geersiz!

Bir ablon parametresi, ilev ablonunun her yerinde tr belirten szck olarak kullanlabilir: #include <iostream> template <class T> T max_of_three(T a, T b, T c) { T max = a; if (b > max) max = b; if (c > max) max = c; return max; } int main() { std::cout << max_of_three(12, 51, 67) << std::endl; std::cout << max_of_three(17.2, 15.3, 6.7) << std::endl; return 0; }

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.

Birden Fazla ablon Parametresi Olabilir


Bir ilev ablonu birden fazla ablon parametresine sahip olabilir. Bu durumda ilev ablonuna ilikin ileve yaplan arda kullanlan argmanlarn trlerine gre, derleyici tarafndan farkl parametrik yapya sahip ilevler retilebilir: #include <iostream> template<class A, class B> void display(A a, B b) { std::cout << a << " " << b << std::endl;

220/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin } #include <string> using namespace std; int main() { string s("Necati"); int x = 10; double d = 3.25; display(x, d); display(d, s); display('A', s); return 0; } // void display(int, double); // void display(double, string); // void display(char, string);

lev ablonu Bildirimleri


Bir ilev ablonuna ilikin bildirim de yaplabilir: template <class T> T min(T, T); lev bildirimlerinde parametre deikeni isimlerinin yazlmasnn zorunlu olmadn hatrlayalm. Ayn bildirimi aadaki gibi de yapabilirdik: template <class Type> Type min(Type a, Type b); Normal ilevlerin bildiriminde olduu gibi, ilev ablonu bildirimlerinde de parametre ayrac iinde kullanlan isimlerin bilinirlik alan bu ayracn iiyle snrldr. Yani bu isimlerin ilevlerin tanmlamasnda kullanlan isimlerle ayn olmas gerekmez. Yukarda bildirimi yaplan ilev ablonunun tanm aadaki gibi yaplabilir: template <class Type> Type min(Type t1, Type t2) { return t1 > t2 ? t1 : t2; }

Derleyicinin ablon Parametresinin Trn karmas


Bir ilev ablonuna ilikin ileve ar yapldnda, derleyici ileve gnderilen argmanlarn trlerine bakarak, ablon parametresi olan tr yerine hangi gerek trn koyulmas gerektiini anlar. Bu ileme ingilizcede "template argument deduction" denir. Bu aamada derleyici tarafndan aadaki ilemler srayla yaplr: 1. lev arsnda kullanlan her bir argmana karlk, bir ar parametresinin karlk gelip gelmedii kontrol edilir. 2. Eer uygun bir argman bulunmu ise, buna karlk gelen ablon parametresinin tr bilgisi belirlenmi olur. 3. Eer ayn ablon parametresi ilev ablonu tanm iinde birden fazla kullanlm ise, bu durumda, ilev ar ifadesindeki argmanlarn tr de benzer ekilde ayn olmaldr. Bu aklamadan sonra aadaki ilevleri inceleyelim : template<class T> void func(T a, T b) { // } int main()

221/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin { char ch = 'K'; func(ch, 'A'); // return 0; } Bu rnekte derleyici func(ch, 'A) gibi bir ar iin birinci aamada ilev ar ifadesine bakarak ch ifadesinin trnn char tr olduu anlar. lev ablonu tanmnda ablon parametresi olarak kullanlan T'nin char tr olduuna karar verir. func ilevine yaplan ar ifadesindeki ikinci argman olan 'A' da char trden bir deimez olduguna gre, ikinci ablon ar parametresinin de char trden olmas gerekir. Gerekten ikinci ar parametresi de T trnden olduguna gre aadaki ilevin retilmesi doal sonutur: void func(char a, char b) { // } Ancak yukardaki func ilev ablonunun aadaki biimde arldn varsayalm: void foo() { double d= 3.15; float f = 10.7F; func(d, f); //Geersiz! } func(d, f) ars geersizdir. Derleyici ilev ar ifadesinde yer alan birinci argmann trne bakarak T trnn double tr olduu sonucunu karr. Bu durumda ikinci ablon argman da T trnden olduuna gre, ilev ar ifadesindeki ikinci argman da double trnden olmaldr. Oysa ileve gnderilen ikinci argman float trnden olduundan, ileve yaplan ar geersizdir. Baka bir rnek verelim: template<class T> T get_max(const T *ptr, size_t size) { T max_elem = ptr[0]; for(size_t k = 1; k < size; ++k) if (ptr[k] > max_elem) max_elem = ptr[k]; return max_elem; } int main() { int a[10] = {1, 2, 3, 7, 8, 4, 5, 6, 9, 1}; double b[3] = {1.0, 2.0, 3.0}; cout << "max int = " << get_max(a, 10) << endl; cout << "max double = " << get_max(b, 10u) << endl; return 0;

222/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin }

Yukardaki ilev arlar geerlidir, derleme zamannda hata oluturmaz. Derleyici baarl bir ekilde yani ift anlamllk hatas olmadan ilev ablon parametresi olan tr, ilev ar ifadesine bakarak saptayabilir. lev ablonlarna ilikin ilevler arldnda, ileve gnderilen arguman says ile, ilev ablonunda belirtilmi parametre saysnn da uyumlu olmas gerekir. Uygun parametre yaps bulunmaz ise derleme zamannda hata oluur. Aadaki kodu inceleyin: template<class T> T sum_square(T a, T b) { return a * a + b * b; } #include <iostream> using namespace std; int main() { cout << cout << cout << cout << }

sum_square(5, 8) << endl; sum_square(2.7, 3.5) << endl; sum_square(10) << endl; sum_square(4, 7.9) << endl;

//Geerli //Geerli //Geersiz //Geersiz!

return 0;

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. Aada rnek olarak ayn trden iki nesnenin deerini takas eden swap isimli bir ilev ablonu yazlyor: template<class T> void swap(T &r, T &b) { T temp = a; a = b; b = temp; } #include <iostream> int main() { int x = 10; int y = 20; swap(x, y); std::cout << "x = " << x << "\ny = " << y << std::endl; double d = 3.75; double e = 4.50;

223/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin swap(d, e); std::cout << "d = " << d << "\ne = " << e << std::endl; return 0; } swap ilevi ablonunda referans yerine gsterici deikenler de kullanlabilirdi: template<class T> void swap(T *p1, T *p2) { T temp = *p1; *p1 = *p2; *p2 = temp; } #include <iostream> using std::cout;

int main() { int x = 10; int y = 20; swap(&x, &y); cout << "x = " << x << "\ny = " << y << std::endl; double d = 3.75, e = 4.50; swap(&d, &e); cout << "d = " << d << "\ne = " << e << std::endl; return 0; }

lev ablonundan retilecek ilevlerin parametre deikeni ya da deikenlerinin tr ya da trleri derleyici tarafndan akllca saptanr. rnein: template <typename T> void func(T p) { T *ptr = &p; // } int main() { int a[10]; func(a); return 0; }

main ilevi iinde func ilev ablonuna ilikin ilev, int trden a dizisinin balang adresi ile arlyor. lev ablonuna gnderilen argmann tr (int *) olduuna gre, derleyici T trn (int *) tr olarak belirleyerek ilevin kodunu yazar. Bu durumda ilev ablonunun iinde tanmlanan ptr isimli nesnenin tr de (int **) olur. imdi de aadaki ilev ablonunu inceleyelim:

224/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin template <typename T> void func(T f) { f(); } #include <iostream> void display_message() { Std::cout << "merhaba!" << std::endl; } int main() { func(display_message); return 0; }

main ilevi iinde func ilevi display_message ilevinin ismi ile arlyor. func ilevi bir ablona ilikindir. Bir ilev isminin, ileme sokulduunda ilevin adresine dntrldn biliyorsunuz. Bu durumda derleyici ablon parametresi olan T trn void (*)(void) tr olarak belirler. levi bu tre gre yazar. Derleyici tarafndan aadaki gibi bir ilev yazlr: void func(void (*f)()) { f(); } Derleyicinin yazd func ilevinin arlmas ile, ilevin ana blou iinde f deikeninin gsterdii ilev, yani display_message ilevi arlr.

ablon Argmanlar Bir Snf Trne Balanabilir


lev ablonuna ilikin bir ilev yaplan ar ifadesinde kullanlan argman ya da argmanlar bir snf trnden de olabilir. class Complex{ double m_r, m_i; public: Complex(double r = 0, double i = 0.): m_r(r), m_i(i){} friend Complex operator+(const Complex &r1, const Complex &r2); friend Complex operator*(const Complex &r1, const Complex &r2); // }; template<class T> T sum_square(T a, T b) { return a * a + b * b; } int main() { Complex c1(4., 7);

225/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin Complex c2(3, 8); Complex c3 = sum_square(c1, c2); return 0; } 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<typename T> void f(T); template<typename T> void g(T&); double a[20]; const int ci = 10;

imdi bu ablonlara ilikin yaplacak baz arlar ele alalm: f(a); 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:

226/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> template<class T> void display(T &a) { for (int k = 0; k < sizeof(a) / sizeof(a[0]); ++k) std::cout << a[k] << " "; std::cout << std::endl; } int main() { int a1[5] = {1, 2, 3, 4, 5}; display(a1); int a2[3] = {10, 20, 30}; display(a2); int a3[10] = {2, 4, 6, 8, 10, 12, 14, 16, 18, 20}; display(a3); return 0; }

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) arsyla, derleyici ilev ablonu ar parametresinin 5 elemanl int trden bir dizinin yerine geen referans olduu bilgisini kartr. Derleyici aslnda display(a1) display(a2) display(a3) arlar iin ayr ilev yazmtr.

Dizgelerin Argman Olarak Kullanlmas


Bir dizge bir ilev ablonuna ilikin ileve argman olarak gnderilirse, ablon parametresinin tr ne olarak belirlenir? ar parametresi bir referans deilse ablon parametresi const char * tr olarak belirlenir. Ancak ilev ar parametresi bir referans ise argman tr belirli bir uzunlukta char trden dizi tr olarak ele alnr. Dizinin uzunluu dizgeyle belirlenen yaznn uzunluundan bir fazla olarak alnr: Aadaki rnei inceleyin: template <class T> void func(T a, T b); template <class T> void foo(T &a, T &b); int main() { func("veli", "murat"); foo("ali", "can"); foo("veli", "murat"); return 0; }

//Geerli //Geerli //Geersiz

227/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin 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.

Bir lev ablonu inde Baka Bir lev ablonu arlabilir


phesiz bir ilev ablonu iinde baka bir ilev ablonuna ilikin ilev arlabilir. Bir diziyi kkten bye doru sralayan bir ilev ablonu tanmlayalm: #include <iostream> template<class T> void mswap(T &a, T &b) { T temp = a; a = b; b = temp; } template <class T> void sort(T *p, size_t size) { for (size_t i = 0; i < size - 1; ++i) for (size_t j = 0; j < size - 1 - i; ++j) if (p[j] > p[j + 1]) mswap(p[j], p[j + 1]); } int main() { int a[10] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; double b[5] = {5.4, 2.3, 3.5, 8.7, 1.9}; sort(a, 10); sort(b, 5); return 0; } Yukardaki ilev ablonlarn inceleyelim. mswap isimli ablon herhangi trden iki nesnenin deerlerinin takas edilmesini salayan ilevler retmek amacyla tanmlanyor. sort ilev ablonu ise dizileri bubble sort algoritmasyla sraya sokmak iin tanmlanyor. sort ilev ablonu iinde mswap ilev ablonuna ar yapldn gryorsunuz.

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:

228/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> template<class T> T sum_square(T a, T b) { std::cout<< "template function " << std::endl; return a * a + b * b; } int sum_square(int a, int b) { std::cout << "non template function" << std::endl; return a * a + b * b; } int main() { int x = sum_square(4, 7); return 0; }

Normal bir ilevle bir ilev ablonunun ayn isimde olabilmesi, ilev ablonlarnn zelletirilmesine yardmc olur. rnein: template <class T> void foo(T a) { //... } 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: void foo(Date date) { //... } Bu durumda foo ilevi Date trnden bir argman ile arldnda ilev ablonu tarafndan ilev retilmez. zel olarak yazlan foo ilevi arlm olur.

Belirlenmi lev ablonu Argmanlar


Derleyici bir ilev ablonuna ilikin ilev arsyla karlatnda, ilev ablonu parametresinin hangi tr olmas gerektiini karmaya alr (template argument deduction). Eer derleyici bu abasnda baarl olamaz ise derleme zamannda hata oluur. Aadaki rnei inceleyelim:

229/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin template<class T> void func(T a, T b); int main() { unsigned int uix = 10u; int y = -3; func(uix, y); //Geersiz //.. return 0; }

Yukardaki rnekte func(uix, y); ars geersizdir. nk derleyici func ilevine yaplan ar ifadesindeki birinci argmana bakarak T trnn unsigned int olduu sonucuna varr. Daha sonra kinci argmann unsigned int trden olmadn grnce bu durumda hata iletisi verir. stenirse ablon parametresinin trnn ne olduu derleyicinin karmna braklmayabilir. zel bir szdizim kural ile ilev arsyla derleyiciye dorudan bildirilebilir. Bu duruma "belirlenmi ilev ablonu argmanlar" (explicit template arguments) denir. Bu amala ilev arsnda, ilev ismini izleyen bir asal ayra iinde, ablon parametresine ilikin tr bilgisi derleyiciye aka bildirilir. Asal ayrac ilev ar ileci izler: func<unsigned int>(uix, y); Yukardaki aryla, func ilev ablon parametresinin (T), unsigned int tr olarak kabul edilmesi gerektii derleyiciye aka bildiriliyor. Bu durumda derleyici bir karm yapmaya almadan, ablondan ilev retirken T tr yerine unsigned int trn kullanr. Yani derleyici aadaki gibi bir ilev yazar: void func(unsigned int a, unsigned int b) { //.. } Belirlenmi ablon argman kullanmnn baz faydalar vardr: 1. Baz durumlarda ilev ablonu kullanm gereksiz yere, fazla sayda ilevin tanmlanmasna yol aar. rnein: template<class T> void func(T a); bildirilen func ilev ablonu aadaki deyimlerle arlrsa derleyici 6 ayr farkl ilev tanmlar: func('A'); func(10); func(10U); func(25L); func(12.f); func(3.8);

Oysa programc yukardaki tm arlarn parametresi double trden olan bir ileve yaplm olmasn isteyebilir. Ne de olsa dier doal trlerden argmanlar derleyici

230/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

tarafndan double trne dntrlebilir. Bylece programc gereksiz ilev tanmlarndan kanm olur: func<double>('A'); func<double>(10); func<double>(10U); func<double>(25L); func<double>(12.f); func(3.8);

2. Baz durumlarda da, ilev ablonu parametresinin derleyici tarafndan karm zaten olanakszdr: template <class T> T func() { //... } Yukardaki ilev ablonunda ablon parametresi olan T, ilevin geri dn deerinin tr olarak kullanlyor. Peki derleyicinin bu ablona ilikin bir ilev arsn grdnde T trnn ne olduunu karma ans var mdr? Bir de aadaki ablonu inceleyelim: template <class T> void foo() { T a; //... }

Yukardaki ilev ablonunda ise T tr, ilev ana blou iinde tanmlanan a isimli nesnenin tr olarak kullanlyor. Bu durumda da, ilev arsna bakarak derleyicinin T trnn ne olduunu anlama ans olmaz. Ancak her iki rnekte de ilev arsnda, T trnn ne olduu aka belirtilirse, derleyicinin karm yapmadan T trnn hangi tr olduunu anlamas salanabilir. yle bir ilev ablonu yazlmak istensin ki, bu ablon iki ablon parametresine sahip olsun. lev ablonu bu parametrelere gnderilecek deerlerin toplam deeriyle geri dnsn. Ancak geri dn deeri de, toplam deerini iinde tutabilecek genilikte bir trden olsun: template<class T, class U> ???? sum (T, U); ilevin geri dn deeri tr yerine ne yazlmaldr? T mi U mu? Bu rnee baklrsa her iki trn de geri dn deeri tr olmas mmkn deildir: char ch; unsigned uix; //... sum(ch, ui) sum(uix, ch) Eer ilev ablonunun tanm template<class T, class U> T sum (T, U);

231/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

biiminde ise sum(ch, ui) ilevinin arlmas durumunda derleyicinin yazaca ilev char sum(char, unsigned int); biiminde olur ki bu durumda bilgi kayb kanlmazdr. Eer ablon ilevin tanm template<class T, class U> U sum (T, U); biiminde yaplmsa sum(uix, ch) ilevinin arlmas durumunda derleyicinin yazaca ilev char sum(unsigned int, char); eklinde olur ki, yine bu durumda bilgi kayb kanlmaz olur. Sorunu zmek iin ilev ablonu tanmna nc bir ablon parametresi eklenebilir: template<class T1, class T2, class T3> T1 sum(T2 a, T3 b); unsigned int ux = 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: unsigned int ux = sum<unsigned int>('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: unsigned int sum(char a, unsigned int b); 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 <class T, class M, class U> T func(M a, U b) { T temp; //... return temp; } int main() { func<int>('A', 12); func<int, double>('A', 12);

232/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin func<int, double, double>('A', 12); //... return 0; } main ilevi iinde yaplan birinci arda func ilev ablonunun birinci parametresi olan T tr ak bir ekilde belirtiliyor. Bu durumda T tr int tr olarak seilir. M tr ve U tr derleyicinin karmna braklyor. Derleyici ilev arsna bakarak M trn char tr, U trn ise int tr olarak belirler, ilevi bu trlere gre yazar. main ilevi iinde yaplan ikinci arda func ilev ablonunun birinci ve ikinci argmanlar olan T ve M tr ak bir ekilde belirtiliyor. Bu durumda T tr int M tr double tr olarak seilir. U tr ise derleyicinin karmna braklyor. Derleyici ilev arsna bakarak U trn int tr olarak belirler, ilevi bu trlere gre yazar. nc arda ise tm ablon argmanlar aka belirleniyor. Hibir argman derleyicinin karmna braklmyor. Derleyici T trn int tr olarak, M ve U trlerini double tr olarak belirler. levi bu trlere gre yazar.

lev ablonlar ve lev Yklemesi


ablon ilevlerin kullanm ilev yklemesi ile ilikili olsa da, ablon ilevler ve ilev yklemesi ayr aralardr: 1. Ayn isme sahip ancak parametre deikeni says farkl ilevler tanmlanarak bu ilevler arlabilir. Ancak ilev ablonu sz konusu olduunda verilen ablona ilikin derleyicinin yazaca tm ilevlerin parametre deikeni says ayndr. 2. lev yklemesi mekanizmasna konu olan, ayn isimli ilevlerin kaynak kodlar farkl olabilir. Yani bu ilevler isimleri ayn olmasna karn farkl iler yapabilir. Ama bir ablondan retilen ilevlerin kaynak kodlar ayn, yalnzca parametre deikenleri trleri farkldr.

lev ablonlar da Yklenebilir


Parametrik yaplar farkl, isimleri ayn olan ilev ablonlar tanmlanabilir. Nasl ayn isimli ilevler bulunabiliyorsa, ayn isimli ilev ablonlar da bir arada bulunabilir. Tabii bu ilev ablonlarnn, parametrik yaplar, ablon parametre ya da ar parametrelerine bal olarak verilen genel imzalar birbirinden farkl olmak zorundadr. Aadaki kod parasn derleyerek altrn: inline const int &getmin(const int &a, const int &b) { return a < b ? a : b; } template <typename T> inline T const& getmin(const T& a, const T& b) { return a < b ? a : b; } template <typename T> inline const T &getmin(const T &a, const T &b, const T &c) { return getmin(getmin(a,b), c); } #include <iostream> using namespace std; int main() { cout << getmin(10, 20, 30) << endl; cout << getmin(7.0, 42.0) << endl;

233/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin cout cout cout cout cout } main ilevi iinde yaplan tm arlar tek tek ele alalm: getmin(10, 20, 30) ars iin template <typename T> inline const T &getmin(const T &a, const T &b, const T &c) ablonundan T trnn int tr olmas karmyla bir ilev retilir. getmin(7.0, 42.0); ars iin template <typename T> inline T const& getmin(T const& a, T const& b) ablonundan T trnn double tr olmas karmyla bir ilev retilir. getmin('a', 'b') ars iin template <typename T> inline T const& getmin(const T& a, const T& b) ablonundan T trnn double tr olmas karmyla bir ilev retilir. getmin(7, 42) ars bir ilev ablonuna ilikin deildir. nk inline const int &getmin(const int &a, const int &b); biiminde bir ilev bilidirmi yaplmtr. Gerek ilevin ilev ablonuna ncelii vardr. getmin<>(7, 42) ars gerek ileve deil ilev ablonuna ilikindir. nk ilev arsnda ilev ismini ii bo bir asal ayra izliyor. 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) << << << << << getmin('a', 'b') << endl; getmin(7, 42) << endl; getmin<>(7, 42) << endl; getmin<double>(7, 42) << endl; getmin('a', 42.7) << endl;

return 0;

234/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

lev ablonunun Belirlenmi Bir Tr in zelletirilmesi


Yazlan bir ilev ablonu tm veri trleri iin uygun dmeyebilir. Belirli bir tr iin, retilecek ilevin, ablonda verilen algoritmaya gre deil de verilecek baka bir tanma gre yazlmas istenebilir. Buna ilev ablonunun bilinli zelletirilmesi (explicit specialization) denir. Aadaki rnei inceleyelim: template<class Type> Type max(Type a, Type b) { return a > b ? a: b; } #include <iostream> int main() { const char *p1 = "Mehmet"; const char *p2 = "Ahmet"; std::cout << max(p1, p2) << std::endl; return 0; }

max ilev ablonu kendisine gnderilen iki nesneden byk olann deeri ile geri dnen ilevlerin retiminde kullanlabilir. Byklk ilikisi char, int, double gibi doal trler iin aktr. Yukardaki ilev ablonun kullanlmasnda bir hata sz konusu olmaz. Ancak iki dizgenin daha bynn bulunmas iin bu ablonun kullanlmas doru olmaz. Yukardaki genel algoritma karlatrmay > ileci ile yapmaktadr. ki dizge > ileci ile karlatrldna karlatrlan yalnizca dizgelerin yerletirildii bellek bloklarnn adresidir. phesiz istenilen dizgelerin szlkteki konumlarna ilikin (lexicographical) bir karlatrmadr. C++ dili byle durumlarda ilev ablonu iin tre bal zel ilev tanmlar yaplabilmesine izin verir. Bu mekanizmaya "lev ablonunun belirlenmi bir tr iin zelletirilmesi" (Template Explicit Specialization) denir. Aadaki kodu inceleyin: template<class Type> Type tmax(Type a, Type b) { return a > b ? a: b; } template<> const char *max<const char *>(const char *p1, const char *p2) { return (strcmp(p1, p2) > 0 ? p1 : p2); } #include <iostream> int main() { int i = max(50, 10); std::cout << "i = " << i << std::endl; const char *p1 = "Necati"; const char *p2 = "Ergin";

235/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin std::cout << "max = " << max(p1, p2) << std::endl; return 0; }

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.

lev ablonlarnda Szdizim Kontrolleri


Derleyicilerin ou, ilev ablonlarnda szdizim kontroln iki ayr aamada gerekletirir. Birinci kontrol henz ilev ablonu arlmadan yaplr. Bu aamada baz temel kontroller yaplr. Hibir biimde ve olaslkta geerli olamayacak durumlar daha bu aamada derleme zaman hatas olarak belirlenir. Gerek szdizim kontrol ilev ablonuna ilikin bir aryla karlaldnda, yani derleyicinin gerek ilevi yazmas srasnda yaplr. Aadaki rnei ele alalm: template<typename T> void func(T p) { p.foo(); } int main() { func(10); return 0; }

//Geersiz

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 olmayan bir trden argmanla arldnda, derleyici ilev arsn bir hata olarak belirler. Derleyicinin szdizim kontrollerini hangi aamalarda yapaca standart olarak belirlenmemitir. Baz derleyiciler ilev ablonuna ilikin bir ar ile karlamadka neredeyse hi bir szdizim geerlilik kontrol yapmaz.

lev ablonlarnn Yeri


lev ablonlar kod dosyasna, yani .cpp dosyasna, yerletirilemez. Derleyicinin her kaynak dosyay derlemesi srasnda , ilev ablonu tanmlamalarn yeniden grmesi gerekir. Projenin birden fazla modlden olumas durumunda, bamsz derlenen her modlde, eer bu ilev ablonu arlm ise tanmlamasnn derleyici tarafndan her modlde grlmesi gerekir. Farkl modllerde ayn ilev ablonu kullanlyorsa bu ilev ablonu tanmlamalarnn tamamen zde olmas gerekir. lev ablonlarnn balk dosyalarnda tanmlanmas, ablonun kullanld her modle bu balk dosyasnn eklenmesi en iyi zmdr. lev ablonu tanmlamalar, static anahtar szc ile balatlmamsa d balantya sahiptir. Bu durumda rnein, ayn ablon ilev ayn almla hem a.cpp hem de b.cpp dosyasndan kullanlm olsun. Derleyici ayn alm hem a.obj hem de b.obj iine yazar.

236/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Ancak balayc bunlardan yalnzca bir tanesini alan koda yerletirir. Yani farkl modllerden ayn ilev ablonu kullanlm olsa bile, alan dosyada bir tane bulunur.

lev ablonlarnda Varsaylan Argmanlar


Normal ilevlerde olduu gibi, ilev ablonlar da varsaylan argmanlar alabilir. Varsaylan argman olarak kullanlan ifade ablon parametresine bal olabilecei gibi ablon parametrelerinden bamsz da olabilir. Aadaki ablonu inceleyelim: template <typename T> void functemp (T &r, T val = 0) { r = val; } Yukarda tanmlanan functemp isimli ablondan retilecek bir ileve tek bir argman geildiinde, ilevin ikinci parametresi olan val deikenine otomatik olarak 0 deeri kopyalanr. imdi de aadaki ablonu inceleyin: template<typename T> void ftemp (T* ptr, const T & val = T()) { *ptr = &val; // } Tanmlanan ftemp isimli ablondan retilecek bir ileve, tek bir argman geildiinde ilevin ikinci parametresi olan val referans varsaylan kurucu ilevle yaratlacak bir geici nesneye balanr. Eer T tr doal veri trlerinden biri olarak belirlenirse bu referans deeri 0 olan bir geici nesneye balanr. Daha nce deindiimiz szde balang ilevi (pseudo constructor) kuraln anmsayn. Varsaylan argman olarak kullanlan ifadeye dayanlarak ablon parametresinin hangi tre ilikin olduu bilgisi karlamaz. Aadaki rnei inceleyin: template <typename T> void func (T x = 42); int main() { func<int>(); func(); return 0; }

// Geerli. T int tr olarak alnr. // Geersiz. T trnn ne olduu bilgisi karlamaz.

typename Anahtar Szc


Bir ilev ablonunun parametresinin bildiriminde class veya typename anahtar szcklerinin kullanlabileceini biliyorsunuz. Ancak typename anahtar szcnn ikinci bir kullanm vardr. Burada class anahtar szc typename anahtar szcnn yerine kullanlamaz. Bir ilev ablonu iinde ablon parametresine bal olarak bir tr bilgisinin kullanldn dnelim: template <class T> void func(T val) { T::Dollar sum; // }

//Geersiz

237/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki ilev ablonu ana blou iinde T::Dollar bir tr bilgisi olarak kullanlyor. Burada typename anahtar szcnn kullanlmas zorunludur. typename T::Dollar sum; deyimiyle tanmlanan sum T tr hangi snfa ilikin ise, o snf trnn iinde bildirilmi olan Dollar trnden bir nesnedir.

ablonlarn Tr Belirtilen parametreleri ve Tr Belirtmeyen Parametreleri


ablonlarn, ablon parametreleri tr belirten (type parameter) ya da tr belirtmeyen (non type) eklinde olabilir. Parametre yazlrken class ya da typename anahtar szc kullanlrsa parametrenin aslnda herhangi bir tr bilgisi ifade eden bir anlama geldii varsaylr. Fakat class ya da typename yerine int, long gibi doal trlere ilikin bir tr kullanlrsa bu durumda ablon parametresi tr belirtmez. O trden bir "deimez ifadesi" belirtir. Bu durumda ablon ileve ar yaplrken, bu parametreler iin bir deimez ifadesi kullanlmas gerekir. rnein: #include #include #include #include <iostream> <algorithm> <cstdlib> <ctime>

template <class T, size_t size, class R> void display(R fptr) { T a[size]; for (int k = 0; k < size; ++k) a[k] = fptr(); sort(a, a + size); for (int k = 0; k < size; ++k) std::cout << a[k] << " "; std::cout << "\n********************************" << std::endl; } double drand() { return static_cast<double>(rand()) / RAND_MAX; } int main() { srand(time(0)); display<int, 100>(rand); display<double, 10>(drand); return 0; }

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:

238/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

Standart C++ ablon Ktphanesi Nedir


STL (Standard Template Library) ilk kez 90 l yllarn balarnda kullanlmaya balamtr. Daha sonra alnan bir kararla STL'in C++'n standart ktphanesi yaplmasna karar verilmitir. 1998 standartlaryla eitli eklentilerle STL, C++n standart ktphanesi yaplmtr. STL ilev ve snf ablonlarndan oluur. Bu ilev ve snf ablonlarnn tanmlar eitli balk dosyalarnn iine yerletirilmitir. rnein, <algorithm> isimli balk dosyasnda ok sayda standart ilev ablonu vardr. STL deki tm ilev ve snf ablonlar std isim alan iinde tanmlanmtr. Bu nedenle programcnn bunlar kullanrken, std isim alann dikkate almas gerekir. Ktphanedeki global ilev ablonlar algoritma olarak isimlendirilir. Snf ablonlar ise farkl veri yaplarnn kodlanmas amacyla tanmlamtr. Algoritmalar iterator kavram kullanlarak snf ablonlaryla btnletirilmitir. teratrler, nesne tutan snflar zerinde baz genelletirilmi ilemlerin yaplmasn salayan gstericiler ya da akll gstericilerdir (smart pointers).

rnek STL lev ablonlar


STL'de ok sayda ilev ablonu bulunur. Bu ilev ablonlar, yaptklar ilere gre eitli gruplara ayrlmtr. yi bir STL kullanm iin zel bir eitim gerekir. Bu ilev ablonlarnn kullanlabilmesi iin yaplmas gereken o ablonun bulunduu balk dosyasn kaynak koda eklemektir. STL ilevlerinin byk ounluu istenilen bir veri yaps zerinde (dinamik dizi, bal liste, ikili aa) ilem yapmaktadr. Diziler de bir veri yaps olarak ele alndklarndan, bir ok STL ilev ablonu diziler zerinde de ilem yapabilir. Bu ilevler daha sonra ayrntl olarak ele alacamz iterator mantna dayandrlmtr. Algoritmalarn ou bir veri yapsnda bulunan ilk elemannn ve sondan bir sonraki elemannn konum bilgisini alacak biimde dzenlenmitir. Algoritmalarn ou normal diziler iin de kullanlabilir. Normal bir dizi sz konusu olduunda, dizinin ilk elemannn ve son elemandan bir sonraki elemann adresi algoritmaya geilerek, algoritmann dizinin tm elemanlar zerinde i yapmas salanabilir. Aada STL'de bulunan baz ilev ablonlar rnek olarak veriliyor. STL blmnde tm ilev ablonlarn tek tek ele alacaz.

sort lev ablonu


Elemanlar bellekte ardl olarak yer alan bir veri yapsnda tutulan elemanlar sralamak iin kullanlr. ablonun bildirimi aadaki gibidir: template<class RandomIt> void sort(RandomIt first, RandomIt last); Herhangi trden bir dizi ya da dinamik bir dizi sz konusu olduunda ilevin first ve last isimli ar parametrelerine, sralanacak dizinin ilk elemannn ve son elemandan bir sonraki nesnenin adresi geilmelidir.

find lev ablonu


find ilev ablonu ile bir veri yapsnda bir deer sral bir biimde aranr, aranan deer bulunursa bulunduu yerin konum bilgisi ile bulunamaz ise son elemandan bir sonraki elemann konum bilgisiyle geri dnlr:

239/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin template<class InputIt, class ValueType> InputIt find(InputIt first, InputIt last, const ValueType& val);

copy lev ablonu


copy ilev ablonu bir veri yapsndaki elemanlar bir baka veri yapsna kopyalamak iin kullanlr. lev, kopyalanacak veri yapsndaki ilk ve sondan bir sonraki elemann konum bilgilerini ve kopyalamann yaplaca hedef veri yapsndaki ilk elemann konum bilgisini alr. lev hedef veri yapsndaki sondan bir sonraki elemann konum bilgisiyle geri dner: template <class InputIt, class OutputIt> OutputIterator copy (InputIt sourceBeg, InputIt sourceEnd, OutputItdestBeg);

random_shuffle lev ablonu


Bellekte bitiik olarak yer alan bir veri yapsnda tutulan elemanlar kartrmak iin kullanlr. Kartrma ilemi rastgele elemanlarn yer deitirmesiyle yaplan bir ilemdir. template<class T> void random_shuffle(RandomIt first, RandomIt last);

for_each lev ablonu


Bir veri yapsndaki tm elemanlarn belirli bir ilemden geirilmesi amacyla kullanlr. ablonun bildirimi aadaki gibidir: template <class InputIt, class UnaryProc> UnaryProc for_each (InputIt beg, InputIt end, UnaryProc op) Veri yapsnn (first last) aralndaki tm elemanlar op ar parametresiyle belirlenen ileve argman olarak gnderilir. Aada rnek olarak verilen ilev ablonlarna ilikin rnek bir kod veriliyor: template <typename T> void display(T begin, T end) { for (T iter = begin; iter != end; ++iter) std::cout << *iter << " "; std::cout << endl; } void func(int &r) { r %= 5; } #include <iostream> #include <algorithm> #include <cstdlib> int main() { const int size = 20; int a[size]; for (int k = 0; k < size; ++k) a[k] = rand() % 100; cout << "a dizisi yazdrlyor" << endl; display(a, a + size); sort(a, a + size); cout << "a dizisi sraland" << endl; cout << "a dizisi yazdrlyor" << endl;

240/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin display(a, a + size); random_shuffle(a, a + size); cout << "a dizisi kartrld\na dizisi yazdrlyor" << endl; display(a, a + size); cout << "dizide aranacak deeri girin : "; int val; cin >> val; int *ptr = find(a, a + size, val); if (ptr == a + size) cout << "dizide " << val << " degeri bulunamad!" << endl; else { cout << "bulundu!" << endl; *ptr *= *ptr; cout << "a dizisi yazdrlyor" << endl; display(a, a + size); } int b[size]; copy(a, a + size, b); cout << "b dizisi yazdrlyor" << endl; display(b, b + size); for_each(b, b + size, func); cout << "for_each ilevinden sonra b dizisi yazdrlyor" << endl; display(b, b + size); return 0; }

241/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ALIMA ZAMANI HATALARININ YAKALANMASI VE LENMES


alan programlarda herey beklendii gibi gelimeyebilir. Oluturulan programlarn almasnda beklenmedik durumlar ortaya kabilir. rnein programn almas annda bir dosyann almas gerekiyor iken eitli nedenlerden bu dosya alamayabilir. Heap alanndan (C++ dilindeki ismiyle free store'dan) yaplmaya allan bir dinamik yer ayrma ilemi, yeteri kadar bo alan olmad iin baarsz olabilir. Program kullanan kiinin girdii veri ya da veriler, programn bekledii dorultuda, kabul edilebilir snrlar iinde olmayabilir. Program iinde beklenmedik bir verinin ilenmesi sonucu sfra blme ilemi yaplyor olabilir. Bir dizinin bir elemanna bir atama yaplmak istenirken, ayrlm olan bir bellek alannn dna yanllkla erimek sz konusu olabilir. Buna benzer beklenmedik durumlara C++ programclnda "exception" denir. Bir ilev iinde byle bir hata olutuunda ne yaplmaldr? Eer ne yaplmas gerektii ak bir ekilde biliniyorsa, bir hatann olumas durumunda yaplmas gerekenler yaplabilir. Ancak temel sorun udur: Darya hizmet vermek amacyla tanmlanan bir ilev, ne durumda arlm olduunu bilemez, yani ou zaman tanmlanan ilevin iinde bir hata durumu olutuu saptanabilse de, ne yaplmas gerektii konusunda yeterli bilgi yoktur. Hata olutuu zaman ne yaplmas gerektiine ou zaman, iinde hata oluan ilevi aran kodlar tarafndan karar verebilir. O zaman iinde hata oluan ilevin sorumluluu hata durumunu kendisini aran ilevlere bildirmektir. inde hata oluan kod, kendisini aracak ilevleri bu durumdan haberdar etmelidir. Bylece bu ilevi arm olan ilevler, hata durumu hakknda bilgi sahibi olduklarnda, gerekirse bir nlem alma ansna sahip olur. Peki bir ilev iinde bir alma zamannda beklenmedik bir durumun olumas durumunda ne yaplabilir? Programc bu durumda, program kullanann bir zarar grmemesi iin ne gibi nlemler alabilir? te exception handling (alma zaman hatalarnn yakalanmas ve ilenmesi) C++ dilinin doal aralarndan biri olan ve bu konuda kullanlan bir mekanizmadr. C++ dilinin hatalarn yakalanmas ve ilenmesi mekanizmasn yakndan tanmaya balamadan nce, bu konuda kullanlan geleneksel aralar inceleyelim ve nesne ynelimli programlama tekniinde bu aralarn neden yetersiz kaldn grelim. C++ dilinin ilk dnemlerinde dilin kendi yaps iinde alma zaman hatalarnn yakalanmas ve ilenmesi mekanizmas yoktu. Bu ama iin C dilinde de kullanlan geleneksel yntemler kullanlyordu. Bu yntemler 3 ana grup altnda toplanabilir: 1. levlerin, yaptklar i konusunda baarl ya da baarsz olduklarn gsteren bir geri dn deeri retmeleri (Bir hata deerinin geri dndrlmesi). 2. Baarszlk durumunda global bir bayrak deikene ilgili hatay belirleyen ayrt edici bir deer atanmas (hata deeri) ve dier ilevlerin bu global deikenin deerini snyarak nlem almalar. 3. Programn almasnn sona erdirilmesi. Nesne ynelimli programlama sz konusu olduunda her yntemin de ok nemli yetersizlikleri ve sakncalar vardr. Bu yetersizlik ve sakncalardan bir ksm zellikle ok byk projeler sz konusu oldugunda, kabul edilebilir olmaktan kar. imdi bu sakncalarn neler olabilecei konusuna deinelim:

Bir Hata Deerinin Geri Dndrlmesi


Bu teknik kk programlarda belirli bir yere kadar kullanlabilir. levler bir hata durumunda, daha nceden belirlenmi olan hata deerlerine geri dner. arlan ilevlerin geri dn deerleri snanarak bir hata oluup olumad, ve ne tr bir hatann olutuu saptanr. Ancak bu tekniin nemli dezavantajlar vardr. Geri dn deeri

242/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

olarak verilen hata deerleri bir standarda bal deildir. Bir ktphanede rnein 0 deeri baarszlk belirtirken baka bir ktphanede 0 deeri baar durumunu gsterebilir. C dilinin standart ilevleri iin bile bu konuda bir birlik olduunu syleyemeyiz. Dosya aan standart C ilevi olan fopen ilevinin 0 (NULL) deeri dosyann baarl bir ekilde alamadn gsterirken, dosyay kapayan fclose ilevinin 0 olan geri dn deeri dosyann baarl bir ekilde kapatldn gstermektedir. C dilinde uygulama gelitirenler genellikle ilevin baar ya da baarszlklarn gsteren geri dn deerlerini simgesel deimezler biiminde tanmlayarak balk dosyas iine yerletirir. Ancak phesiz bu simgesel deimezler de standart bir hale getirilmemitir. Ayr ekipler tarafndan farkl zamanlarda gelitirilmi ktphaneler, ayn proje iinde kullanldnda, hata durumlar sz konusu olabilir. levlerin geri dn deerlerinin farkl biimlerde yorumlanmas karklklar yaratr. Ayrca her bir geri dn deerinin snanmas ve yorumlanmas, zahmetli ve zaman alc bir ilemdir. ou zaman bunun iin ek bir koda gereksinim duyulur. lev her arldnda geri dn deeri mutlaka ilenmelidir. Geri dn deeri snanmadan kullanlrsa, baz durumlarda alan program bile kebilir. rnein malloc ilevinin geri dn deerinin, snanmadan kullandn dnelim. malloc ilevi baarsz olduunda NULL adresine geri dner. Bylece bir gsterici hatasna neden olunur. arlan bir ilev iinde bir hata durumu oluursa, arlan ilevin yrtlmesi, normal olarak retecei geri dn deeri ifadesine gelmeden nce, hata olutuu bilgisini veren bir baka return deyimiyle sonlandrlr. Baka bir kod da ilevin geri dn deerini snayan ve ilev herhangi bir hata deeri geri dndrmse, programn devam edip etmemesi hakknda karar veren ek kod parasdr. Bu da bir ok durumda yazlan kodun boyutunu byk lde artrr ayrca programn okunabilirliini bozar, programn gelitirilmesi ve test edilmesi konusunda sakncalar yaratr. Baz durumlarda arlan ilev iinde bir baarszlk ya da hata olutuunda, bu durumu darya iletecek zel bir hata deeri kullanlamaz. rnein bir adrese geri dnen bir ilev, baarszlk ya da bir hata durumunda NULL adresine geri dnebilir. Ama hesap edilmek istenen bir tamsay deerine geri dnen bir ilev, bir hata durumu olutuunda hangi deere geri dnebilir? nk ayrt edici ve hatay gsterebilecek bir deer sz konusu deildir. C dilinden tandmz atoi ilevini ele alalm: int atoi(const char *str); atoi ilevi adresini ald yazdan, karakterler olarak kodlanm int trden bir deeri ekerek geri dn deeri olarak iletir. Peki ya yaz bir int deeri gsteren geerli bir yaz deilse atoi ilevi hangi deer ile geri dnebilir? Derleyicilerin ou bu durumda atoi ilevi 0 deeriyle geri dndrr. Bu da pheli bir durum yaratr. Peki atoi ilevine gnderilen yaznn iinde gerekten "0" varsa ve bu deerin elde edilmesi gerekiyorsa? nemli bir nokta da C++ dilinde baz ilevler iin geri dn deeri diye bir kavramn sz konusu olmamasdr. rnein kurucu ilevler (constructors), tr dntrme ilevler iin geri dn deeri diye bir kavram sz konusu deildir. Bu ilevler iinde oluabilecek hata durumu yukarda anlatlan geleneksel teknikle yakalanamaz.

Global Bir Bayrak Deikene Deer Atanmas


alma zaman hatalarnn bildirilmesinde alternatif bir teknik de global bir deikene atama yaplmasdr. Global bir deikenin deeri yaplan son ilemin baars hakknda bir fikir verir. C dilinde hata durumunda geri dn deeri mekanizmas standart bir hale getirilmemitir ama global bir deikene atama yaplmas mekanizmas standart bir hale getirilmitir. C dilinin standart bir balk dosyas olan <errno.h> dosyas global bir deiken olan errno deikenine deer atanmas konusundaki mekanizmay tanmlar. Standart ilevler iinde bir hata durumu olutuunda errno deikenine ayrt edici bir deer atanr. Bu yntemin de nemli sakncalar vardr: ok ilemli bir almada (multithread) bir ak iindeki ilev ars yznden oluan bir hata durumu, ilgili global deikenin deeri daha snama amacyla alnamadan baka bir ak tarafndan tekrar deitirilebilir. Ayrca byle bir hata kodunun global bir deikenden srekli olarak alnarak okunmas ok disiplinli bir programlama teknii gerektirmektedir.

243/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Global bir deikene yaplan atama ya da ilevin geri dn deerinin snanmas ilev iinde oluan bir hatay ilevi arana iletebilir ancak bu hatann ortadan kaldrlmas konusunda bir ey yaplmaz. rnein dosya aan bir ilev dosyann alamadn darya bildirebilir ancak baka bir ilevin alamam olan bu dosyaya bir veri aktarmas ve bu dosyay kapatmas konusunda bir nlem alamaz. Dahas errno gibi bir deikenin deeri alndktan sonra, snamay yapan kii tarafndan tekrar sfrlanmas gerekir ki, bundan sonra saptanacak hata bildirimlerinde bir yanllk olumasn. Eer programc global deikeni tekrar normal deerine getirmeyi unutursa baka bir ilevin baars snandnda, ilev aslnda baarl olmu iken programc tarafndan ilevin baarsz olduu gibi yanl bir yargya varlabilir. Bylece gereksiz bir ekilde, olmayan bir hata ilenmeye allr ki bu da phesiz beklenmeyen sonular yaratabilir.

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. void exit(int status); 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: int atexit(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

244/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

durumda kayt edilen ilevler, kayt edilmelerine ters srada arlr. Aadaki program derleyerek altrnz: #include <iostream> #include <cstdlib> using namespace std; void func1() { cout << "func1()" << endl; } void func2() { cout << "func2()" << endl; } void func3() { cout << "func3()" << endl; } int main() { atexit(func3); atexit(func2); atexit(func1); exit(EXIT_FAILURE); }

abort levi
abort ilevi de cstdlib balk dosyasnda bildirilmitir: void abort(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 yle srdrmesi istenebilir. Ancak dinamik yer ayrma ileminin baarszlk nedeni

245/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

tamamyla donanmsal da olabilir. Bu durumda daha ciddi bir nlem alnmas gerekir. Yani hatann ne nedenle olup olmad programn sonlandrlaca m yoksa bir nlem alnarak srdrlecei mi konusunda bir seim ans verilmelidir. Baz durumlarda exit ya da abort ilevleriyle programn ak sonlandrlabilse de nesne ynelimli programlama ortamnda, programn ak bu ilevler ile sonlandrlmamaldr, zira bu ilevler C++ dilinin nesne tabanl programlama modeli hakknda bilgi sahibi deillerdir. C++ dilinde bir snf nesnesi, kurucu ilev ya da herhangi bir ye ilevi kanalyla bir takm kaynaklar tutabiliyor olabilir. Bu kaynaklar, dinamik olarak elde edilen bloklarn balang adresleri, dosyalara ilikin handle deerleri, iletiim amacyla portlara ilikin set edilmi deerler, giri k birimlerine ilikin aygtlar, kilit kontrolleri vs. olabilir. Birok durumda bu kaynaklarn, nesnenin yapt i bitince serbest braklmas ya da geri verilmesi gerekir. C++ dilinde genel olarak bu kaynaklar nesneye ilikin sonlandrc ilev yardmyla geri verilirler ya da serbest braklr. Yerel snf nesneleri, bilinirlik alanlar sona erdiinde otomatik olarak serbest braklr. Ancak ne exit ne de abort ilevi yerel nesnelerin sonlandrc ilevlerinin arlmasn salar. Bu nedenle programlarn bu ilevler ile ani olarak sonlandrlmas, geri dn olmayan zararlarn olumasna neden olabilir. Bir veri taban sistemi tamamyla bozulabilir, dosyalar kaybedilebilir, deerli veriler kaybolabilir. Nesne ynelimli programlamada exit ve abort ilevleri kullanlmamaldr! Grld gibi, C dilinin hata saptamasna ve ilenmesine ilikin geleneksel yntemlerden hibiri C++ dili iin yeterli deildir. C++ dilinin kullanlmasndaki ana temalardan biri byk projelerin C diline gre daha iyi ve daha gvenli bir ekilde yazlmasna olanak salamasdr. C++ dilinin dinamik gelime sreci iinde C++ dilinin yaratclar alma zamanna ilikin hatalarn yakalanmasna ve ilenmesine ilikin yeterli bir mekanizmann eksikliinin farknda olduklarndan, yukardaki sakncalar iermeyen bir yntemin arayna girdiler. nerilen sistem, beklenmeyen bir durum olutuunda programn aknn otomatik olarak, hatay ileyecek bir kod parasna akn salanmasyd. Sz konusu yntem basit ve etkili olmalyd, bir takm deerlerin srekli olarak test edilmesi ilkesine dayanmamalyd. Fakat en nemlisi, bir hata olutuunun saptanmas durumunda, hatay ileyen kodun bu durumdan otomatik olarak haberdar olmasnn salanmasyd. Ayrca bir hata yerel olarak kendi kod grubu iinde ilenemediinde, o kapsamdaki yerel snf nesnelerinin tutmakta olduu kaynaklar, daha genel bir hata ileyicisinin devreye girmesinden nce, sisteme iade edilmeliydi. Uzun sren incelemeler ve aratrmalar sonucunda alma zamannda oluan hatalarn yakalanmasna ve ilenmesine ilikin bir mekanizmaya ynelik tasar 1989 ylnda kabul edilerek C++ diline eklendi. C++ ncesinde baka programlama dilleri bu ynde mekanizmalar oluturmutu. rnein daha 1960'l yllarda PL/1 dili dilin kendi yaps iinde var olan bir hata ileme mekanizmasna sahipti. Yine 1980'li yllarn balarnda Ada dili byle bir mekanizmay kendi bnyesi iine katmt. Fakat bu mekanizmalar C++ dilinin nesne ynelimli programlama yaklamna uygun dmyorlard. Bu yzden C++ diline eklenen mekanizma kendi tr iin ilkti ve kendisinden sonra gelitirilen bir ok programlama dili tarafndan kullanld. alma zamannda hatalarn yakalanmasna ve ilenmesine ilikin bir mekanizmann programclara salanmas derleyiciyi yazanlara eitli zorluklar getirir. rnein ilk C++ derleyicisi olan cfront UNIX iletim sistemi altnda alyordu. Bir ok UNIX derleyicisi gibi cfront da nce C++ kaynak kodunu C koduna dntryor daha sonra elde edilen C kodunu derliyordu. cfront derleyicisinin 4.0 srmnn hata ileme mekanizmasna sahip olmas planlanmt. Ancak btn gerekleri salayan bir hata ileme mekanizmasnn gelitirilmesi o kadar karmak bir prosesti ki, 4.0 srmn gelitiren ekip, almaya balamasndan 1 yl sonra projeyi sonlandrma karar ald. cfront 4.0 srm hi olmad. Ancak daha sonra hata ileme mekanizmas C++ dilinin doal bir arac durumuna getirildi. Daha sonraki derleyiciler daha sonra bu arac desteklemeye balad.

246/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

alma Zaman Hatalarn Saptama ve leme Mekanizmasn mplemente Etmek Neden Zordur?
Mekanizmay bir derleyicinin implemente etmesindeki birinci zorluk oluan belirli hatay ileyecek uygun bir kodun (handler) bulunmasdr. kinci zorluk, hatay saptayan kod parasnn hatay ileyecek koda gnderecei hata nesnesinin ok biimli zelliklere sahip olabilmesidir. Yani tremi snf trnden bir hata nesnesini ileyecek olan uygun bir ileyici kod bulunamad zaman taban snf trnden nesneyi alabilecek bir hata ileme kodunun devreye girebilmesidir. Ancak ilk zamanlarda, ok biimlilii destekleyen bir nesnenin alma zamanndaki trn saptayacak bir mekanizma (Dynamic typing) C++ diline eklenmemiti. Hata ileme aracnn yeterli bir duruma getirilebilmesi iin nce dinamik tr kontrol mekanizmasnn sfrdan gelitirilmesi gerekiyordu. Baka bir komplikasyon da hatann ilenmesi durumunda, hata ileyici ileve programn akn ynlendirmeden nce yerel snf nesnelerinin sonlandrc ilevlerinin arlmasnn salanmasyd. Bu mekanizmaya ngilizcede yn dengelenmesi (stack unwinding) denir.

alma Zaman Hata leme Mekanizmasnn Uygulanmas


Exception Handling esnek ve karmak bir aratr. C dilinde kullanlan geleneksel hata ileme yntemlerinin sakncalarn iermez ve ok eitli alma zaman hatalar iin kullanlabilir. Ancak bir kaynak kod iinde, C++ dilinin dier aralar gibi bu ara da kt kullanlm olabilir. Bu mekanizmay etkili bir ekilde kullanabilmek iin, mekanizmay iyi bir ekilde tanmak ve geri planda makina dzeyinde neler olduunu anlamak gerekir.

alma Zaman Hata leme Mekanizmasnn Bileenleri


CZHI mekanizmasnda, alma zaman iinde bir hata olutuunda programn ak bu hatay ileyecek uygun bir kod parasna (handler) ynlendirilir. Bu ynlendirmeyle birlikte hatay ileyen koda bir nesne deerle (call by value) ya da adresle (call by reference) geilir. Sz konusu nesneye bundan sonraki blmlerde "hata nesnesi" (exception object) diyeceiz. Hata nesnesi doal veri trlerinden olabilecei gibi, bir snf trnden de olabilir. Mekanizmann drt bileeni vardr. try blou try blou ile ilikilendirilen catch bloklar (hata ileyen kodlar -handler) throw ifadesi hata nesnesinin kendisi

Mekanizma kabaca yle zetlenebilir: alma zaman iinde bir hata olutuunda bu durumda throw ifadesiyle bir hata nesnesi gnderilir. Gnderilen bu hata nesnesini hata ileyen kodlardan uygun olan bir tanesi yakalar. Eer hata yakalanamaz ise standart abort ilevi arlarak program sonlandrlr.

throw Anahtar Szc ve Hata Nesnesinin Gnderilmesi


throw C++ dilinin bir anahtar szcdr. throw anahtar szcn bir ifade izler. throw anahtar szcn izleyen ifade gnderilen hata nesnesinin trn ve deerini belirler. Gnderilen hata nesnesini bir hata ileyen kod paras yani ingilizce ismiyle bir handler yakalar. throw ifadesi herhangi bir ilevin iinde yer alabilir. Aadaki kod parasn inceleyin:

247/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin int &Array::at(int index); { if (index >= m_size) throw index; return pd[index]; }

Yukarda Array snfnn at isimli ye ilevinin tanm yer alyor. levin kaynak kodu iinde, dardan gelen index deerinin geerli olup olmad snanyor. index deeri geerli deilse, int trden bir hata nesnesi gnderiliyor.

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" int main() { Array myarray(10); int index; std::cout << "bir index degeri girin "; std::cin >> index; try { int x = myarray.at(10); } //... return 0: } Yukardaki main ilevinde arlan at ilevi iinden bir hata nesnesi gnderilebilecei iin int x = 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.

catch Anahtar Szc ve catch Bloklar


catch C++ dilinin bir anahtar szcdr. catch anahtar szcn alan ve kapanan bir ayra izler. Bu ayra iinde, ilevlerdeki parametre deikeni bildirimine benzer bir ekilde deiken bildirimi yaplabilir. Ayralardan sonra yine bir blok yerletirilir. catch anahtar szcn bir ayracn izlemesi ve ayracn kapanmasndan sonra da bir bloun yerletirilmesi szdizimsel bir zorunluluktur. catch blou hatay ileyen bloktur(handler). catch bloklar try bloklarn izler. Bir try bloundan sonra herhangi bir catch blounun yer almamas geersizdir. Eer bir catch ayrac iinde tanmlanm nesnenin tr ile gnderilen hata nesnesinin tr tamamen ayn ise, catch parametresine gnderilen hata nesnesinin deeri aktarlr. Programn ak catch blou iinde akmaya ynlendirilir. Bu duruma "gnderilen hata nesnesinin yakalanmas" denir. Bir try blou ayr bir bilinirlik alan belirtir. Bu blok iinde tanmlanan nesneler try blounu izleyen catch bloklar iinde bilinmez.

248/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

try blou ile catch blou arasnda baka bir kod olamaz. Bir try bloundan sonra istenilen sayda catch blou yer alabilir. imdiye kadar anlatlanlar bir kod paras ile rnekleyelim: #include <iostream> #include <cstdlib> using namespace std; int main() { int val; cout << "pozitif bir sayi giriniz : "; cin >> val; try { if (val < 0) throw val; } catch(int x) { cout << x << " negatif! program sonlaniyor!" << endl; exit(EXIT_FAILURE); } cout << val << " degeri girildi!" << endl; return 0; }

Bir try blounu birden fazla catch blou izleyebilir. Bu durumda catch parametrelerinin trlerinin farkl olmas gerekir. Ayn trden nesne bekleyen birden fazla catch parametresinin bulunmas geersizdir. Aadaki rnei inceleyin: #include <iostream> void may_raise_exception(); int main() { try { may_raise_exception(); } catch (int) { //... } catch (double) { //... } catch (long) { //... } std::cout << "programn akisi bu noktadan devam edecek!" << std::endl; //... return 0; }

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,

249/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

throw fadesiyle catch Parametresinin Uyumu


Bir catch blounun gnderilen hata nesnesini yakalayabilmesi iin throw ifadesi ile catch parametresinin trlerinin tamamen ayn olmas gerekir. catch parametresinin nnde bulunan const ya da volatile niteleyicileri tam uyumu bozmaz. Ancak dier otomatik tr dnm ve ykseltme kurallar burada geerli deildir. rnein throw ifadesi char trden catch parametresi int trden ise, tamsayya ykseltme (integral promotion) kural iletilmez. Benzer ekilde gnderilen hata nesnesinin trnn float tr olduunu kabul edelim. Parametresi double trden olan bir catch blou float trden bir hata nesnesini yakalayamaz.

Gnderilen Hata Nesnesinin Bir Snf Trnden Olabilmesi


inde alma zamann hatasnn oluabilecei kod parasnn, hatay ileyecek kod parasna gnderdii hata nesnesi, bir snf trnden de olabilir. Bu durumda phesiz bu hatay yakalayacak catch blounun parametresi ayn snf trnden bir nesne ya da ayn snf trnden bir referans olmaldr. Hata nesnesi oluturmada ounlukla doal trler yerine snf nesneleri kullanlr. Bylece hata olutuunda gnderilen snf nesneleri catch parametresine alndktan sonra, snfn ye ilevleri kullanlarak oluan hata ele alnr. Snf ktphanelerinin ounda ok biimlilii destekleyen hata ileme snflar vardr. C++ standart ktphanesinde de hiyerarik hata ileme snflar tanmlanmtr. Bir ktphane iinden gnderilecek hata nesnelerinin bir snf hiyerarisi biiminde organize edilmesinin nemli bir faydas da, ktphaneye yeni hata snflarnn eklenmesinden sonra hata ileyen kodlarda bir deiikliin gerekmemesidir.

catch all Blou


stenirse, ne trden olursa olsun gnderilen bir hata nesnesinin bir catch blou tarafndan yakalanmas salanabilir. Bu durum catch blou ayrac iine nokta atomu (elipsis) yerletirilmesiyle salanr: try { func(); } catch () { // }

250/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki rnekte, try blou iinde arlan func ilevi iinden ne trden hata nesnesi gnderilirse gnderilsin, try blounu izleyen catch all blou bu hata nesnesini yakalar.

catch Bloklarnn Uygun Sras


Uygulamalarda genellikle catch bloklarnn parametreleri, en zel olandan en genel olana doru yukardan aaya doru seilir. Yani tremi snf parametreli catch bloklar taban snf parametreli catch bloklarndan daha yukarda yer alr. nk catch bloklar yukardan aaya doru taranr. Aadaki kodu inceleyin: #include <stdexcept> #include <iostream> using namespace std; int main() { try { char * buff = new char[100000000]; //... } catch(bad_alloc &r) { cout << "memory allocation failure"; //... } catch(exception &r) { cout << r_exception.what() << endl; } catch(...) { cout << "bilinmeyen hata nesnesi" << endl; } return 0; }

main ilevinde yer alan try blou iinde, new ileciyle byk bir dinamik blok elde edilmeye allyor. try blounu izleyen ilk catch blou bad_alloc snf trnden hata nesneleri yakalanmaya allyor. bad_alloc trnden bir hata nesnesi yakalandnda daha aadaki catch bloklarna hi baklmaz. kinci catch blou ile standart exception snfndan tremi herhangi bir snf trnden hata nesnesi yakalanabilir. catch blou iinde snfn what ye ileviyle, hatann niteliini gsteren bir yaznn ekrana yazdrldn gryorsunuz. Son catch blou catch all bloudur. Bu bloa kadar yakalanamayan bir hata nesnesi mutlaka catch all blou tarafndan yakalanr. Eer proje iinde gnderilen tm hata nesneleri exception snf hiyerarisine ait snflardan ise, normal olarak catch all blou tarafndan hi bir hata nesnesinin yakalanamamas gerekir. Bu blok tarafndan bir hatann yakalanmas projeyi yazanlarn kontrol dnda bir hata nesnesinin gnderilmi olduunu gsterir.

Yakalanamayan Hata Nesnesi


Gnderilen bir hata nesnesinin bir catch blou tarafndan yakalanamamas durumuna "yakalanamayan hata nesnesi" (uncaught exception) denir. Yakalanamayan hata durumunda terminate isimli standart bir ilev otomatik olarak arlr. terminate ilevinin nceden belirlenmi (default) davran, standart abort ilevini ararak program sonlandrmaktr. Standart terminate ilevinin kodunun aadaki gibi olduunu dnebilirsiniz:

251/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void(*fp)() = abort; void terminate() { (*fp)(); }

Ancak istenirse, terminate ilevinin abort ilevi yerine baka bir ilevi armas salanabilir. terminate ilevinin programc tarafndan belirlenen baka bir ilevi armasn salamak iin set_terminate isimli standart ilev arlabilir. Hem terminate ilevinin hem de set_terminate ilevlerinin bildirimleri <exception> balk dosyas iindedir: typedef void(*terminate_handler)(); terminate_handler set_terminate(terminate_handler fp); void terminate() Yukardaki bildirimden u anlam kartlr: terminate_handler parametre deikeni olmayan ve geri dn deeri retmeyen bir ilevi gsteren gsterici trnn yeni tr ismidir. set_terminate ilevi terminate_handler trnden bir geri dn deerine ve terminate_handler trnden bir parametre deikenine sahiptir. Daha uzun bir ifadeyle set_terminate ilevinin geri dn deeri, geri dn deeri retmeyen ve parametre deikeni olmayan bir ilev trnden adrestir. set_terminate ilevinin parametre deikeni geri dn deeri retmeyen ve parametre deikeni olmayan bir ilevi gsteren gstericidir. Bu durumda set_terminate ilevine bu trden bir ilevin adresi geilmelidir. ilev isimlerinin ileme sokulduunda otomatik olarak ilgili ilevin balang adresine dntrldn hatrlayn: #include <iostream> #include <exception> #include <cstdlib> using namespace std; void may_raise_exception() { throw "gnderilen hata nesnesi yakalanamayacak!"; } void myterminate() { cout << "Ben myterminate isleviyim" << endl; cout << "gonderilen hata nesnesi yakalanamadigi icin ben cagrildim" << endl; cout << "exit islevini cagirarak programi sonlandiracagim!"; exit (EXIT_FAILURE); } int main() { set_terminate(myterminate); try { may_raise_exception(); } catch (int) { //... } catch (double) { //... } catch (long) {

252/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin //... } cout << "Hata nesnesi yakalanirsa program buradan devam edecek\n"; //... return 0; } Yukardaki rnekte, main ilevinde nce set_terminate ilevine daha nce tanmlanm olan myterminate ilevinin adresi geiriliyor. Bylece terminate ilevi arldnda, terminate ilevinin nceden belirlenmi davran olan abort ilevini armas yerine, myterminate ilevini armas salanyor. try blou iinde arlm olan may_raise_exception ilevi const char * trden bir hata nesnesi gnderiyor. Ama try blounu izleyen catch bloklarnn hibirinde bu trden bir hata nesnesi yakalanamyor. Bu durumda yakalanamayan hata durumu oluuyor. Otomatik olarak terminate ilevi arlyor. terminate ilevi de myterminate ilevini aryor. myterminate ilevi de ekrana bulgu iletilerini yazdktan sonra, exit ilevini ararak program sonlandryor.

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> using namespace std; void func1(); void func2(); void func3(); int main() { func1(); //... return 0; } void func1() { func2(); //... return; } void func2() { func3(); //... return; } void func3() { //... return; }

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

253/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: #include <iostream> void func1(); void func2(); void func3(); int main() { try { func1(); } catch(int x) { //... } //... return 0; } void func1() { func2(); //... } void func2() { func3(); //... } void func3() { throw 5; //... }

Yukardaki programda func3 ilevi iinde throw anahtar szcyle bir hata nesnesi gnderildiinde, programn ak bu noktadan dorudan main ilevi iindeki catch blouna ynlendirilir. lev arlarnda arlan ilevin kodunun yrtlmesi sonunda, arlan ileve geri dnlmesi normal olarak yna(stack) yazlan bilgilerle gerekleir. Programn almas srasnda aran ilevin komutu yn alanna yazlr. arlan ilevin kodunun yrtlmesi sona erdiinde, yani return deyimi ile karlaldnda program yn alanndan programn aknn srecei noktann adresini alarak programn akn bu noktaya ynlendirir. aran ilevin arlan ileve gndermi olduu arlar da yna yerletirilir. aran ilevin kendisi de yerel deikenler tanmlanm ise bunlar iin de yn alannda yer ayrlr. Eer arlan ilevin kendisi de bir ilevi armsa, yine geri dn noktasna ilikin adres yna yazlr. Bir ilevin yrtlmesi sona erdiinde, yndan geri dn adresi ekilerek programn ak o noktaya ynlendirilir. Yndaki yerel deikenlerin de mr sona erince bu deerler de yndan kartlr. Ynda yer alan otomatik mrl nesne eer bir snf nesnesi ise bu snf nesnesinin sonlandrc ilevi arlr.

254/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bir ilevin kodunun yrtlmesi return deyimi ile deil de bir hata nesnesi gnderilmesi nedeniyle sonlandrlrsa ne olur? Bu durumda yine srasyla yndaki deerler yndan karlr. Ancak programn ak bu durumda geri dn adresine ynlendirilmez. Programn aknn devam edecei noktann belirlenmesi iin bir try blou iinde yer alan geri dn adresi aratrlr. Byle bir geri dn adresi bulunduunda programn ak geri dn noktasn izleyen ilk deyime deil, sz konusu try blounu izleyen catch bloklarna ynlendirilir. Bu ileme "ynn dengelenmesi" (stack unwinding) denir. Yn dengelenmesi mekanizmasnda, ilevden yaplan normal geri dnlerde olduu gibi yndaki snf nesneleri iin sonlandrc ilevler arlr. Bir ilevin geri dn mekanizmas ile yalnzca o ilev tarafndan yna yerletiren nesneleri ele alnrken, bir hata nesnesi gnderilerek ynn dengelenmesi durumunda ilgili catch blouna ulalncaya kadar ynda yer alan tm nesneler ele alnr. Bu nesnelerin bir snf trnden olmas durumunda bu snf nesneleri iin sonlandrc ilevler arlr. Yn dengelenmesi mekanizmas olmasayd, throw noktas ile catch noktas arasnda mrleri srmekte olan yerel snf nesneleri iin sonlandrc ilevler arlmazd. Bu da bu snf nesnelerinin tuttuu kaynaklarn (resources) serbest braklmamas ya da sisteme geri verilmemesi anlamna gelirdi. Dinamik snf nesneleri yn alannda deil "free store" alannda yer alr. Bir hata nesnesi yakalandnda dinamik snf nesneleri iin sonlandrc ilev arlmaz. Bunun uygulamadaki nemi udur: Eer programn kulland kaynaklar dinamik snf nesnelerine balanmlarsa, bir hata olumas durumunda bu nesnelerin sonlandrc ilevleri arlmayaca iin, bu kaynaklar geri verilemez. Dinamik nesnelerin akll gsterici nesnelerine balanmasyla bu sorun zlebilir. Akll gsterici snflarna ileride deineceiz.

throw fadesinin catch Parametresine Kopyalanmas


throw ilemi ile throw ifadesi nce derleyici tarafndan oluturulmu bir geici blgeye alnr. Geici blgeden catch parametresine kopyalanr. Yani throw ifadesinin catch parametresine kopyalanmas dorudan deil, geici blge ile iki aamada yaplr. Derleyicinin oluturduu bu geici blge throw ifadesi ile ayn trdendir. Uygulamada throw ifadesi genellikle snf trlerine ilikin olur. throw ifadesi bir snfa ilikinse, geici blgenin devreye girmesi anlam karmaklna yol aabilir. Bu karmakl engellemek iin eitli durumlar tek tek ele alalm: i) throw ifadesi bir snf trndendir. catch parametresi de ayn trden bir snf nesnesidir. Bu durumda nce geici blge iin kopyalayan kurucu ilev arlarak geici blgeye atama yaplr. Sonra catch parametresi iin kopyalayan kurucu ilevin arlmasyla, geici blgeden catch parametresine kopyalama yaplr. Programn ak catch blounu bitirdiinde, ters srada yani nce catch parametresi iin sonra geici blge iin sonlandrc ilev arlr. throw ileminde snf nesnesi kullanlm ise yn dengelemesi srasnda geici blgeye kopyalama ileminden sonra bu nesne iin sonlandrc ilev arlr. Bu biimdeki bir hata ileme mekanizmas ok fazla kopyalayan kurucu ilev arld iin, genellikle tercih edilmez. ii) throw ifadesi bir snf trndendir. catch parametresi ayn snf trnden bir referans olur. Bu durumda geici blge iin kopyalayan kurucu ilev arlr. Fakat daha sonra geici blgenin adresi catch parametresi olan referansa aktarlr. Geici nesne catch blou boyunca yaar. catch blounun sonunda geici blge iin sonlandrc ilev arlr. Bu sk kullanlan bir tekniktir. rnein C++'n standart ktphanesindeki hata nesnesi yakalama teknii byledir. iii) throw ilemi bir snf trnden adres ile yaplr. catch parametresi de ayn snf trnden bir gsterici deiken olur. Bu durumda geici blge de ayn snf trnden bir gsterici olacana gre, ne geici blge iin ne de catch parametresi iin herhangi bir kurucu ilev arlmaz. Tabii throw ifadesindeki adres yerel bir nesneye ilikin

255/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

olamamaldr. Bu teknikte genellikle programc new ileci ile dinamik olarak elde ettii hata nesnesini gnderir. rnein: throw new Sample(); throw ilemi yaplm olsa bile dinamik nesneler yaamaya devam eder. Byle bir throw ilemi ile geici blgeye dinamik nesnenin adresi aktarlr. Bu adres buradan da catch parametresine aktarlr: throw new Sample(); gibi bir ifade iin catch(Sample *p) { //... delete p; }

ilemi yaplmaldr. Bu yntem geici blge ve catch parametresi iin kopyalayan kurucu ilev arlmad iin, en verimli yntemdir. Ancak bu yntemde throw ileminde yaratlan geici nesnenin boaltlmas catch blounun sonunda programc tarafndan yaplmaldr. rnein MFC ktphanesi bu yntemi kullanmaktadr. Oluturulan hata nesnelerinin yaratlmasn, catch parametresine kopyalanmasn ve yok edilmesini izlemek iin aadaki kod parasn derleyerek altrn: #include <iostream> using namespace std; class Sample { public: Sample(int a):m_a(a){cout << "Sample::Sample(int) " << endl;} Sample(const Sample &r){m_a = r.m_a; cout << "Sample::Sample(const sample &) " << endl;} ~Sample(){cout << "Sample::~Sample() " << endl;} void display()const {cout << "a = " << m_a << endl;} private: int m_a; }; void func1() { Sample a(1); throw a; } void func2() { throw new Sample(2); } int main() { try { //func1(); func2(); } catch(Sample a) { a.display(); } catch(Sample *ptr) { ptr->display();

256/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin delete ptr; } cout << "end" << endl; return 0; }

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) { //... delete pCException; }

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 { throw new CSerialException; //... } catch (CException *pCException) { pCException->Disp(); delete pCException; }

CException snfndan tretilmi CMemoryException gibi bir snf olduunu dnelim. Aadaki gibi try-catch bloklar oluturulmu olsun.

257/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin try { //... } catch (CMemoryException *pException) { //... } catch (CException *pException) { //... }

Burada CMemoryException snfndan yaplan throw ilemleri birinci catch blou tarafndan, dierleri ikinci catch blou tarafndan yakalanr. Burada catch bloklarnn yerleimi nemlidir. Yani CException snfna ilikin catch blounun aada olmas gerekir.

iie try bloklar


Bir try blou baka bir try blounun iinde yuvalanm olabilir (nested try blocks). Bu durumda her bir try blounu ayr catch bloklar izlemelidir. Aadaki rnei inceleyelim : #include <iostream> void func1() { throw 1; } void func2() { throw 2.3; } int main() { try { func1(); try { func2(); } catch (double x) { std::cout << "hata yakalandi : " << x << std::endl; } } catch (int x) { std::cout << "hata yakalandi : " << x << std::endl; } std::cout << "hata yakalandiktan sonra program devam ediyor!" << std::endl; return 0; } Yukardaki rnekte dtaki try blounun iinde baka bir try blou daha yer alyor. teki try bloundaki kodun almas srasnda bir hata nesnesi gnderilmesi durumunda, yani func2 ilevi tarafndan bir hata nesnesinin gnderilmesi durumunda, gnderilen hata nesnesi nce iteki try blounun catch blou tarafndan yakalanmaya allr. Eer iteki try blouna ilikin catch blou gnderilen hata nesnesini yakalayamaz ise, bu kez gnderilen hata nesnesini dtaki try blouna ilikin catch blou yakalamaya alr.

258/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

phesiz, yalnzca dtaki try blou iinde yer alan kod parasnn almas durumunda, eer bir hata nesnesi gnderilirse, yani func1 ilevi iinden bir hata nesnesi gnderilirse, bu hata nesnesini yalnzca dtaki catch blou yakalamaya alr.

Farkl Derinliklerdeki try catch Bloklar


try ve catch bloklar farkl derinliklerde yer alabilir. Bir try blou iinde bir hata nesnesi gnderildiinde, bu hata nesnesini yakalama olasl bulunan catch bloklarnn aratrlmas aadan yukarya doru yaplr. Yani ilk nce throw deyimine en yakn durumda olan ve throw deyimini kapsayan try bloklarnn catch bloklarna baklr. Hata buradaki catch bloklar tarafndan yakalanamamamsa, bu kez srasyla daha yukardaki catch bloklar aratrlr. Aadaki rnei dikkatli ekilde inceleyiniz, program derleyerek altrn: #include <iostream> #include <cstdlib> void func1(); void func2(); void func3(); int main() { try { func1(); } catch (double) { std::cout << "hata main islevi icinde yakalandi!" << std::endl; exit(EXIT_FAILURE); } return 0; } void func1() { try { func2(); } catch (long) { std::cout << "hata func1 islevi icinde yakalandi!" << std::endl; exit(EXIT_FAILURE); } } void func2() { try { func3(); } catch (int) { std::cout << "hata func2 islevi icinde yakalandi!" << std::endl; exit(EXIT_FAILURE); } } void func3() { try { //throw 'A'; //throw 1; //throw 1L; throw 1.5; } catch (char) { std::cout << "hata func3 islevi icinde yakalandi!" << std::endl; exit(EXIT_FAILURE); }

259/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin }

func3 ilevi iinde gnderilen hata nesnesinin trn deitirerek program yeniden derleyip altrn. Programn almas srasnda oluan farkll gzleyin.

levi Kapsayan try Blou


stenirse tanmlanan bir ilevin tm ana blou bir try blou grevi yapabilir. Bu durumda ilev parametre ayracnn kapatlmasndan sonra, ancak ilevin ana blounun almasndan nce try anahtar szc kullanlr. Aadaki rnei inceleyin: void func(int x) try { //... } catch (int ) { //... } catch (double) { //... }

levi kapsayan try blouna ilikin catch blou ak bakmndan ilevin iinde kabul edilir. Yukardaki szdizim ile, eer func ilevi iinde herhangi trden bir hata nesnesi gnderilirse programn ak ilevin ana blounu izleyen catch bloklarna srar. try blounun bann ayn zamanda func ilevin ana blounun balangc ve try blounun sonunun ayn zamanda ilevin ana blounun sonu olduuna dikkat edelim. stenirse main ilevi de bu ekilde bir try blou iine alnabilir: int main() try { //... } catch (int ) { //... } catch (double) { //... }

Hata Nesnesi Belirlemeleri


Bir ilev iinde bir hata nesnesinin gnderilip gnderilmeyecei, gnderilecekse gnderilecek hata nesnesinin hangi trlerden olabilecei zel bir szdizim ile ilevin bildiriminde ve tanmnda belirtilebilir. Buna "hata nesnesi belirlemesi" (exception specification) denir. lev bildiriminde parametre ayracnn kapanmasndan sonra throw anahtar szc yazlr. Bu szc izleyen ayracn iine ilevin gnderebilecei hata nesnesinin/nesnelerinin tr/trleri yazlabilir: double func()throw (Exception);

260/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Yukardaki bildirimde, geri dn deeri double trden olan, parametre deikenine sahip olmayan func ilevinin Exception snf trnden bir hata nesnesi gnderilebilecei bildiriliyor. Byle bir bildirimle iki ilev salanr. 1. levin Exception snf trnden bir hata nesnesi gnderebilecei bilgisi verilir. Bylece kaynak kodun okunabilirlii artar. levi aran programc, ilev ar kodunu bir try blou iine alarak, ilev iinde bir hata nesnesinin gnderilmesi durumunda, bu hata nesnesini yaklamak ve hatay ilemek iin bir nlem alabilir. 2. Derleyiciye ilevin ne trden bir hata nesnesi gnderebilecei bildirilmi olur. Bylece derleyicinin rettii kod sonucunda, programn alma zamannda eer baka bir trden hata nesnesi gnderilirse, standart bir ilev arlarak programn ak sonlandrlr. Bir ilevin iinden birden fazla trden farkl hata nesneleri de gnderilebilecei iin bu durumun da ilev bildiriminde belirtilmesine olanak salanmtr. Bu durumda ilev bildirimindeki throw ayracnn iine virgllerle ayrlm biimde gnderilebilecek hata nesnelerinin trleri yazlr: double func()throw (bad_alloc, MathException); Yukardaki ilev bildiriminden func ilevinin bad_alloc ya da MathException snf trlerinden bir hata nesnesi gnderebilecei belirtilmektedir. throw (bad_alloc, MathException) bildiriminin ilev tanmnda da yazlmas zorunludur. Yani birinde bulunup dierinde bulunmamas geersizdir. func ilevi aadaki gibi tanmlanmaldr: double func(double x) throw (bad_alloc, MathException) { //... } Yine okunabilirlii artrma amacyla, bir ilevin herhangi bir hata nesnesi gndermedii bilgisi de ilev bildiriminde verilebilir. Bu durumda ilev bildiriminde yer alan throw anahtar szcn ii bo bir ayra izler. int foo(int) throw(); Yukardaki foo ilevinin bildiriminde, foo ilevinin herhangi bir hata nesnesi gndermedii belirtiliyor. Bir ilevin bildiriminde throw anahtar szcnn yer almamas, ilevin herhangi bir hata nesnesi gndermediine iaret etmez. Derleyici tarafndan bu durum, ilevin bir hata nesnesi gnderip gndermedii ya da hangi trden bir hata nesnesi gnderebilecei konusunda bir bilgi verilmedii biiminde yorumlanr. int process(int); Yukardaki ilev bildirimine gre process ilevinin bir hata nesnesi gnderip gndermeyecei konusunda bir bilgi verilmiyor. process ilevi herhangi bir trden hata nesnesi gnderebilecei gibi bir hata nesnesi gndermeyedebilir. Hata nesnesi belirlemesi, bir ilevin imzasnn bir paras deildir. Parametrik yaplar ayn hata belirlemeleri farkl ayn isimli iki ilev ayn bilinirlik alannda yer alamaz:

261/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

void func(int) throw(int); void func(int) throw(double);

//func ilevinin yeniden bildiriminde hata

Hata belirlemesi, ilevin trnn bir paras olmadndan typedef bildiriminde de yer alamaz. Aadaki typedef bildirimi derleme zamannda hata oluumuna neden olur: typedef void (*PVPTR)(int) throw(MathException); //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: typedef void(*terminate_handler)(); terminate_handler set_unexpected(terminate_handler fp) throw(); void unexpected() 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: void func(); //... set_unexpected(func); set_unexpected ilevine adresi gnderilen ilev aada tanmlanan ilevlerden birini yapmaldr: terminate ilevini ararak program sonlandrmak (zaten bu nceden belirlenmi (default) davrantr. abort ilevini armak. exit ilevini armak. Bir hata nesnesi gndermek

262/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Bir ilevin tanm iinde hata belirlemesiyle bildirilen snflarn dnda bir hata nesnesi gnderildiinde, ilgili ilev bu hatay kendi iinde ele alabilir. Bu durumda unexpected ilevi arlmaz. Aadaki rnei inceleyin: void func(int val) throw(Exception) { try { //... throw string("Hata!"); } catch (string){ //... } //... }

Yukardaki rnekte func ilevinin Exception snf trnden bir hata nesnesi gnderilebilecei bildiriliyor. Ancak ilev iinde, standart string snf trnden bir hata nesnesi gnderiliyor. Gnderilen hata nesnesinin, ilev iinde yer alan catch blou tarafndan yakalandn gryorsunuz. Bu durumda unexpected ilevi arlmaz. Hata belirlemelerine ilikin kontrol derleme zamannda deil, programn alma zamannda yaplr. Aadaki kodu inceleyin: void func(double)throw(string, Exception); void process (int val) throw (string) { //... func(7.5); //... }

Yukarda func isimli ilevin bildiriminde, ilevin string ya da Exception snf trnden bir hata nesnesi gnderebilecei bildiriliyor. Aada tanmlanan process ilevinin tanmnda ise, string snf trnden bir hata nesnesi gnderilebilecei bildiriliyor. Oysa process ilevi iinde, Exception snf trnden bir hata nesnesi gnderme olasl bulunan func ilevi arlyor. Bu durumda derleme zamannda bir hata olumaz. Ancak programn alma zaman srasnda process ilevinin kodu altnda, func ilevi arldnda gerekten Exception snf trnden bir hata nesnesi gnderilirse, bu durumda unexpected ilevi arlr. Bu durumun salanmas iin process ilevin tanmnn aadaki gibi yapldn dnebelirsiniz. void process (int val) throw (string) { try { //ilevin kodu } catch (string &) { throw; } catch (...) { unexpected(); } }

Bu durumda

263/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin void foo(void) throw() { //... } gibi bir ilev tanmnn da gerekte derleyici tarafndan aadaki gibi ele alnd dnlebilir, deil mi? void foo(void) throw() { try { //... } catch(...) { std::unexpected(); } }

bad_exception Snf Trnden Hata Belirlemesi


Bir ilevin bildiriminde yer alan hata belirlemesinde standart bad_exception snf kullanlabilir. Bunun anlam udur: Eer sz konusu ilev, hata belirlemesinde belirtilen snflarn dnda bir trden hata nesnesi gnderirse, yani beklenmeyen hata nesnesi durumu oluursa bu durumda unexpected ilevi arlmaz. bad_exception snf trnden bir hata nesnesi gnderilir.

Taban Snf le Tremi Snf Arasndaki Hata Belirlemesi Uyumu


C++ dilinin kurallarna gre tremi snfn hata belirlemeleriyle taban snfn hata belirlemeleri arasnda bir uyum olmak zorundadr. Taban snfn sanal ilevini ezen, bir tremi snf ilevi, taban snfn gnderdii hata nesneleri trlerinin dnda bir hata gndermemelidir. Aadaki rnei inceleyin: class Exception{}; class MathException:public Exception {}; class SpecialException {}; class Base { public: virtual void virtual void virtual void virtual void virtual void virtual void };

func1() func2() func3() func4() func5() func6()

throw throw throw throw throw throw

(Exception); (Exception); (MathException); (MathException); (Exception); ();

class Der: public Base { void func1() throw(MathException); // void func2() throw(SpecialException); void func3() throw(MathException); //void func4() throw(Exception); //void func5() throw(Exception, SpecialException virtual void func6(); //Geersiz! };

//Geerli Geersiz! //Geerli Geersiz! ); Geersiz!

Der snf Base snfndan tretiliyor. Der snfnn, Base snfnn sanal ilevlerini ezen ilevlerinin hata belirlemelerinde, Base snfnn gndermedii bir hata tr kullanlamaz. Der snfnn func2, func4 ve func5 ilevlerinin bildirimi geersizdir.

264/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

lev Gstericileri ve Hata Belirlemeleri


Bir ilev gstericisinin tanmnda, gstericinin gsterecei ilevin hata belirlemeleri de belirtilebilir: void (*fp)()throw (Myclass); Yukardaki bildirimden u anlalr: fp geri dn deeri retmeyen, parametre deikeni olmayan ve Myclass snf trnden bir hata nesnesi gnderme olasl olan bir ilevi gsterecektir. Byle bir ilev gsterici deikeni, parametrik yaps ayn ama, baka snf trnden hata nesnesi gnderme olasl olan bir ilevi, gsteremez: void func() throw (Exception); void (*fp) ()throw (Myclass) = &func; //Geersiz!

Hata leyen Kodun Yeniden Hata Nesnesi Gndermesi


Hata nesnesini yakalayan ve ileyen bir kod yakalam olduu hata nesnesini yeniden gnderebilir. Yeniden gnderilen hata nesnesi daha yukar bir seviyede kapsayan bir try blou tarafndan tutulmaya allr. Bylece hata yakalayan bir catch blou yakalad bir hatay ksmen ele alabilir. Yapmas gerekenleri yaparak, daha sonra hatann daha yukar seviyedeki catch bloklar tarafndan yakalanabilmesini salamak iin, hata nesnesini yeniden gnderebilir. catch blounun iinde, throw anahtar szc yannda bir ifade olmakszn, yaln olarak kullanlrsa bu ileme "hata nesnesinin yeniden gnderilmesi" (rethrow) denir. try { func(); } catch (...) { //... throw; }

Yeniden throw ileminde, hata nesnesi bir dtaki (kapsayan) catch blouna gnderilir. Bu ilemde geici blge silinmez. Yani ilk throw ilemindeki ifade geici blgede kalr. Bir baka deyile gnderilen ayn hata nesnesidir. Aadaki kodu inceleyiniz: #include <iostream> #include <string> enum {SUCCESS, FAILURE}; class File { public: File (const char *str) {} bool is_valid() const { //... return false; } int open_new_file() const {return FAILURE;} }; class Exception { //... }; class FileException: public Exception { public:

265/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin FileException(const char *p) : s(p) {} const char * get_message()const {return s.c_str();} private: std::string s; }; void foo(File &); using namespace std; int main() { try { File f ("letter.txt"); foo(f); // 1 } catch (...) { cout << "yeniden gonderilen hata nesnesi yakalandi" << endl; } return 0; }

void foo(File &r) { try { if (!r.is_valid()) throw FileException("letter.txt"); } catch(FileException &r_ex) { cout << "gecersiz dosya :" << 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: gecersiz dosya :letter.txt yeni dosya acilamiyor! tekrar gonderilen hata nesnesi yakalandi 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:

266/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin catch (Exception e) { e.set_value(/**/); //catch parametresi zerinde deiiklik yaplyor... throw; //Hata nesnesi yeniden gnderiliyor. } 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: catch (Exception &r) { r.set_value(/**/); throw; }

//hata nesnesi zerinde deiiklik yaplyor... //hata nesnesi yeniden gnderiliyor.

Yukardaki catch blou hata nesnesinin kendisini yakalar. r.set_value(/**/); arsyla deitirilen hata nesnesinin kendisidir. Bu durumda yeniden gnderilen hata nesnesi, zerinde deiiklik yaplm olan hata nesnesidir.

Kurucu levlerden Hata Nesnesi Gnderilmesi


Kurucu ilevler iin geri dn deeri diye bir kavramn sz konusu olmadn biliyorsunuz. Kurucu ilevler iinde oluan normal d durumlarn, geri dn deeri ile darya iletilmesi mmkn deildir. Kurucu ilev iinden bir hata nesnesi gnderilmesi sk karlalan bir durumdur. Bir kurucu ilev iinden gnderilen hata nesnesi yakalandnda, kurucu ilevi arlm olan nesne iin, yani *this nesnesi iin sonlandrc ilev arlmaz. Zira nesnenin oluturulma sreci henz sona ermeden bir hata nesnesi gnderilmitir. Bir hata nesnesi yakalandnda, yalnzca ynda yer alan oluumu tamamlanm yerel nesneler iin sonlandrc ilevler arlr. Peki ya iinde throw ilemi yaplan kurucu ilev dinamik bir nesne iin arlmsa? Bu durumda new ileci ile *this nesnesi iin bellekten ayrlm dinamik alann free store alanna geri verilmesi gvence altna alnmtr.

Bileik Nesnelerde Yer alan Elemanlarn Kurucu levlerinden Hata Nesnesi Gnderilmesi
imdi de bir snfn baka bir snf trnden elemanlara sahip olduunu dnelim. Elemanlardan birinin kurucu ilevi iinden bir hata nesnesi gnderildiinde, ancak oluumu tamamlanm elemanlar iin sonlandrc ilevler arlr. Durumu incelemek ve gzlemek iin aadaki kod parasn derleyerek altrn: #include <iostream> class Mem1 { public: Mem1() {std::cout << "Mem1()::Mem1()" << std::endl;} ~Mem1() {std::cout << "Mem1()::~Mem1()" << std::endl;} }; class Mem2 { public: Mem2() {std::cout << "Mem2()::Mem2()" << std::endl; }

267/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin ~Mem2() {std::cout << "Mem2()::~Mem2()" << std::endl;} }; class Owner { Mem1 m1; Mem2 m2; public: Owner(){std::cout << "Owner::Owner()" << std::endl; throw 1;} ~Owner(){std::cout << "Owner::~Owner()" << std::endl;} }; int main() { try { Owner x; } catch (int x) { std::cout << "hata yakalandi!" << ( << x << )" << std::endl; } return 0; } Owner snfnn Mem1 snf trnden m1, ve Mem2 snf trnden m2 isimli private elemanlara sahip olduunu gryorsunuz. Tm snflarn kurucu ve sonlandrc ilevleri, arldklarnda ekrana arldklarn gsteren bir yaz yazdryor. Owner snfnn kurucu ilevi iinde int trnden bir hata nesnesi gnderiliyor: // Owner() {cout << "Owner::Owner()" << endl; throw 1;} // main ilevi iinde Owner snf trnden x isimli bir nesne yaratldn, tanmlama deyiminin try blou iine alndn gryorsunuz. try blounu izleyen catch blou ile int trden hata nesnesi yakalanr. Hata nesnesi yakalandnda hangi snf nesneleri iin sonlandrc ilevler arlr? m1 ve m2 snf nesnelerinin oluturulma sreci tamamlanm olduu iin bu nesnelerin sonlandrc ilevleri arlr. Ancak Owner snfnn sonlandrc ilevi arlmaz. nk Owner snf nesnesinin oluturulmas henz tamamlanmamtr. Programn ekran kts aadaki gibi olur: Mem1()::Mem1() Mem2()::Mem2() Owner::Owner() Mem2()::~Mem2() Mem1()::~Mem1() hata yakaland!(1)

Bu kez Owner snfnn kurucu ilevi iinde deil de, Mem2 snfnn kurucu ilevi iinden bir hata nesnesi gnderildiini dnelim: // Mem2() {cout << "Mem2()::Mem2()" << endl; throw 2;} // Gnderilen hata nesnesi yakalandnda, yalnzca Mem1 snfnn sonlandrc ilevi arlr. Bu durumda programn ekran kts aadaki gibi olur: Mem1()::Mem1() Mem2()::Mem2() Mem1()::~Mem1() hata yakalandi!(2)

268/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Tretmede de benzer bir durum sz konusudur. Bir tremi snf nesnesinin oluturulmas durumunda nce taban snf nesnesi oluturulur deil mi? Tremi snfn kurucu ilevi taban snfn kurucu ilevini arr. Taban snf nesnesinin kurucu ilevi iinden bir hata nesnesi gnderildiinde hangi snflarn sonlandrc ilevleri arlr?

Kurucu levi Sarmalayan try Blou


Bir kurucu ilevin tamam try blou ile kapsanabilir: Foo::Foo() try { //... } catch (bad_alloc) { //... } Kurucu ilevde M.I.L. szdizimi kullanlmas durumunda da kurucu ilevi sarmalayan bir try blou oluturulabilir: Foo::Foo() try : x(a), y(b) { //... } catch (bad_alloc) { //... }

Yukardaki gibi bir try blounda, yalnzca kurucu ilev ana blou iinden gnderilecek hata nesneleri deil, ayn zamanda elemanlarn kurucu ilevleri iinden gnderilen hata nesneleri de yakalanabilir. Kurucu ilevi sarmalayan try blounun uygulamada bir nemi daha vardr: Byle bir kurucu ilev hata belirlemesiyle, public arayznde belirli bir tr dnda hata nesnesi gndermemeyi gvence altna alabilir. Kurucu ilev iinde gnderilen baka trden bir hata nesnesi, her trden hata nesnesini yakalayan bir catch blou ile yakalanarak, istenilen trden bir hata nesnesinin daha yukarya gnderilmesi salanabilir. Aadaki rnei inceleyin:

269/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <string> class Ex{ //... }; class Foo{ std::string s; public: Foo(const char *) throw (Ex); }; Foo::Foo(const char *str) throw(Ex) try: s(str) { //... } catch(...) { throw Ex(); }

Yukardaki rnekte Foo snfnn kurucu ilevi hata belirlemesi ile ancak Ex snf trnden bir hata nesnesi gndereceini bildiriyor. Foo snfnn kurucu ilevinin bir try blou ile sarmalanm olduunu gryorsunuz. Kurucu ilevi sarmalayan try blounu, her trden hata nesnesini yakalayabilen bir catch blou izliyor. Bu catch blou ile bir hata nesnesinin yakalanmas durumunda, Ex snf trnden bir hata nesnesi gnderiliyor. Bylece hata belirlemesiyle verilen gvenceye uyulmas salanyor.

Kurucu levler Tarafndan Elde Edilen Kaynaklar ve Hata Nesneleri


Bir ok snfn tasarmnda, snf nesnesinin kullanaca bir kaynak, snfn kurucu ileviyle snf nesnesine balanr. Snfn sonlandrc ilevinin arlmasyla bu kaynak geri verilir. Eer bu kaynaklar bir gstericiye balanrsa, kaynan balanmasndan sonra, kurucu ilev iinden bir hata nesnesi gnderilmesi durumunda, balanan kaynaklarn geri verilebilmesi mmkn olmaz. Bu kaynaklar akll gsterici (smart pointer) snflarndan nesnelere balanabilir. STL iinde bu ama iin tasarlanm ablon temelli auto_ptr isimli standart bir akll gsterici snf vardr. "Akll gstericiler" bal altnda bu konuya daha ayrntl bir ekilde deineceiz.

Sonlandrc levlerden Hata Nesnesi Gnderilmesi


Sonlandrc ilevlerden hata nesnesi gnderilmesi dilin kurallarna gre geerli olmasna karn, genellikle nerilmez. Bir sonlandrc ilev ayr nedenden dolay arlm olabilir: 1. Bir snf nesnesinin bilinirlik alan sona ermitir. Snf nesnesinin mr sona erdiinden, snfn sonlandrc ilevi arlr. 2. Dinamik bir snf nesnesi delete ileci kullanlarak free store'a geri verilmek istendiinde dinamik snf nesnesi iin de sonlandrc ilev arlr. 3. Gnderilen bir hata nesnesi bir catch blou tarafndan yakalandnda, yn dengelenmesi sreci iinde, ilgili catch blouna ulalmasna kadar ynda yer alan tm yerel snf nesneleri iin sonlandrc ilev arlr. Eer bir sonlandrc ilev ynn dengelenmesi srasnda arlmsa, sonlandrc ilev iinden bir hata nesnesi gnderilmesi durumunda programn aknn ilevin dna kmasna izin verilmez. Bu durumda otomatik olarak std::terminate ilevinin arlmas gvence altna alnmtr.

270/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Sonlandrc ilev iinde, hata nesnesi gnderme olasl olan bir ilevin arlm olmas son derece normal bir durumdur. Ancak arlacak ilevler iinden gnderilmesi olasl olan hata nesneleri, yine sonlandrc ilev iinde yer alan catch bloklaryla yakalanmal, hatann daha yukarya gnderilmesine izin verilmemelidir: Myclass::~Myclass() { // try { if (x < 0) throw 1; } catch(int) { // } // }

Yukardaki rnekte Myclass isimli snfn sonlandrc ilevi iinde gnderilen int trden hata nesnesi, yine ayn ilev iinde yakalanyor.

uncaught_exception levi
Bir baka olanak ise, sonlandrc ilev iinde bir hata nesnesi gndermeden nce, yani bir throw ilemi yapmadan nce, bir hatann ilenmekte olup olmadnn snanmasdr. Bir hata nesnesinin ilgili bir catch blou tarafndan tutulmasyla yani bir catch blouna girilmesiyle, ilenmesinin bitmi olduu kabul edilir. Standart <stdexcept> balk dosyas iinde bildirilen uncaught_exception ileviyle bir hatann ilenmekte olup olmad snanabilir: bool uncaught_exception(); lev true deere geri dnerse bir hata durumu ileniyor demektir. Yani bir hata nesnesi gnderilmi ancak henz catch blouna girilmemitir. Eer sonlandrc ilevin kodu iinde uncaught_exception ilevi arlm ve true deere dnmse, sonlandrc ilevin yn dengelenmesi srasnda yerel bir snf nesnesinin yok edilmesi yznden arld anlam kar: Sample::~Sample() { // if (x < 0) { if (uncaught_exception()) return; throw 1; } return; }

Yukardaki kod parasnda Sample snfnn sonlandrc ilevi iinde, x deikeninin deerinin 0dan kk olmas durumunda nce uncaught_exception ilevi arlyor. Ancak ilevin false deerine geri dnmesi durumunda bir hata nesnesi gnderiliyor.

Global Snf Nesnelerinin Kurucu ve Sonlandrc levlerinden Hata Nesnesi Gnderilmesi


Global snf nesnelerinin kurucu ilevleri main ilevin almaya balamasndan nce arldndan, bu nesnelerin kurucu ilevleri iinden gnderilen hata nesnelerinin yakalanma ans yoktur. Global nesnelerin sonlandrc ilevlerinden gnderilen hata

271/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

nesnelerinin de yakalanma ans yoktur. nk global nesnelerin sonlandrc ilevleri de main ilevinden sonra arlr.

STL deki Hata Snflar


STL deki snflar ablon tabanl olmasna karn hata snflar ablon tabanl deildir. Hata snflar <exception> ve <stdexcept> balk dosyalar iinde bildirilmitir. Standart ktphanedeki hata snflar da bir tretme hiyerarisi iermektedir. Tretme hiyerarisinin tepesinde "exception" isimli bir snf bulunur. exception snf standart exception balk dosyas iinde bildirilmitir: class exception { public: exception() throw(); exception(const exception& right) throw (); exception& operator=(const exception& right) throw(); virtual ~ exception() throw(); virtual const char *what() const throw(); };

exception snfndan tretilen btn standart hata snflar, what isimli sanal ilemi ezer. Bu ilev, hatay tanmlayan bir yaznn balang adresine dnmektedir. 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: class MyException { public: MyException(const char *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.

272/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

273/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ALIMA ZAMANINDA TR BELRLENMES


alma zamannda tr bilgisinin elde edilmesi C++ diline eklenen son zelliklerden biridir. Standartlar ncesi oluturulmu derleyicilerde, bu zellik desteklenmeyebilir. Baz derleyiciler de, bu zellii istee bal olarak etkin duruma geiriyor olabilir. Bu zellie ksaca RTTI (Runtime Type Information/Identification) denir. RTTI, C++ dili tarafndan standart hale getirilmi, bir nesnenin trnn alma zamannda belirlenebilmesine ynelik, baz aralara verilen genel isimdir. Byle bir aracn standart bir hale getirilmesinden nce, zaten yazlm gelitiren baz firmalar, alma zamannda bir nesnenin trnn belirlenebilmesine ynelik aralar kendileri gelitirip, kendi oluturduklar snf ktphaneleri iin kullanyorlard. Ancak bu durum farkl ktphanelerin kullanlmas durumunda bir takm tanabilirlik sorunlarna yol ayordu. nk bir firmann kulland bileen, baka bir firmann kulland bileen ile uyumsuz olabiliyordu. RTTI aralarnn standart hale getirilmesi, gelecekte oluturulacak farkl snf ktphanelerinin birbirleriyle uyumlu olmas konusunda atlm nemli bir adm kabul edilebilir.

alma Zamannda Bir Nesnenin Trnn Belirlenmesi (RTTI) Nedir?


Mekanizmay iyi anlayabilmek iin nce "aa doru dnm" (downcast) konusunu incelemek gerekir. Aa doru dnm ne anlama gelir? Taban snf trnden bir gsterici deikene, tremi snf trnden bir nesnenin adresi dorudan dorudan atanabilir. ngilizcede bu duruma "Yukar doru dnm" (upcasting) denir. "Aa doru dnm" (downcasting) ise, taban snf trnden bir adresin tremi snf trnden bir gstericiye, tr dntrme ileci kullanlarak atanmas ilemidir. Tr dntrme ileci kullanlmadan, taban snf nesnesinin adresinin, tremi snf trnden bir gstericiye atanmas dilin kurallarna gre geersizdir. Bir snf hiyerarisi iinde, bir taban snftan tremi eitli snflarn yaratldn dnelim. Tremi snf trnden bir snf nesnesinin adresi, taban snf trnden bir gsterici deikene atanabilir. Ya da byle bir ilem referans kullanlarak yaplabilir. Peki taban snf trnden gsterici ile bir ye ileve ar yapldnda, hangi ilev arlr? Eer arlan sanal bir ilev deilse, taban snfn ilevi arlr. Ancak arlan sanal bir ilev ise, taban snf gstericisine hangi tremi snf trnden bir nesnenin adresi atanmsa, o tremi snfa ilikin ilev arlr. Tabi bunun iin, tremi snfn devir ald sanal ilevi ezmi (override) olmas gerekir. Sanal ilevin sz konusu olmas durumunda, zaten taban snfa ilikin gstericinin iinde ne trden bir nesnenin adresinin tutulduunun bilinmesine gerek kalmaz. Ancak baz durumlarda, tremi snfa ilikin bir nesnenin adresi taban snf trnden bir nesneye atanm olsa da, halen bu gsterici yoluyla tremi snf trlerinden birine ilikin, sanal olmayan bir ilev arlmak istenebilir. te RTTI aralar arlkl olarak byle durumlarda kullanlr. Bu ara ile, ye ilevi arlacak nesnenin tr bilgisi elde edilerek ye ilevin arlp arlmayacana karar verilir. Sz konusu gereksinimi basit bir rnekle gstermeye alalm. Aadaki kodu inceleyin:

274/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Base { //.. public: virtual void vfunc(); }; class Der1: public Base { //... public: virtual void vfunc(); void foo(); }; class Der2: public Base { public: virtual void vfunc(); //... }; class Der3: public Base { public: virtual void vfunc(); //... }; void process(Base *baseptr) { baseptr->vfunc(); //baseptr Der1 snf trnden ise Der1 snfnn foo islevinin //cagrilmasi gerekiyor. }

Base snfnn vfunc isimli bir sanal ye ilevi var: Bu ilev Base snfndan treyen Der1, Der2 ve Der3 snflar tarafndan eziliyor. Bylece okbiimli bir snf hiyerarisi oluturuluyor. Der1snf, Base snfndan devir ald bu sanal ilevin dnda, kendisi de foo isimli bir ilev tanmlyor. Base snf trnden bir gsterici parametre deikenine sahip, global process isimli ilev, okbiimli ilev yapmak amacyla tanmlanyor. Yani process ilevi Base trnden bir nesnenin adresi ile arlabildii gibi, Der1, Der2, Der3 snflar trlerinden bir nesnenin adresi ile de arlabilir. process ilevi iinde, nce taban snfn, sanal vfunc ilevi arlyor. Bu ilev sanal sanal olduu iin, hangi trden snf nesnesinin adresi process ilevine geirilirse o snfa ilikin vfunc ilevi arlr. process ilevine adresi gnderilen nesne Der1 snf trnden ise, bu snfn foo isimli ilevinin arlmas gerektiini dnelim. if (baseptr'nin gsterdii nesne Der1 trnden ise) ((Der1 *)baseptr)->foo() Peki ama taban snf iindeki gstericinin, Der1 snf trnden nesneyi gsterip gstermedii nasl bilinebilir? Bu bilgi her zaman derleme zamannda elde edilemez. Ancak alma zaman iinde devreye giren bir mekanizma yardmyla saptanabilir. Yukardaki rnekte taban snf trnden gsterici olan baseptr gstericisi iindeki adres, tr dntrme ileci ile tremi snf trnden bir adrese dntrlp, bu adres ile tretilen Der1 snfnn foo ilevi arlyor. Ancak bu dntrme ilminin alma zaman hatas olmamas iin, gerekten baseptr iinde Der1 snf trnden bir adres olup olmadnn bilinmesi gerekir. Peki ptr iinde Der1 snf trnden bir adres deil de rnein Base snf trnden bir adres varsa ne olur? Bu durumda foo ilevine this adresi

275/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

olarak geirilen adres geerli bir adres olmaz. foo ilevine yaplan ar bir gsterici hatasna neden olur. Aa doru dnm, tr dntrme ilelerinin kullanlarak taban snf trnden bir adresten tremi snf trnden bir adresin elde edilmesidir. Ancak bu riskli bir ilemdir. nk tr dntrme ilemi yapldnda, taban snf gstericisinin iinde gerekten tremi snf trnden bir nesnenin adresi olup olmad bilinmeyebilir. te RTTI aralar bu noktada ie yarar. Bu aralarla aa doru bir dnmn gvenilir bir biimde yaplmas mmkndr. C++ dilinde RTTI aralarnn ayr bileeni vardr: 1) dynamic_cast tr dntrme ileci dynamic_cast C++ dilinin bir anahtar szcdr. Bu ile, taban snf trnden bir adresi -eer mmkn ise- tremi snf trnden bir adrese dntrr. Tr dntrme ilemi baarl olarak yaplrsa ilecin rettii deer, hedef tr olan tremi snf trnden bir adrestir. Aksi halde, yani dntrme ilemi baarl olmamsa, ile 0 deerini yani NULL adresini retir. 2) typeid ileci. Bir nesnenin trn kesin olarak belirleyen bir iletir. typeid C++ dilinin bir anahtar szcdr. 3) Bilgi edinilmek istenen tr hakkndaki nemli bilgilerin tutulduu type_info snf. RTTI aralar ancak sanal ileve sahip bir snf hiyerarisi iin kullanlabilir. Eski derleyiciler alma zamannda tr belirlenmesini desteklemiyor olabilir. Yeni derleyicilerin ounluu ise RTTI aralarn seime bal olarak etkin duruma geirir. imdi bu aralar ayrntl olarak inceleyelim:

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. void process(Base *baseptr) { baseptr->vfunc (); if (Der1 *der1_ptr = dynamic_cast<Der1 *> (baseptr)) der1_ptr->foo(); }

276/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

imdi process ilevi iinde alma zamannda tr belirleniyor. baseptr gstericisi dynamic_cast ilecinin terimi yaplarak, Der1 * trne dntrlyor. dynamic_cast ilecinin rettii deer Der1 trnden gsterici olan der1_ptr deikenine atanyor. Eer atanan deer NULL adresi deilse, bu gsterici yoluyla, tremi Der1 snfnn foo isimli ilevi arlyor. dynamic_cast ilecinin tr dntrme ilemini baaryla yapp yapmad mutlaka snanmaldr.

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> class Base { //... public: virtual ~Base(){} }; class Der : public Base{ //... }; void func(Base &baseref) { Der &r = dynamic_cast<Der &> (baseref); //... } int main() { Der der; func(der); return 0; }

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. void func (Base &baseref) { try { Der &r = dynamic_cast<Der &> (baseref); } catch (std::bad_cast &) { //... } }

277/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Ne zaman bir adres tr yerine bir referans trne dynamic_cast ileciyle dnm yaplmaldr? Bu tamamyla programcnn seimidir. Programc oluturduu tasarm dorultusunda bir seim yapar. Referansa yaplan bir dnm sz konusu olduunda, baarszlk durumu gz ard edilemez. Mutlaka hata ileme aralar kullanlmaldr. Ancak hata ileme aralarnn, programn alma zaman asndan bir maliyeti olduu iin, programclarn ou dynamic_cast ileciyle bir referans trne deil bir adres trne dnm yapmay tercih eder.

dynamic_cast leci ile static_cast leci Arasndaki Fark


static_cast ileci ile, bir snf trnden adresin, ayn hiyerari iinde baka bir snf trnden adrese aa doru dntrlmesi geerlidir. rnein A snfndan B snf, B snfndan da C snfn tretmi olalm. static_cast ileci ile A snf trnden bir adresi C snf trnden bir adrese dntrmek geerlidir: class A { //... }; class B : public A { //... }; class C : public B { //... }; void func(A *aptr) { C *cptr = static_cast<C *>(aptr); //... }

Yukardaki kod parasnda, func ilevinin parametre deikeni olan aptr gstericisinin deeri, ilevin ana blou iinde static_cast ileci ile, C snf trnden bir adrese dntrlyor. Kod tamamen geerlidir. Ancak kodun geerli olmas aptr gsterici deikeni iinde, C snf trnden bir nesnenin adresi olduunun gvencesi deildir. Snama derleme zamannda yaplr. func ilevinin A snf trnden bir adresle arlmas gsterici hatasdr. static_cast ileci ile aa doru dnm yaplabilir. Ancak dnmn gvenli olup olmad, programn alma zamannda snanmaz. lecin rettii deer, her zaman asal ayra iine yazlan hedef trden adrestir. Ancak programn alma zamannda o adreste o trden bir nesne olacann hibir gvencesi yoktur. imdi de dnmn dynamic_cast ileci ile yapldn dnelim: void func(A *aptr) { if (C *cptr = dynamic_cast<C *>(aptr)) //... }

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.

typeid leci ve type_info Snf


typeid ileci ile iki nesnenin trnn ayn olup olmad, programn alma zamannda saptanabilir. typeid tek terimli nek konumunda bir iletir. lecin kullanm yledir:

278/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

typeid(ifade) Bu ile ifadenin trn saptar ve ifadenin trne uygun const std::type_info & trnden bir deer retir. Burada belirtilen ifade, sizeof ilecinde olduu gibi herhangi bir tr ismi, ya da normal bir ifade olabilir. lecin terimi olan ifade, okbiimli bir snf trnden bir nesne belirtiyorsa, elde edilecek bilgi alma zaman srasndaki dinamik tre ilikindir. rnein baseptr, okbiimli bir tretme emasnda taban snf trnden bir gsterici olsun. *baseptr, typeid ilecinin terimi yaplm olsun. baseptr alma zamannda hangi nesneyi gsteriyor ise, o snfa ilikin tr bilgisi elde edilir. typeid ilecinin rettii deer std::type_info snf trnden const bir referanstr. Bu referans std::type_info snf trnden bir nesnenin yerine geer. type_info snfnn kodlanmas derleyiciden derleyiciye deiebilir. Ancak bu snfn temel yaps standart olup, tm derleyiciler iin ayndr: namespace std{ class type_info { //Derleyiciye bal ksm private: type_info(const type_info &); type_info &operator=(const type_info &); public: virtual ~type_info(); bool operator==(const type_info &, const type_info &); bool operator!=(const type_info &, const type_info &); bool before(const type_info &r) const; const char *name() const; }; }

Yukardaki snf tanmnda baz noktalar inceleyelim : type_info snf da dier snflarda olduu gibi std isim alan iinde bildirilmitir. 1. type_info snfnn bir varsaylan kurucu ilevi yoktur. type_info snf trnden bir nesne varsaylan kurucu ilev arlacak ekilde yaratlmaz. 2. type_info snfnn kopyalayan kurucu ilevi ve atama ilecini ykleyen ilevi snfn private blmne yerletirilmitir. Bu da u anlama gelir: typeid ilecinin rettii referansa bir baka type_info trnden nesne atanamaz. Bir type_info nesnesi baka bir type_info nesnesi ile ilk deer verilerek yaratlamaz. #include <typeinfo> type_info t1; // Geersiz! type_info *ptr = new type_info; // Geersiz! //Geersiz! type_info t2(type_id(unsigned int)) type_info snf trnden bir nesne, ancak typeid ileci ile elde edilebilir. 2. == ve != karlatrma ilelerini ykleyen, snfn public ilevleri tanmlanmtr. type_info trnden nesnelerin eitlii bu ileler tarafndan snanabilir. Aadaki rnei inceleyin:

279/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin #include <iostream> #include <typeinfo> class Sample1 { //... public: virtual ~Sample1() {} }; class Sample2 { //... public: virtual ~Sample2() {} }; class Sample3 { //... public: virtual ~Sample3() {} }; int main() { Sample1 s1; Sample2 s2; Sample3 s3; Sample1 &rs1 = s1; Sample2 *ptrs2 = &s2; if (typeid(Sample1) == typeid(Sample2)) std::cout << "Ayni tur!" << std::endl; else std::cout << "Farkli tur!" << std::endl; if (typeid(rs1) == typeid(s1)) std::cout <<"Ayni tur!" << std::endl; else std::cout <<"Farkli tur!" << std::endl; if (typeid(*ptrs2) == typeid(rs1)) std::cout <<"Ayni tur!" << std::endl; else std::cout <<"Farkli tur!" << std::endl; return 0; }

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 std::cout std::cout std::cout std::cout << << << << << typeid(Sample1).name() << std::endl; typeid(*ptrs2).name() << std::endl; typeid(rs1).name() << std::endl; typeid(int).name() << std::endl; typeid(long double).name() << std::endl; //class Sample1 //class Sample2 //class Sample1 //int //long double

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.

280/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Karlatrma derleyiciye zg bir biimde yaplr. rnein geri dn deeri true ise *this nesnesine ilikin tr, "tretme hiyerarisinde daha yukarda" anlam kartlamaz.

typeid ilecinin okbiimli Olmayan Snflar in Kullanm


typeid ileci genel olarak okbiimli snflara ilikin uygulamalarda kullanlsa da, byle bir zorunluluk yoktur. Yukardaki rnekte grld gibi typeid ilecinin terimi C++ dilinin temel veri trlerinin ismi, ya da bu veri trlerinden nesneler de olabilir. typeid ilecinin terimi olan nesne taban snf trnden ise, typeid ilecinin davran, sz konusu taban snfn okbiimli olup olmamasna gre, yani sanal ilev ierip iermemesine gre deiir. Aadaki rnei inceleyelim: #include <iostream> class Base { //... public: }; class Der : public Base{ //... }; int main() { Der der; Base *base_ptr = &der; std::cout << typeid(*base_ptr).name() << std::endl; return 0; } int main() { Der der; Base *base_ptr = &der; std::cout << typeid(*base_ptr).name() << std::endl; return 0; }

Yukardaki rnekte ekrana class Base yazlr. Base trnden bir gstericiye, Der trnden bir nesnenin adresi atanm olmasna karn, typeid ilecinin retmi olduu referans ile name ilevi arldnda, ekrana taban snfn ismi yazlr. nk Base snf okbiimli snf deildir. Oysa Base snfnn tanm bir sanal ilev ierecek ekilde deitirilirse bu kez ekrana class Der yazlr. Sample snfnn tanmn aadaki gibi deitirip, kaynak kodu yeniden derleyin ve altrn: class Sample { //... public: virtual ~Sample(){} };

Derleyiciler, yukarda belirtilen ve standartlara gre snf tanm iinde bulunmas gereken ilevlerin dnda, type_info snf iin, baka faydal ye ilevler tanmlayarak programcnn kullanmna sunabilir. rnein baz derleyiciler, snf trne ilikin ye ilevlerin listesini veren bir ye ilev tanmlam olabilir. Yine baz derleyiciler snfa ilikin bir nesnenin bellekteki organizasyonu hakknda bilgi veren bir ye ilev tanmlam olabilir.

281/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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); } //... }

RTTI aralarnn maliyeti


alma zamannda bir nesnenin trnn belirlenmesinin bir maliyeti vardr. RTTI aralarnn kodlanmas derleyiciyi yazanlarn seimine braklmtr. Derleyicilerin ou bellekte her bir veri tr iin bir type_info nesnelik alan ayrr. Daha nce belirtildii gibi dynamic_cast mekanizmas yalnzca okbiimli nesnelere uygulanabilir. Sistemlerin ounda, okbiimli her nesne, bu nesne iin ayrlm bellek alannda, ait olduu okbiimli snfa ilikin tutulan sanal ilev tablosunun adresini tutan bir gstericiye sahiptir. Bu gstericiye kavramsal olarak vptr (pointer to virtual functions table) denir. Daha nceki konularda, sanal ilev tablosu iinde sanal ilevlerin adreslerinin tutulduu belirtilmiti. te alma zaman tr bilgisinin elde edilmesi iin, derleyiclerin ou sanal ilev tablosunun banda, tr bilgilerinin tutulduu type_info snf nesnesinin adresini de tutar. Dolaysyla alma zaman tr bilgisinin elde edilmesi, aslnda gsterici ilemleriyle yaplr. nce nesnenin sanal ilev tablo gstericisine eriilir. Bu gstericiden, sanal ilev tablosunun adresi alnr. Sanal ilev tablosundan da, ilgili type_info nesnesinin adresi elde edilir. Maliyetin bir sanal ilev arsnn maliyetine edeer olduu dnlebilir. Her veri tr iin bir type_info nesnesinin var olmas, zellikle byk programlar iin fazladan yk getirir. Yzlerce okbiimli snfa sahip bir programda yine yzlerce type_info nesnesi yaratlr. Tm bu nesneler bellekte yer kaplar. Btn snf nesneleri gibi type_info snf nesnelerinin de yaratlmasnn da zamansal bir maliyeti vardr. RTTI program iinde hi kullanlmam olsa da type_info nesneleri yine yaratlr. te bu yzden derleyiciler RTTI mekanizmasn bir anahtar ile aktif hale getirilmesine izin verir. RTTI mekanizmasnn devre d braklmas, alabilir kodun klmesine ve alma hznn artmasna neden olur.

typeid leci ile dynamic_cast lecinin Maliyet Asndan Karlatrlmas


typeid ilecinin bir deer retmesi her nesne iin "sabit" bir sre iinde gerekleir. Sre her okbiimli nesne iin, tretme snf hiyerarisi iinde yeri ne olursa olsun sabittir. Bu sre bir sanal ilevin ars iin harcanan sreye edeer kabul edilebilir.

282/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin typeid(nesne) gibi bir ifadenin edeeri *(nesne.vptr[0]) gibi bir ileme karlk geldii dnlebilir. Ancak dynamic_cast ilemi iin geen sre, sabit deildir. dynamic_cast<T *>(base_ptr) gibi bir ifadenin deerlendirilmesi iin geen sre, dntrlme ilemine sokulan snfn, ait olduu snf hiyerarisinin derinliinin artmasyla artar. Yukardaki ifadede base_ptr gstericisinin ait oldugu snfn ok derin bir tretme zincirinin en tepesindeki snf olduunu dnelim. Dntrlmenin yaplaca tr de snf hiyerarisi iinde yer almayan bir tr olsun. dynamic_cast ilecinin NULL adresi retmesi iin tretme zincirinin en alt kademesine kadar gidilip kontrol yaplmas gerekir. Tasarm asndan dynamic_cast ileci typeid ilecine tercih edilmelidir. nk dynamic_cast ileci ile oluturulan kodun esneklii ve gelitilebilirilii type_id ileci ile oluturulmu koda gre daha fazladr. Ancak buna kar dynamic_cast ilecinin maliyeti de typeid ilecinin maliyetine gre daha fazladr.

alma Zamannda Tr Belirlenmesinin Sakncalar


Sanal ilev yaps her zaman RTTI aralarna tercih edilmelidir. RTTI aralarnn, bir zorunluluk bulunmamasna karn kullanlmas, programlarn karmakln arttrr, test ilemlerini zorlatrr. Kaynak kod deiikliklere kar daha krlgan hale gelir. Programn almas yavalayabilir. RTTI aralarnn kullanlmas konusunda bir zorunluluk varsa, bu durumda tasarm asndan, nce dynamic_cast ileci tercih edilmelidir. C++ dilinde Nesne Ynelimli Programlama Teknii kullanlarak yazlm programlarda, program tasarm, dier programlama dillerinde olduundan ok daha fazla neme sahiptir. alma zamannda tr bilgisinin belirlenmesine ynelik gereksinim, nesne ynelimli programlama tekniinin iyi bir ekilde uygulanmas durumunda, ou zaman ortadan kalkar. RTTI ou zaman programcnn nesne ynelimli programlama konusunda yeterli deneyime sahip olmamas nedeniyle bavurulan bir yntemdir. yi bir tasarmda yalnzca zorunlu durumlarda kullanlmaldr. Peki dinamik tr kontrolnn yaplmasnda tasarm asndan sorunlu olan durum nedir? Aa doru dnmler gvenli bir ekilde yaplabilmesine karn, genel olarak aadaki sakncalardan sz edilebilir: 1. Aa doru dnmler, karmakln hizmet veren kodlardan hizmet alan kodlara, yani kullanc kodlarna kaydrlmasna neden olur. Bylece kodlarn karmakl artar, buda kodlama maliyetinde bir arta neden olur. 2. Karmakln kullanc kodlarna kaymas kodlarn genel olarak bymesine neden olur. 3. Dnmn gvenli olup olmadn snamak iin, her dnm iin ayr bir kod yazmak gerekir. Bu durumda bakm giderleri artar. 4. Bu dnmler programn almasnda genel bir yavalamaya neden olur. Yavalama oran da, snf hiyerarisinin derinlii orannda artar. 5. Programn yaplacak eklemeler zorlar. ou zaman, tretme hiyerarisine eklenen snflar iin ek snamalar ya da kontroller yapmak gerekir.

283/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Ana sorun daha nce yazlm bir kod parasna ulaabilmek iin, fazladan bir kod yazma zorunluluudur. stelik fazladan yazlan kod, programn geniletilmesi amacnda daha sonradan sorunlara yol aabilir. Fazladan yazlan kod, bir snf tr iin alma zamannda, o snf tr iin belirli ilevlerin arlp arlamayacan snamak iin yazlan kodlardr. Ek kod iinde yaplan snama olumlu sonulanrsa aa doru bir dnm yaplarak, istenen ilev arlr. Eer daha nce yazlm olan bir kodu (server code) bulmak iin programc ek bir kod yazm ise, programcnn yazm olduu ek kod karmaktr, eitli hatalara aktr. Oysa nesne ynelimli Programlama tekniinin ana zelliklerinden birisi karmakl indirgemektir. Dinamik tr kontrolunun uyguland ou durumda programcnn, ana kodda oluturulan tretme hiyerarisi hakknda bilgi sahibi olmasn zorunlu klar, bu durumda da ileride ana kodda kullanlan snf hiyerarisi yapsnda bir deiiklik yapldnda, yazlan kodlarn geersiz bir duruma dmesine, yeniden yazlmak zorunda kalnmasna neden olabilir, bu durum da yine nesne ynelimli programlama tekniinin genel felsefesine aykrdr.

Aa Doru Dnmlere Seenek: Sanal levler


RTTI aralarn ve aa doru dnmleri (downcast) kullanmak yerine, dinamik balama ve sanal ilevler kullanlabilir. Bu teknikte, yalnzca tremi snflarda kullanlacak ye ilevler genelletirilerek taban snfa kaydrlr. Yani kullanc kodlar, sanal ilevler biiminde nesnenin iine kaydrlr. Bu durumda aa doru dnmler derleyiciler tarafndan hem otomatik olarak hem de gvenli bir ekilde yaplr. Aa doru yaplan bir dnm eer gvenilir deilse, zaten derleme aamasnda derleyici tarafndan saptanr. Ayrca bu tr bir tasarm, sz konusu programn gelitirilebirlii konusunda da son derece esnek bir yap salar. Yeni bir tretme yaplmas durumunda var olan ana snf kodlarnda bir deiiklik yaplmas gerekmez. Aa doru dnm ve izleyen if deyiminden oluan kod paracklar ou zaman, sanal ilev arlaryla deitirilebilir. Buradaki anahtar faktr, yetenek sorgulamasnn bir hizmet isteine dntrlmesidir. Nesneye yaplan bir hizmet alma istei, bir sanal ilev arsdr.

284/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

new ve delete lelerinin Yklenmesi


new ilecinin dinamik bir snf bir snf nesnesi elde etmek iin kullanldn dnelim: class A { //... }; new A *pd = new A; new ilecinin standartlar tarafndan gvence altna alnan davran yledir: new ileci operator new isimli bir global ilevi ararak free store alanndan sizeof(A) kadar byklkte bir blok elde eder. Daha sonra, elde edilen dinamik bellek blounun balang adresi, A snfnn kurucu ilevine this adresi olarak geirilir. Bylece snfn kurucu ilevi, dinamik olarak elde edilen bellek blounda, A snf trnden bir nesne oluturur. new ileci ve operator new terimleri sklkla birbirleriyle kartrlr. new, C++ dilinin bir ilecidir. Programc hangi ilevi tanmlarsa tanmlasn, new ilecinin yukarda aklanan davrann deitiremez. Ancak programc isterse, new ilecinin free store alanndan yer elde etmek amacyla ard operator new ilevini ykleyebilir. Gerekte yklenen new ileci deil operator new ilevidir.

operator new levi


Tek bir snf nesnesi iin free store alanndan yer elde etme ilemleri standart bir ilev olan operator new tarafndan yaplr. Bu ilevin bildirimi void *operator new(size_t); biimindedir. levin parametresi, new ilecinin terimi olmu ifadenin sizeof deeridir. Bu ilev parametresine aktarlan byklkte bir dinamik bellek blou elde ederek bloun balang adresini dndrr. new ilecinin kullanlmas durumunda, ilecin terimi olan trn sizeof deeri derleyici tarafndan hesaplanarak bu ileve argman olarak gnderilir. Yani new A gibi bir ifadenin, derleyici tarafndan nce aadaki gibi bir ilev arsna dntrldn dnebilirsiniz: operator new(sizeof(A)) Bu ardan elde edilen adresle A snfnn kurucu ilevinin arld dnlebilir. Programc isterse operator new ilevini dinamik bir blok elde etme amacyla dorudan da arabilir. Aadaki rnei inceleyin: #include <iostream> #include <cstring> using namespace std; int main() { char str[100]; cout << "bir yazi girin "; cin >> str; char *ptr = (char *)operator new(strlen(str) + 1);

285/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

strcpy(ptr, str); strrev(ptr); 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.

Global operator new levinin Yklenmesi


levin global olarak tanmlanmas durumunda new ileciyle yaplan btn dinamik yer elde etme ilemlerinde, programc tarafndan tanmlanan new ilevi arlr. Yklenen ilevin geri dn deeri void * trnden olmaldr. Baka trden bir geri dn deeri seilmesi geersizdir. levin parametre deikeni size_t trnden olmaldr. Parametre deikeninin baka bir trden olmas geersizdir. Aada global operator new ilevinin yklenmesine ilikin bir rnek veriliyor. Program derleyerek altrn: #include <iostream> #include <cstdlib> class A { int a, b; public: A(); }; using namespace std; A::A() { cout << "A::A()" << endl; cout << "this = " << this << endl; a = b = 0; } void *operator new(size_t size) { cout << "operator new()" << endl; cout << "size = " << size << endl; void *ptr = malloc(size); if (!ptr) { cerr << "malloc basarsz" << endl; exit(EXIT_FAILURE); } cout << "elde edilen blogun adresi : " << ptr << endl; return ptr; }

286/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin int main() { cout << "sizeof(A) = " << sizeof(A) << endl; A *pd = new A; cout << "pd = " << pd << endl; //... return 0; }

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 operator new() size = 8 elde edilen blogun adresi : 0x3d3dc0 A::A() this = 0x3d3dc0 pd = 0x3d3dc0

Global operator new ilevi 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.

operator delete levi


delete C++ dilinin bir ilecidir. delete ilecinin terimi bir snf trnden adres olduunda, derleyici nasl bir kod retir? nce delete ilecinin terimi olan adres ile snfn sonlandrc ilevi arlr. Daha sonra operator delete isimli global bir ilev arlarak dinamik olarak elde edilen bloun free strore alanna geri verilmesi salanr. ptr gsterici deikeninin, A snf trnden dinamik bir nesneyi gsterdiini dnelim: delete ptr gibi bir ifadenin, derleyici tarafndan aadaki gibi bir koda dntrld dnlebilir: ptr->~A(); operator delete(ptr); delete ilecinin bu davran standartlar tarafndan gvence altna alnmtr. Yazlacak yeni ilevlerle bu davran deitirilemez. Ancak istenirse, delete ilecinin dinamik bellek blounu free store a geri vermek amacyla kulland, operator delete isimli ilev yklenebilir. Bu durumda delete ileci kullanldnda, artk programcnn yazd operator delete ilevi arlr. levin bildirimi aadaki gibidir:

287/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

void operator delete(void *); lev, parametre deikenine balang adresi geilen dinamik bellek blounu free store alanna geri verir. stenirse operator delete ilevi dorudan da arlabilir: #include <iostream> using namespace std; int main() { cout << "kac tamsayi : "; int n; cin >> n; int *pd = (int *)operator new(n * sizeof(n)); for (size_t k = 0; k < n; ++k) { pd[k] = rand() % 1000; cout << pd[k] << " "; } cout << endl; //... operator delete(pd); return 0; }

Yukardaki rnekte operator new ile elde edilen bellek blou, daha sonra standart operator delete ilevi ile geri veriliyor.

operator delete levinin Yklenmesi


Programcnn yazaca operator delete ilevinin geri dn deeri olmamal ve ilevin ilk parametre deikeni void * trnden olmaldr. Daha nce verilen rnee imdi aadaki eklemeleri yapyoruz. nce operator delete ilevini yazyoruz: void operator delete(void *ptr) { cout << "operator delete()" << endl; cout << "geri verilecek blogun adresi : " << ptr << endl; free(ptr); }

rnekteki A snf iin, bir de sonlandrc ilev ekliyoruz: A::~A() { cout << "A::~A()" << endl; cout << "this = " << this << endl; } main ilevinin sonunda, new ileci ile elde edilen dinamik bellek blounu geri vermek iin delete ileci kullanlyor:

288/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin int main() { cout << "sizeof(A) = " << sizeof(A) << endl; A *pd = new A; cout << "pd = " << pd << endl; delete pd; return 0; } Eklemelerden sonra program yeniden derlenip altrldnda elde edilen rnek bir ekran kts aada veriliyor: sizeof(A) = 8 operator new() size = 8 elde edilen blogun adresi : 0x3d3dc0 A::A() this = 0x3d3dc0 pd = 0x3d3dc0 A::~A() this = 0x3d3dc0 operator delete() geri verilecek blogun adresi : 0x3d3dc0

operator new[] levi


Birden fazla sayda nesne iin bir dinamik bellek blounun elde edilmesi iin new[] ileci kullanlr: A *pd = new A[n]; new A[10] ifadesi ile nce operator new[] isimli global ilev arlarak, A snf trnden n tane nesnenin kaplayaca kadar bir bellek blou elde edilir. Daha sonra A snfnn kurucu ilevi her bir dinamik nesne iin ayr ayr arlr. Yani new[] ileci dinamik bellek alan elde etmek iin operator new[] ilevini kullanr. Standartlara gre derleyicilerde byle bir ilevin tanml olmas gerekir. levin bildirimi aadaki gibidir: void *operator new[](size_t size); levin geri dn deeri ayrlan dinamik bloun balang adresidir. levin parametre deikeni ayrlacak dinamik bellek blounun bykldr. new[] ilecinin kullanlmas durumunda blok bykl derleyici tarafndan hesaplanarak operator new ilevine argman olarak geilir. Standart ktphanedeki operator new ilevi kendi tanm iinde global operator new ilevini arr: void *operator new[](size_t size) { return operator new(size); } Yani programc operator new[] ilevini yklememi olsa bile new[] ile yaplan yer elde etme ilemlerinde yine operator new ilevi arlr. Ancak isterse programc operator new[] ilevini de tanmlayabilir. Bu durumda new[] ileci kullanldnda yer ayrma ilemi programcnn yazaaca operator new[] ilevi ile yaplr. Programcnn yazaca ilevin geri dn deeri void * trnden olmaldr. levin en az bir parametre deikeni olmal ve ilevin ilk paramatre deikeni size_t trnden olmaldr.

operator delete[] levi

289/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

new[] ileci ile elde edilen dinamik bloklar delete[] ileci ile geri verilir. delete[] ilecinin terimi bir snf trnden adres olduunda derleyici yle bir kod retir: nce ilece verilen adresten balayan blok iinde yer alan btn snf nesnleri iin tek tek snfn sonlandrc ilevi arlr. Sonra operator delete ileviyle, daha nce elde edilmi olan dinamik blok free store a geri verilir. Yani delete[] ileci sonlandrc ilevi n kez ardktan sonra operator delete[] ilevini kullanarak ayrlan dinamik blou geri verir. operator delete standart bir ilevdir bildirimi aadaki gibidir: void operator delete[](void *ptr); levin geri dn deeri yoktur. lev dinamik olarak ayrlm bir bellek blounun balang adresini alr, blou free store a geri verir. Aslnda operator delete[] ilevi de kendi iinde operator delete ilevini arr: void operator delete[](void *ptr) { operator delete(ptr); } Tabii programc isterse operator delete[] ilevini ykleyebilir. Programcnn yazaca operator delete[] ilevinin de geri dn deeri olmamaldr. levin en az bir parametre deikeni olmal ve bu parametre deikeninin tr void * olmaldr. Daha nce yazlan programn main ilevini aadaki gibi deitirelim: int main() { A *p = new A[4]; cout << "p = " << p << endl; delete[] p; return 0; }

Program yeniden derlenip altrldnda rnek bir ekran kts aadaki gibi olabilir: operator new() size = 36 elde edilen blogun adresi : 0x3d3ec0 A::A() this = 0x3d3ec4 A::A() this = 0x3d3ecc A::A() this = 0x3d3ed4 A::A() this = 0x3d3edc p = 0x3d3ec4 A::~A() this = 0x3d3edc A::~A() this = 0x3d3ed4 A::~A() this = 0x3d3ecc A::~A() this = 0x3d3ec4 operator delete() geri verilecek blogun adresi : 0x3d3ec0

290/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

Operator new ve operator delete levlerinin Bir Snf in Yklenmesi


Bir snf kendisi iin yaplacak dinamik yer elde etme ilemlerini kendi ye ilevleriyle yrtebilir. Eer bir snf iin operator new ve operator delete ilevleri arlrsa, global ilevler deil programcnn snf iin yazd ilevler arlr.

Snfn operator new() levi


Bir snf iin operator new() ilevi yazlabilir. Derleyici, new ilecinin terimi olarak bir snfa ilikin tr isminin verildiini grrse, nce sz konusu snfn, ismi operator new olan bir ilevinin bulunup bulunmadn aratrr. Snfn byle bir ilevi varsa, new ileci dinamik yer elde etme iin bu ilevi kullanr. Snfn byle bir ilevi yoksa bu snf tr iin new ilecinin kullanlmas sonucu yaplacak dinamik yer elde etme ilemi yine global operator new ilevi ile yaplr. new ilecinin bir snf iin yklenmesiyle, bir snfa ilikin dinamik yer elde etme ilemlerinin o snfa zg bir ekilde, dier snf trlerinden farkl bir biimde yaplmas salanabilir. Ayn ekilde bir snf iin operator new[] ilevi de tanmlanabilir. Programc isterse bir snfn operator delete ya da operator delete[] ilevlerini de yazabilir. Bylece belirli bir snfa ilikin dinamik alan geri verme ilemleri yine o snfa zg bir biimde yaplabilir. Snf iin yazlacak operator new ilevinin geri dn deeri void trden bir adres olmaldr. Aada rnek bir bildirim grlyor: class A { public: void *operator new(size_t); }; A *ptr = new A;

new ilecinin teriminin A snf olduunu gren derleyici, A snfnn operator new ilevine sahip olup olmadn aratrr. A snf iin operator new ilevi tanmlanm olduundan bu ilev arlr. levin size_t trnden olan parametresine sizeof(A) deeri geilir. Snf iin operator new ve operator delete ilevlerinin tanmlanmas ya da tanmlanm ilevlerin kaynak koddan kartlmas new ve delete ilelerinin kullanld kodlarda bir deiiklik yaplmasn gerektirmez. nk global operator new ilevi de programcnn snf iin tanmlad operator new ilevi de ayn ifade ile arlr. Programc operator new ilevini tanmlam olsa bile, global olan ilevi arabilir: A *ptr = ::new A; A snfnn operator new ilevi olmasna karn, new ilecinin nek konumundaki znrlk ileci ile kullanlmas sonucu standart ktphanedeki operator new ilevi arlr.

Snfn operator delete levi


Snf iin tanmlanacak operator delete ilevinin geri dn deeri olmamal, ilevin birinci parametre deikeni void * trnden olmaldr. A snfna ilikin operator delete ilevinin bildirimi aadaki gibi olabilir: class A { public: void *operator new(size_t); void operator delete(void *); //... }

291/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

delete ilecine verilen adres bir snf trnden ise, derleyici bu snfn operator delete ilevine sahip olup olmadn aratrr. Eer snfn operator delete ilevi bildirilmi ise bildirilen ilev arlr. Aksi halde global operator delete ilevi arlr. A *ptr = new A; //... delete ptr; ifadeleri iin derleyici yle bir kod retir: ptrnin gsterdii snf nesnesi iin nce snfn sonlandrc ilevi arlr. Sonra ptr adresi A snfnn operator delete ilevine argman olarak gnderilir. operator new, operator[], operator delete, operator delete[] ilevleri snfn statik ilevleridir. Bu ilevlerin bildirimlerinde static anahtar szc yazlmam olsa bile bunlar derleyici tarafndan statik ye ilevler olarak ele alnr. Tabii programc isterse bu ilevlerin bildirimlerinin nne static anahtar szcn yerletirebilir. Bu ilevler statik ye ilevler olduklarndan snfn statik olmayan elemanlarna dorudan ulaamaz. operator new ve operator delete ilevlerinin bir snf iin yklenmesine rnek olarak aada kk bir program veriliyor. Program derleyerek altrn: #include <iostream> #include <cstring> #include <new> typedef unsigned char Byte; class A { char s[20]; //yalnzca snf nesnesi iinde yer kaplasn diye static Byte allocation_pool[]; static Byte allocation_map[]; public: static const size_t max_object_size = 10; A(){std::cout << "A()\n";} ~A(){std::cout << "~A()\n";} void *operator new(size_t); void operator delete(void *); }; //cpp dosyas using namespace std; Byte A::allocation_pool[max_object_size * sizeof(A)]; Byte A::allocation_map[max_object_size] = {0}; void *A::operator new(size_t) { Byte *ptr = (Byte *)memchr(allocation_map, 0, max_object_size); if (!ptr) { cout << "bellek yetersiz" << endl; throw bad_alloc(); } int block_no = ptr - allocation_map; cout << block_no << "no lu blok kullanlyor" << endl; *ptr = 1; return allocation_pool + (block_no * sizeof(A)); } void A::operator delete(void *ptr) { if (!ptr) return; unsigned long block = reinterpret_cast<unsigned long>(ptr) reinterpret_cast<unsigned long>(allocation_pool); block /= sizeof(A);

292/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin cout << block << "no lu blok geri veriliyor!" << endl; allocation_map[block] = 0; }

int main() { A *pa[A::max_object_size]; try { for (size_t i = 0; i < A::max_object_size; ++i) pa[i] = new A; new A; } catch (bad_alloc) { cerr << "bellek yetersiz!" << endl; } delete //geri A *ptr delete pa[3]; verilen bellek aln kullaniliyor! = new A; ptr;

for (size_t i = 0; i < A::max_object_size; ++i) delete pa[i]; return 0; }

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.

Yeri Belirli new leci


new ilecinin baka bir biimi olan yeri belirli new ileci ile bir snf nesnesi istenilen bir adreste yaratlr. lecin kullanlmas iin new balk dosyasnn koda eklenmesi gerekir. Bu ile kullanldnda dinamik bir blok elde etmek iin bir ilev arlmaz. nk zaten snf nesnesi ilece verilen adreste yaratlr. lecin kullanmnn genel biimi aadaki gibidir: new(adres) tr ismi

293/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

lecin ayra iindeki terimi herhangi trden adres olabilir. lecin rettii deer tr ismiyle belirlenen trden bir adrestir. Eer tr bir snf tr ise derleyicinin rettii kod sonucunda o snfn kurucu ilevi arlr. Aadaki kod parasn inceleyin:

#include <iostream> #include <new> class A { int a, b; public: A():a(0), b(0) {std::cout << "A::A()" << std::endl;} ~A(){std::cout << "A::A()" << std::endl;} void display()const {std::cout << "a = " << a << "\n" << "b = " << b << std::endl;} }; int main() { char s[sizeof(A)]; A *ptr = new(s)A; ptr->display(); ptr->~A(); }

rnekte sizeof(A) byte boyutunda yerel dizi tanmlanyor. Bu dizinin balang adresinin new ilecine verildiini gryorsunuz. le dardan ald adresle snfn kurucu ilevinin arlmasn salyor. Ancak daha sonra snfn sonlandrc ilevin arlmas isteniyorsa ar ak bir ekilde yaplmaldr: ptr->~A(); deyimiyle ptr gstericisinin gsterdii yap nesnesinin sonlandrc ilevi arlyor. C++ da bir snfn sonlandrc ilevinin ak bir ekilde arlmas gereken tek yer burasdr. Eer programc delete ptr gibi bir deyim yazsayd bu alma zamanna ilikin bir hata olurdu. Dinamik olarak elde edilen bir blok olmamasna karn, operator delete ileviyle ptr deikeninin deeri olan adres free store alanana geri verilmeye allm olurdu. Yeri belirli new ilecinin de keli ayral biimi vardr. lecin bu biimi kullanlarak, verilen bir adresten balayarak birden fazla snf nesnesinin yaratlmas salanabilir. Aada daha nce yazlan main bu kez ilevin keli ayra biimini kullanacak biimde yeniden tanmlanyoruz:

294/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin int main() { char s[3 * sizeof(A)]; A *ptr = new(s)A[3]; for (int k = 0; k < 3; ++k) ptr[k].display(); for (int k = 0; k < 3; ++k) ptr[k].~A(); return 0; }

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.

nothrow new leci


new ve new[] ilelerinin nothrow parametreli bir biimleri de vardr. Bu ileler dinamik bellek alan elde edilemedii zaman bad_alloc snf trnden bir hata nesnesi gndermek yerine NULL adresi retir. Bu ilelerin dinamik blok elde etmek iin ardklar global ilevlerin bildirimleri yledir: void *operator new(size_t, const nothrow_t &) throw(); void *operator new[](size_t, const nothrow_t &) throw(); Grld gibi bu ilevlerin bildirimi hata nesnesi belirleme szdizimiyle yaplm, bu ilevler iinden bir hata nesnesi gnderilmedii bildirilmitir. nothrow_t ilevlerin imzalarn farkl klmak iin tanmlanan bo bir yap trnn ismidir. new balk dosyas iinde nothrow_t trnden ismi nothrow olan bir nesne bildirilmitir. struct nothrow_t { }nothrow; Bu ileler aadaki gibi kullanlr: class A{ //... }; void func() { 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:

295/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin 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: typedef void (*new_handler)(); new_handler set_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 srekli arr. Bu kural set_new_handler ilevine sunulacak ilevin tasarmna baz olanaklar salar. Bu ilev aadaki biimlerde tasarlanabilir: 1. lev daha fazla bir bellek alann operator new ilevinin hizmetine sunabilir. Bylece ilev iini bitirdiinde artk bellek yetersizlii sz konusu olmayacandan, operator new ilevi bad_alloc snf trnden bir hata nesnesi gndermez, iini baaryla tamamlar. Program baladnda byk bir bellek blou elde edilir, ilev bu bellek alann kk paralar halinde operator new ilevinin kullanmna sunabilir. 2. set_new_handler ileviyle yeni bir ilev kayt edilebilir. Bylece operator new bu kez yeni ilevi arr. Yeni kayt edilen ilev bellek alan elde etmek iin baka i yapabilir ya da baka bir algoritma kullanabilir, bylece operator new ilevi iin bir bellek alan yaratlabilir. 3. lev set_new_handler ilevini NULL adresiyle arr. Bylece ortada arlmas gereken bir ilev kalmadndan operator new ilevi bad_alloc snf trnden bir hata nesnesi gnderir. 4. levin kendisi bad_alloc snf trnden ya da bad_alloc snfndan tretilmi bir snf trnden hata nesnesi gnderir. Bu durumda gnderilen hata nesnesi operator new ilevi tarafndan yakalanamaz. Bylece gnderilen hata nesnesi, new ilecinin kulland yerde ya da daha yksek seviyede yakalanmaya braklabilir.

296/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

OKLU TRETME
Bir snf tek bir tretme ilemiyle birden fazla taban snftan tretmeye oklu tretme (multiple inheritance) denir. class A { // }; class B { // }; class C : public A, public B { // };

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 class C :public A, 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:

oklu Tretmede Bilinirlik Alan ve sim Arama


oklu tretme durumunda tremi snf, btn taban snflara eriebilir. Taban snflara eriim konusunda, hibir taban snfn dierine gre stnl yoktur. Btn taban snflar ayn bilinirlik alan iinde kabul edilir. Her iki taban snfta da ayn isimli bir eleman ya da ye ilev varsa, elemana eriim znrlk ileci ile yaplmamsa bu durum ift anlamllk hatasna yol aar. Tremi snf bilinirlik alannda bir isim kullanldnda bu

297/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

isim nce tremi snf bilinirlik alannda aranr. Eer bulunamaz ise bu kez bir sra gzetmeksizin, taban snflarn her birinin bilinirlik alan iinde aranr. Aadaki rnei inceleyin: #include <iostream> class Base1 { public: void func() {std::cout << "Base1::func()" << std::endl;} }; class Base2 { public: void func() {std::cout << "Base2::func()" << std::endl;} }; class Mder : public Base1, public Base2 { //... }; int main() { Mder md; //md.func(); Geersiz! Cift anlamllk hatas md.Base1::func(); //Geerli md.Base2::func(); //Geerli return 0; }

Yukardaki programda Mder snf Base1 ve Base2 snflarndan oklu tretme ile elde ediliyor. Hem Base1 hem de Base2 snflar iinde func isimli ilevin bildirildiini gryorsunuz. main ilevi iinde tanmlanan Mder snf trnden md nesnesi ile, func ilevinin arlmas derleme zamannda ift anlamllk hatas oluumuna neden olur. Ancak istenirse znrlk ilecinin kullanlmasyla, yani ismin nitelenmesiyle ift anlamllk hatas ortadan kaldrlabilir. stenilen taban snfn func isimli ilevinin arlmas salanabilir: md.Base1::func() arsyla Base1 snfnn func ilevi md.Base2::func(); arsyla ise Base2 snfnn func ilevi arlabilir. oklu tretmenin farkl kollarndaki ayn isimli ilevler arasnda ilev yklemesi (function overloading) yaplamaz. nk imzalar farkl, ayn isimli ilevlerin bulunmas ayn bilinirlik alanna zgdr. Aadaki kodu inceleyin: #include <iostream> class Base1 { public: void func(int) {std::cout << "Base1::func(int)" << std::endl;} }; class Base2 { public: void func(double) {std::cout << "Base2::func(double)" << std::endl;} };

298/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Mder : public Base1, public Base2 { //... }; int main() { Mder md; //md.func(3.7); Geersiz! ift anlamllk hatas md.Base1::func(5); //Geerli. md.Base2::func(3.14); //Geerli. return 0; } Yukardaki kodda, Base1 snf iinde double trden parametre deikenine sahip func isimli bir ilev tanmlanrken, Base2 snf iinde int parametreli ayn isimli bir ilev tanmlanyor. main ilevi iinde tanmlanan Mder snf trnden md nesnesi ile yaplan md.func(3.7); ars yine ift anlamllk hatas oluturur. nk farkl bilinirlik alanlarnda bulunan, parametrik yaplar birbirinden farkl olan func ilevleri iin ilev yklemesi yaplmas mmkn deildir. Ancak istenirse nitelenmi ismin kullanlmasyla bu ilevlerden istenilen birinin arlmas salanabilir: md.Base1::func(5); ars ile Base1 snfnn func isimli ilevi arlrken md.Base1::func(3.14); ars ile Base2 snfnn func isimli ilevi arlm olur.

Taban Snflarn Kurucu levlerinin arlmas


oklu tretmede taban snfn varsaylan kurucu ilevleri, taban snflarn bildirim srasna gre arlr. Yani nce bildirilen taban snfn kurucu ilevi daha nce arlr. Tremi snfn kurucu ilevlerin bana eklenen bir kodla, bildirim sralarna gre tm taban snflarn kurucu ilevlerinin arlmas salanr. Sonlandrc ilevlerin ars ters srada olur. Aadaki rnei derleyerek altrn ve kurucu/sonlandrc ilevlerin arlma srasn gzlemleyin: #include <iostream> class Base1 { public: Base1() {std::cout << "Base1::Base1()" << std::endl;} ~Base1() {std::cout << "Base1::~Base1()" << std::endl;} }; class Base2 { public: Base2() {std::cout << "Base2::Base2()" << std::endl;} ~Base2() {std::cout << "Base2::~Base2()" << std::endl;} };

class Mder : public Base1, public Base2 { public: Mder() {std::cout << "Mder::Mder()" << std::endl;} ~Mder() {cout << "Mder::~Mder()" << endl;} };

299/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

void func() { Mder mder; } int main() { func(); return 0; } Taban snflarn varsaylan kurucu ilevlerinin deil de parametreli kurucu ilevlerinin arlmas isteniyorsa bunun iin MIL szdizimi kullanlmaldr.

oklu Tretilmi Snflarda Tremi Snf Taban Snf Dntrmeleri


Bir tremi snfn public tretmesi yoluyla birden fazla taban snftan tretilmesi durumunda, tremi snf nesnesi ayn zamanda taban snflardan herhangi birinin trnden nesneymi gibi kullanlabilir. rnein bir tremi snf nesnesinin adresi taban snflardan herhangi biri trnden gstericiye atanabilir. phesiz taban snflardan biri trnden gstericiye tremi snf trnden bir adres atandnda, atanan adres tremi snf nesnesinin ilgili taban snf alt nesnesinin adresidir. Aadaki rnei derleyip altrn: class Base1 { int b1; public: }; class Base2 { int b2; public: }; class Mder : public Base1, public Base2 { int m1; public: }; #include <iostream> int main() { Mder md; Base1 *base1_ptr = &md; Base2 *base2_ptr = &md; std::cout << "&md = " << &md << std::endl; std::cout << "base1_ptr = " << base1_ptr << std::endl; std::cout << "base2_ptr = " << base2_ptr << std::endl; return 0; }

Programn ekran ktsndan da greceiniz gibi, dntrme sonunda adresin saysal bileeni deiebilmektedir. Daha sonra ters bir dnmn yaplmas, yani adresin eski haline getirilmesi mmkn olmayabilir.

300/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

ki ayr snftan oklu tretmeyle elde edilmi tremi snf nesnesinin adresinin her iki taban snf trnden gstericiye de atanabilmesi, ilev yklemesi srasnda ift anlamllk hatasnn olumasna neden olabilir. Aadaki gibi iki ilevin bildirildiini dnelim: void display(const Base1 &) { std::cout << "display(const Base1 &)" << std::endl; } void display(const Base2 &) { std::cout << "display(const Base2 &)" << std::endl; } void foo() { Mder md; display(md); }

//Geersiz! ift anlamllk hatas

Tremi snf adreslerinin taban snf trlerinden adreslere dntrlmesinde, taban snflarn birbirlerine gre stnlkleri yoktur. md nesnesi ile yukardaki rnekte bildirilen her iki display ilevi de arlabilir. Bu durumda ift anlamllk hatas oluur. ift anlamllk hatas tr dtrme ilecinin kullanlmas ile ortadan kaldrlabilir: void foo() { Mder md; 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: class Base { int a; public: void func(); }; class Der1 : public Base { public: //... }; class Der2 : public Base { public: //... };

class Mder : public Der1, public Der2 { public: //... };

301/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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:

Grld gibi Base snf alt nesnesinden iki tane bulunmaktadr. Oysa bir tane bulunmas daha istenen bir durumdur. Bu yapya "elmas oluumu" denir (Dreadful Diamond of Derivation). Elmas oluumu bir ok durumda ift anlamllk hatasna yol aar. Aadaki main ilevini inceleyin: int main() { Mder md; //md.func(); md.Der1::func(); md.Der2::func(); return 0; }

//Geersiz! ift anlamllk hatas

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(); //Der1 snfndan alnan func ilevi arlyor. md.Der2::func(); //Der2 snfndan alnan func ilevi arlyor. 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: void func() { Mder mder; Base *base_ptr = &mder; //Geersiz! ift anlamllk hatas } 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));

302/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: #include <iostream> class Base { int m_a; public: Base():m_a(0){} void set(int val) {m_a = val;} int get() const {return m_a;} }; class Der1: public Base { public: void func(int x){set(x);} }; class Der2: public Base { public: void display()const {std::cout << get() << std::endl;} }; class MDer: public Der1, public Der2 { public: // }; int main() { MDer mder; mder.func(135); mder.display(); return 0; }

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

303/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

oklu Tretme Snflarnda Sanal levlerin Kullanlmas


oklu tretilmi snf her bir taban snfnn sanal ilevini ezebilirBu durumda oklu tretmeyle tremi snf iin, sanal ileve sahip taban snf says kadar ilev tablosu bulunmas gerekir. Bu tr durumlarda oklu tretilmi snf nesnesinin adresini taban snflardan birine ilikin gstericiye aktarp o gsterici yoluyla o snfn sanal ilevi arldnda, derleyici o gstericinin gsterdii yerde sanal ilev tablo gstericisini arar. #include <iostream> class Base1 { public: virtual void base1_vfunc() {std::cout << "Base1::base1_func()" << std::endl;} }; class Base2 { public: virtual void base2_vfunc() {std::cout << "Base2::base2_func()" << std::endl;} }; class Mder : public Base1, public Base2 { public: virtual void base1_vfunc() {std::cout << "Mder::base1_func()" << std::endl;} virtual void base2_vfunc() {std::cout << "Mder::base2_func()" << std::endl;} virtual void mder_vfunc() {std::cout << "Mder::mder_func()" << std::endl;} }; int main() { Mder md; Base1 *base1_ptr = &md; Base2 *base2_ptr = &md; base1_ptr->base1_vfunc(); base2_ptr->base2_vfunc(); return 0; }

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.

304/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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: class Base { int a; public: void func(); }; class Der1 : virtual public Base { public: //... }; class Der2 : virtual public Base { public: //... }; class Mder : public Der1, public Der2 { public: // };

Der1 ve Der2 snflar Base snfndan "sanal" olarak tretiliyor. virtual anahtar szc : atomundan sonra yer alyor. virtual anahtar szc public anahtar szcnden sonra da yazlabilir. Yukardaki tretme ilemlerinden sonra Mder snf trnden bir nesne tanmlandn dnelim: Mder md; Bu nesnenin bellekte yerleimi aadaki gibi olur:

Sanal Taban Snfn Kurucu levinin arlmas


Sanal tretmede kurucu ilevlerin arlmas durumunda bir farkllk vardr. oklu tretilmi bir snf nesnesi yaratldnda ilk olarak sanal taban snfn kurucu ilevi arlr. Sanal taban snfn kurucu ilevini en alttaki tremi snfn kurucu ilevi armaldr. En alttaki tremi snf (most derived class) nedir? A snfndan sanal tretme yoluyla B ve C snflarnn tretildiini daha sonra B ve C snflarndan oul tretme ile D isimli bir snfn tretildiini dnelim. Son olarak da D snfndan E isimli bir snf tretilmi olsun:

305/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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> class Base { public: Base() {std::cout << "Base::Base()" << std::endl;} }; class Der1 public: Der1() }; class Der2 public: Der2() }; : virtual public Base { {std::cout << "Der1::Der1()" << std::endl;} : virtual public Base { {std::cout << "Der2::Der2()" << std::endl;}

class Mder1: public Der1, public Der2 { public: Mder1() {std::cout << "Mder1::Mder1()" << std::endl;} }; class Mder2: public Mder1{ public: Mder2() {std::cout << "Mder2::Mder2()" << std::endl;} }; int main() { Mder2 mder2; Mder1 mder1; Der1 der1; Der2 der2; return 0; } 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:

306/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class Vbase { //... public: Vbase(int); //... }; class Base1: virtual public Vbase { // public: Base1():Vbase(0) {} }; class Base2: virtual public Vbase { //... public: Base1():Vbase(0) {} //... }; class Der1: public Base1, public Base2 { //... public: Der1():Vbase(0){} }; class Der2: public Der1{ //... public: Der2():Vbase(0){} };

Yukardaki rnekte Vbase isimli snfn varsaylan kurucu ilevi yok. Snfn yalnzca tek parametreli bir kurucu ilevine sahip olduunu gryorsunuz. Bu snftan sanal tretme yoluyla Base1 ve Base2 snflar tretiliyor. Base1 ve Base2 snflarnn kurucu ilevleri iinde M.I.L. szdizimiyle sanal taban snfn tek parametreli kurucu ilevi arlyor. Base1 ve Base2 snflarndan oklu tretme yoluyla Der1 snf tretiliyor. Der1 snfnn kurucu ilevinde de tepedeki sanal taban snf olan Vbase snfnn tek parametreli kurucu ilevi M.I.L szdizimi kullanlarak arlyor. Ayn durum Der1 snfndan tretilen Der2 snf iin de geerlidir. Hangi kademede olursa olsun tretme hiyerarisi iinde yer alan bir snf sanal taban snfn parametreli kurucu ilevini MIL szdizimiyle armak zorundadr. Aksi halde tremi snf trlerinden herhangi biri trnden nesne tanmlanmas durumunda derleme zamannda hata oluur.

oklu Sanal Tretmede Sanal levlerin Durumu


oklu sanal tretmede taban snflardaki bir sanal ilevin aaya doru tek bir sonlanan ilevi (final overrider) olmaldr. rnein aadaki gibi bir tretme emas sz konusu olsun:

307/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

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.

oklu Tretmenin Bir Taban Snfn Arayznn Onarlmas Amacyla Kullanlmas


Baz durumlarda hizmet veren bir taban snfn arayz programcnn elinde olmasna karn snfn kodlama dosyas yani kaynak dosya programcnn elinde bulunmaz. Bu durumda byle bir snf zerinde ancak kstl deiiklikler yaplabilir. Snfa normal bir ye ilev eklenebilir ama snfa bir sanal ilev eklenemez. Sanal olmayan bir ilev sanal ilev haline getirilemez. Byle bir durumda, hizmet veren taban snfda bu tr deiiklikler yaplmas mmkn olmad iin oklu tretme aracndan faydalanlr. Onarmak amacyla yaplan i zetle udur: nce baka bir soyut taban snf oluturulur. Bu soyut snfla elde ara yz olan hizmet veren taban snftan oul tretme yoluyla bir snf tretilir. Dier tretmeler artk oul tretilmi snftan yaplr. Aadaki rnei dikkatli bir ekilde izleyin: Elimizde aadaki gibi bir arayzn olduunu dnelim: ////server.h class ServerBase { public: virtual void vfunc() const; //sanal ilev void nvfunc()const; //sanal olmayan ilev (sanal olmasn istiyoruz) ~Base3rd(); //sanal olmayan sonlandrc ilev(tasarm hatas) }; class ServerDer: public ServerBase { public: void vfunc() const; //sanal ilev eziliyor ~Der3rd(); }; void global_func(const ServerBase3rd &); //ok biimli ileme yapan ilev Yukarda hizmet veren bir kaynak dosyann arayz grlyor. ServerBase snfnn vfunc isimli sanal bir ilevi var. Ancak snfn nvfunc isimli ye ilev sanal olmayan bir ilevdir. Bu arayzn onarlarak nvfunc ilevinin sanal bir ilev haline getirilmesi isteniyor. Yine snfn sonlandrc ilevi belki de ihmal sonucu sanal yaplm. Oysa kullanc kodlar ServerBase snfnn sonlandrc ilevinin sanal sonlandrc ilev olmasn istiyor. Bir de

308/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin

belirli bir amac gerekletirmesi amacyla snfa new_vfunc isimli bir sanal ilevin eklenmesi isteniyor. global_func1 ilevi ise taban snf referans yoluyla ok biimli ilem yapan global bir ilev. server. cpp dosyas elde bulunmad iin snfn kaynak kodlarn deitirme ansmz olmadn yeniden anmsatalm. server.cpp Kaynak dosyasnn da aadaki gibi olduunu dnelim: void ServerBase::vfunc()const { cout << "ServerBase::vfunc()const" << endl; //... } void ServerBase::nvfunc()const { cout << "ServerBase::nvfunc()const" << endl; //... } ServerBase::~ServerBase() { cout << "ServerBase::~ServerBase()" << endl; //... } void ServerDer::vfunc()const { cout << "ServerDer::vfunc()" << endl; //... } ServerDer::~ServerDer() { cout << "Der3rd::~Der3rd()" << endl; } void global_func(const Base3rd &r) { r.vfunc(); r.nvfunc(); }

Snama amacyla nce ismi NewBase olan bir snf tanmlayalm:

class NewBase { public: virtual void vfunc()const = 0; //saf sanal ilev virtual void nvfunc()const = 0; //saf sanal ilev //yeni arayuze iliskin ilev virtual void new_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:

309/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin class ServerBase { public: virtual void vfunc()const; //sanal ilev void nvfunc()const; //sanal olmayan ilev (sanal olmasn istiyoruz) ~ServerBase(); //sanal olmayan sonlandrc ilev (tasarm hatas) }; class ServerDer: public ServerBase { public: void vfunc() const; //sanal ilev eziliyor ~ServerDer(); }; #include <iostream> using namespace std; void global_func1(const ServerBase &); //okbiimli ileme yapan ilev //elimizde olmayan kodlama dosyas void ServerBase::vfunc()const { cout << "ServerBase::vfunc()const" << endl; //... } void ServerBase::nvfunc()const { cout << "ServerBase::nvfunc()const" << endl; //... } ServerBase::~ServerBase() { cout << "ServerBase::~ServerBase()" << endl; //... } void ServerDer::vfunc()const { cout << "ServerDer::vfunc()" << endl; //... } ServerDer::~ServerDer() { cout << "ServerDer::~ServerDer()" << endl; } void global_func(const ServerBase &r) { r.vfunc(); r.nvfunc(); } class NewBase { public: virtual void vfunc()const = 0; //saf sanal ilev virtual void nvfunc()const = 0; //saf sanal ilev //yeni arayuze iliskin ilev virtual void new_vfunc ()const = 0; virtual ~NewBase(){cout << "~NewBase()" << endl;} }; class Join: public NewBase, public ServerBase { public: void vfunc()const {cout << "Join::vfunc()" << endl; ServerBase::vfunc();} void nvfunc()const {cout << "Join::nvfunc()" << endl; ServerBase::nvfunc();} void new_vfunc ()const {cout << "Join::new_vfunc ()" << endl;}

310/311

C ve Sistem Programclar Dernei - C++ Ders Notlar - Necati Ergin ~Join(){cout << "Join::~Join()" << endl;} }; int main() { Join &join_ref = *new Join; NewBase &nbase = join_ref; nbase.vfunc(); cout << "*******************************************" nbase.new_vfunc(); cout << "*******************************************" global_func(join_ref); cout << "*******************************************" delete &nbase; cout << "*******************************************" return 0; }

<< endl; << endl; << endl; << endl;

311/311

You might also like