2. BÖLÜM SINIFLAR. Iste bugünkü dersimizde bu iki yeni özellik üstünde detayli birbiçimde duracagim.

Benzer belgeler
BTEP243 Ders 3. class Yazım Kuralı:

Fonksiyonlar. C++ ve NESNEYE DAYALI PROGRAMLAMA 51. /* Fonksiyon: kup Bir tamsayının küpünü hesaplar */ long int kup(int x) {

NESNEYE YÖNELİK PROGRAMLAMA

BİLİNİRLİK ALANI ve ÖMÜR, KONTROL DEYİMLERİ

Uzaktan Eğitim Uygulama ve Araştırma Merkezi

HSancak Nesne Tabanlı Programlama I Ders Notları

Pointer Kavramı. Veri Yapıları

10. DOSYA GİRİŞ ÇIKIŞ FONKSİYONLARI

Temel Bilgisayar Bilimleri Ders Notu #4-2. kısım

Örnek: İki fonksiyondan oluşan bir program. Fonksiyon Tanımı

/ C Bilgisayar Programlama Final Sınavı Test Soruları. Adı soyadı :... Öğrenci no :... İmza :... Tarih, Süre : , 60 dak.

NESNEYE YÖNELİK PROGRAMLAMA SINIFLAR

ELN1001 BİLGİSAYAR PROGRAMLAMA I

BİL-142 Bilgisayar Programlama II

Göstericiler (Pointers)

Programlama Dilleri 1. Ders 4: Diziler

Sınav tarihi : Süre : 60 dak. a) strstr b) strchr c) strcat d) strcpy e) strlen. a) b) d) e) 0

PROGRAMLAMAYA GİRİŞ FONKSİYONLAR

Uzaktan Eğitim Uygulama ve Araştırma Merkezi

YAPILAR BİRLİKLER SAYMA SABİTLERİ/KÜMELERİ. 3. Hafta

enum bolumler{elektronik, insaat, bilgisayar, makine, gida};

FONKSİYONLAR. Gerçek hayattaki problemlerin çözümü için geliştirilen programlar çok büyük boyutlardadır.

BLM-111 PROGRAMLAMA DİLLERİ I. Ders-12 Fonksiyonlar. Yrd. Doç. Dr. Ümit ATİLA

Diziler (Arrays) Çok Boyutlu Diziler

ELN1002 BİLGİSAYAR PROGRAMLAMA 2

/ C Bilgisayar Programlama Yıliçi Sınavı Test Soruları. Adı soyadı :... Öğrenci no :... İmza :... Tarih, Süre : , 60 dak.

1 PROGRAMLAMAYA GİRİŞ

C++0x - Sağ Taraf Değerine Bağlanan Referanslar (Rvalue References)

Nesneye Yönelik Programlama (OOP) 7.Hafta

PROGRAMLAMAYA GİRİŞ DERS 2

Hafta 13 Fonksiyonlar

NESNEYE YÖNELİK PROGRAMLAMA C++ a Giriş

HSancak Nesne Tabanlı Programlama I Ders Notları

Sunum İçeriği. Programlamaya Giriş

Temel Bilgisayar Programlama Final Sınavı Çalışma Notları

Sınav tarihi : Süre : 60 dak. a) ABCDE b) BCDE c) ABCD d) kod hatalı e) BCD

C de Detaylı Üs Alma Programı. C# Dilinde Metot Tanımlama ve Yazdırma

BLM-111 PROGRAMLAMA DİLLERİ I. Ders-8 Değişken Tipleri ve Temel Giriş/Çıkış İşlemleri

C# Metotlar ve Metot Tanımlama

PASCAL PROGRAMLAMA DİLİ YAPISI

Sınav tarihi : Süre : 60 dak. c) En başta #include<stdio.h> yazılmamıştır. c) zt d) Pi e) X0

NESNEYE YÖNELİK PROGRAMLAMA

Istanbul Teknik Üniversitesi. Bilisim Enstitüsü C++ Nesne Yönelimli Programlama. Ilave Notlar Sürüm 1.1 Nisan Düzenleyen

8. İŞARETCİLER (POINTERS)

Fonksiyonlar (Altprogram)

Özyineleme (Recursion)

Nesne tabanlı programlama nesneleri kullanan programlamayı içerir. Bir nesne farklı olarak tanımlanabilen gerçek dünyadaki bir varlıktır.

BİLG Dr. Mustafa T. Babagil 1

Gereksiz Kodlar. burada if deyiminin else bölümüne gerek var mı? İfade doğruysa zaten fonksiyon geri dönüyor. Bu aşağıdakiyle tamamen eşdeğerdir:

C++ Dersi: Nesne Tabanlı Programlama

Temel Giriş/Çıkış Fonksiyonları

ENF102 TEMEL BİLGİSAYAR BİLİMLERİ VE C/ C++ PROGRAMLAMA DİLİ. Gazi Üniversitesi Mühendislik Fakültesi Bilgisayar Mühendisliği Bölümü

Diziler. Yrd.Doç.Dr.Bülent ÇOBANOĞLU

Operator Aşırı Yükleme (Operator OverLoading)

ESM-361 Mikroişlemciler. 3. Hafta Ders Öğretim Üyesi Dr.Öğr.Üyesi Ayşe DEMİRHAN

C++ Dersi: Nesne Tabanlı Programlama

BİL-142 Bilgisayar Programlama II

sayi=3 harf=a reelsayi=8.72 Bellek durumu 5. İşaretç iler (pointers)

Genel Programlama II

Adı soyadı :... Öğrenci no :... İmza :... Tarih, Süre : dak.

Değişkenler tanımlanırken onlara ne tür veriler atanabileceği de belirtilir. Temel veri türleri oldukça azdır:

NESNEYE YÖNELİK PROGRAMLAMA

Sınav tarihi : Süre : 60 dak.

C++ Giriş Ders 1 MSGSU Fizik Bölümü Ferhat ÖZOK Kullanılacak kaynak: Published by Juan Soulié

Veri Yapıları. Amaçlar: Temel Veri Yapılarını Tanımlamalı Veri Yapılarını Veri Modeli ve Türlerini Öğreneceksiniz. İçindekiler:

BİLGİSAYAR TEMELLERİ VE PROGRAMLAMAYA GİRİŞ

Fonksiyonlar -Genel Test- A

ALGORİTMA VE PROGRAMLAMA I

Programlama Dilleri 1. Ders 12: Belirleyiciler ve Niteleyiciler

ALGORİTMA VE PROGRAMLAMA I

ALGORİTMA VE PROGRAMLAMA I

Öğr. Gör. Serkan AKSU 1

BÖLÜM 11: YAPISAL VERİ TİPLERİ

BĠLGĠSAYAR PROGRAMLAMA II C++ Programlamaya GiriĢ Published by Juan Soulié

BMT 101 Algoritma ve Programlama I 11. Hafta. Yük. Müh. Köksal Gündoğdu 1

YAPILAR (STRUCTURES)

Operatörlere Yeni İşlevler Yüklenmesi (Operator Overloading)

Programlama Dilleri. C Dili. Programlama Dilleri-ders08/ 1

ALGORİTMA VE PROGRAMLAMA II

BASİT C PROGRAMLARI Öğr.Gör.Dr. Mahmut YALÇIN

ANA SINIF TÜRETİLEN BİRİNCİ SINIF TÜRETİLEN İKİNCİ SINIF

C Programlama Dilininin Basit Yapıları

C++ Dersi: Nesne Tabanlı Programlama

Dr. Fatih AY Tel: fatihay@fatihay.net

DÖNGÜ DEYİMLERİ (while, do while, for)

3/7/2011. ENF-102 Jeoloji 1. Tekrar -- Değişken Tanımlamaları (Definition) ve Veri Türleri (Data Type) Veri Tanımları ve Mantıksal Đşlemler

Dizi nin Önemi. Telefon rehberindeki numaralar, haftanın günleri gibi v.b.

Hafta 12 Karakter Tutan Diziler

YZM 2105 Nesneye Yönelik Programlama

Önemli noktalar. Paradigma Nesnelere Giriş Mesajlar / Ara bağlantılar Bilgi Gizleme (Information Hiding ) Sınıflar(Classes) Kalıtım/Inheritance

MAK 1005 Bilgisayar Programlamaya Giriş. Fonksiyonlar. Prof. Dr. Necmettin Kaya

C++ Dersi: Nesne Tabanlı Programlama

İŞLEVLER ve İŞLEÇLER. İstanbul Üniversitesi Elektrik Elektronik Mühendisliği. Kaynak: C ve Sistem Programcıları Derneği Kurs notu

C PROGRAMLAMA DİLİNE GİRİŞ

5.HAFTA. Sınıf ve Nesne Kavramı, Metot Oluşturma, Kurucu Metot, this Deyimi

BLM 111 ALGORİTMA VE PROGRAMLAMA I

B02.6 Karar Verme, Eşitlik ve Bağıntı Operatörleri

BLM 111 ALGORİTMA VE PROGRAMLAMA I

Pointers (İşaretçiler)

BLM 112- Programlama Dilleri II. Hafta 2 C Programlarının Bellek Düzeni ve Rekürsif (Özyinelemeli) Fonksiyonlar

Transkript:

2. BÖLÜM SINIFLAR Siniflar nesne yönelimli programlama tekniginin en önemli yapi taslaridir. Nesne yönelimli programlama teknigine siniflari kullanarak program yazma teknigi diyebiliriz. Bu dersimizde sinif veri yapisina bir giris yapacagiz ve siniflara iliskin temel kavramlari açiklayacagim. Bundan sonraki derslerimde ise agirlikli olarak siniflarin kullanilmasi ve siniflar üzerinde yapilan islemler üstünde duracagim. SINIF NEDIR? Sinif (class) nesne yönelimli programlama tekniginin uygulanmasina olanak saglayan ve C dilinde olmayan yeni bir veri yapisidir. Siniflari C deki yapilara benzetebiliriz. Ancak C deki yapilar yalnizca veri elemani içerirler; oysa C++ da siniflar yapilardan fazla olarak hem veri elemani(member), hem de fonksiyon içermektedir. Yapilari hatirlayalim. Nasil bir yapi türü programci tarafindan tanimlanmis bir tür ise (user defined type) siniflar da programcinin tanimlamis oldugu veri yapilaridir. Programci önce yeni bir türü derleyiciye tanitir, daha sonra bu yeni türden nesne, gösterici, referans tanimlayabilir. Siniflari kullanabilmek için ilk yapmamiz gereken islem bir sinifin bildirimini yapmaktir. Bir sinifin bildirimini yapmakla, bu sinif hakkinda derleyiciye bilgi vermis oluruz ki, derleyici bu sinif türünden bir nesne tanimlanmasi durumunda bellegi ne sekilde organize edecegini bilir. Siniflar yapilara göre temel olarak iki önemli farkliliga sahiptir. i. Siniflarin elemanlari ismine public, protected ya da private denilen 3 ayri bölgede yer alabilir. ii. Siniflar yalnizca veri elemanlari degil fonksiyonlar da içerirler. Iste bugünkü dersimizde bu iki yeni özellik üstünde detayli birbiçimde duracagim. SINIF BILDIRIMI Bir sinifin bildirimi yani derleyiciye tanitilmasi özel bir sentaksa tabidir : Sinif bildiriminin genel biçimi söyledir: class [ sinif_ismi] [] // [protected:] // [] // ; class bir anahtar sözcüktür. Türkçe sinif anlamina gelir. Genel biçimdeki sinif_ ismi, bildirimini yaptigimiz sinifin ismidir (class tag) ve isimlendirme kuralina uygun herhangi bir isim olabilir. Bir sinif private, protected ve public isimli üç bölümden olusur. Bir bölüm, bölüm belirten anahtar sözcük ve iki nokta üstüste (:) ayiraci ile baslatilir; diger bir bölüm belirten anahtar sözcüge kadar sürer. Üç bölümün hepsinin bir sinif bildiriminde bulunmasi zorunlu degildir. Üye fonksiyonlarin yalnizca prototip bildirimleri sinif bildirimi içerisine yazilir; tanimlamalari normal bir fonksiyon gibi disarida yapilir. (Bu durumun bir istisnasini ileride sinif içi inline fonksiyonlar basligiyla inceleyecegiz.) Sinifin içinde prototip bildirimleri yapilan fonksiyonlarla sinifin disinda prototip bildirimleri yapilan (Yani bizim simdiye kadar nortmal olarak tanimladigimiz) fonksiyonlari birbirlerinden ayirabilmek 1

için, sinifin içinde bildirilen fonksiyonlara o sinifin üye fonksiyonu (member function) derken, diger fonksiyonlara global fonksiyonlar (global function) diyecegiz. Yani bizim her zaman tanimlamaya alismis oldugumuz fonksiyonlara artik bundan böyle global fonksiyonlar diyecegiz. Sinifa iliskin veri elemanlari yapilarda oldugu gibi sinif içerisinde tür bilgileri ile bildirilirler. Iste örnek: class Sample int a; void func1(); protected: long b; int func2(int); double c; double func3(); ; Yukaridaki sinif bildiriminde sinifin üç bölümü de kullanilmistir. Sinifin her bölümünde birer veri elemani ve birer fonksiyon bildirilmistir. Sinif bildirimi içerisinde birden fazla bölüm belirten anahtar sözcük kullanilabilir. Örnegin yukaridaki bildirim asagidaki gibi de yapilabilirdi: class Sample protected: long b; int a; double func3(); protected: int func2(int); double c; void func1(); ; Sinif bildirimi bölüm belirten bir anahtar sözcük ile baslatilmamissa, private bölüm üzerinde islem yapildigi anlasilir. Örnek vereyim: class Sample long b; int a; double func3(); protected: int func2(int); double c; void func1(); ; Yukaridaki örnekte Sample isimli sinifin a ve b veri elemanlari private bölümde bildirilmistir. Asagida dersimizin kalan kisminda araç olarak kullanacagimiz bir sinifin bildirimini yapiyorum: class Date 2

int day, mon, year; bool VerifyDate(); void SetDate(int, int, int); void DisplayDate(); ; Yukaridaki sinif bildirimini inceleyelim. Sinifimizin ismi Date. Yukaridaki bildirimle Date isimli yeni bir veri türü yaratmis olduk. C dili ile C++ dilinin bu noktadaki farkini hatirlayalim, C++ dilinde bu veri türünün ismi hem class Date hem de Date. Yani diledigimiz ismi kullanabiliriz. Oysa C dilinde yapilar söz konusu oldugunda bir yapi ismini (structure tag) bir tür ismi olarak kullanmak için bir typedef bildirimi yapmak gerekiyordu. Daha önce söylemis oldugum gibi sinif ismi (class tag) isimlendirme kurallarina uygun olmak sartiyla istenildigi gibi seçilebilir. Ancak C++ dilinde sinif isimlerinin ilk harfinin büyük diger harflerinin küçük seçilmesi konusunda bir egilimin var oldugunu söyleyebiliriz. Date sinifimizin bildirimini incelemeyi sürdürelim. int türden day, mon, year isimli veri elemanlari sinifin private bölümünde bildirilmis. Bir belirteç kullanilmadigi zaman sinifin private bölümünün anlasildigini hatirlayalim. Baska bir deyisle int, mon ve year, Date sinifinin private veri elemanlari. Date isimli sinifimizin bildirimi içinde, VerifyDate, SetDate ve DisplayDate isimli üç fonksiyonun prototip bildiriminin yapildigini görüyoruz. Bu fonksiyonlar Date sinifinin üye fonksiyonlari. VerifyDate isimli fonksiyonun prototip bildirimi sinifin private bölümünde yapilmisken, SetDate ve DisplayDate fonksiyonlarinin prototip bildirimleri Date sinifinin public bölümünde yapilmis. Söyle de söyleyebilirdik: SetDate ve DisplayDate, Date sinifinin public üye fonksiyonlari. VerifyDate Date sinifinin private üye fonksiyonu. Peki sinifin üye fonksiyonlari ile bizim daha önceden bildigimiz fonksiyonlar arasinda bir fark var mi? Siniflarin üye fonksiyonlari ne ise yariyorlar, nasil tanimlaniyorlar? Biraz sabredin, bütün bu konulara detayli bir sekilde deginecegim. Sinifa iliskin fonksiyonlara üye fonksiyonlar (member functions) denir. Her üye fonksiyon sinifin veri elemanlarina dogrudan erisebilir. Sinifin veri elemanlari üye fonksiyonlar tarafindan ortaklasa kullanilan degiskenlerdir. Sinifin üye fonksiyonlari bir konuya iliskin çesitli alt islemleri yaparlar. Bu islemleri yaparken de sinifin veri elemanlarini ortaklasa olarak kullanirlar. Bir isi gerçeklestiren bir dizi fonksiyonun sinif adi altinda ele alinmasi algilamayi ve tasarimi kolaylastirmaktadir. SINIF Üye Fonksiyonlar Veri Elemanlari Yapilarla oldugu gibi siniflarla da çalismak için önce sinif bildirimi yapmak, sonra da bu sinif türünden nesneler tanimlamak gerekir. Sinif bildirimi kaynak kodun neresinde yapilir diye bir soru soruyorsaniz cevap C dilindekinden farkli olmayacak. Bir sinifin bildirimi kaynak kodun herhangi bir yerinde olabilir. Ancak bildirimin faaliyet alaninin dosya faaliyet alani ya da blok faaliyet alanina sahip olmasi farkli anlamlar içerecek degil mi? Date sinifimizin bildirimini bütün bloklarin disinda yani global düzeyde yaparsak, dosya faaliyet alanina sahip bu bildirim sonucunda, Date sinifini tüm fonksiyonlar bilecek. Yani sinif bildiriminden sonra dosyanin sonuna kadar her noktada Date sinifi türündne bir nesne tanimlayabilecegiz. Oysa bildirim yerel düzeyde, yani bir blok içinde yapilmis olsaydi, yalnizca bildirimin yapildigi blok içinde bu veri türü bilinecek, o blogun disinda söz konusu Date sinifimiz bilinmeyecekti. 3

Yapilar gibi siniflar da ideal olarak baslik dosyalari içinde bildirilirler. Ancak C++ dilinde baslik dosyalarinin kullanilmasi konusunu kursumuzun ileriki saatlerine birakiyoruz. Sinif bildirimi, bir sinifin veri elemanlarinin ve üye fonksiyonlarinin derleyiciye tanitilmasi islemidir. Sinif bildirimi ile derleyici bellekte herhangi bir yer ayirmaz; yalnizca sinif hakkinda bilgi edinir. Siniflar üzerinde islemlerin yapilabilmesi için sinif nesnelerinin tanimlanmasi gerekir. O zaman artik sinif nesnelerinin tanimlanmasina bir göz atalim: SINIF NESNELERININ TANIMLANMASI Madem sinif yeni bir tür, bu türden bir nesne tanimlanabilir. Peki nasil? Diger türlerden nesne tanimlamalarimiz ne sekilde yapiliyorsa ayni sentaksi bir sinif türünden nesne tanimlarken de kullanacagiz. Yani önce tür bildiren sözcük ya da sözcükler daha sonra tanimlanan nesnenin ismi gelecek. Bildirimini yapmis oldugumuz Sample ve Date isimli siniflarimizi düsünelim. Sample sam; sam degiskeni Sample türünden bir sinif nesnesidir. Date date1, date2; date1 ve date2 degiskenleri Date sinifi türünden nesnelerdir. C++ da yapi ve sinif türünden nesneler tanimlarken struct ve class anahtar sözcüklerinin yazilmasina gerek olmadigini söylemistim. Tabii bu anahtar sözcüklerin kullanilmasi herhangi bir probleme yol açmaz. Yani yukaridaki bildirim, class Date x, y; biçiminde de yapilabilirdi. Ancak madem class anahtar sözcügünü kullanmasak da oluyor o zaman biz de kullanmayalim. Ben böyle düsünüyorum. Gelin biraz daha ilerlemeden önce ögrendigimiz noktalari anahtar cümlelerle özetleyelim: Anahtar Notlar 1. Bir sinif, veri elemanlari ve üye fonksiyonlardan olusur. 2. Sinifin private, protected ve public olarak isimlendirilen üç bölümü vardir. 3. Bölüm belirten anahtar sözcük kullanilmamissa private bölüm anlasilir. 4. C++ da yapi ve sinif nesneleri tanimlanirken struct ve class anahtar sözcükleri kullanilmak zorunda degildir. Simdi geldik üye fonksiyonlarimiza. Bu fonksiyonlar hakkinda akliniza gelen sorularin büyük bir bölümüne birazdan cevap alacaksiniz. Bir sinifa iliskin üye fonksiyonlar, sinifin disinda ve belirlenmis bir sentaks biçimi ile tanimlanirlar. [geri dönüs degerinin türü] <sinif_ismi> :: <fonksiyon_ismi> ([parametre degiskenleri]) // Genel biçim kafanizi karistirmissa (benim hep karistirir) ben size isin özetini söyleyeyim: Önce fonksiyonun geri dönüs degerinin türü, sonra sinifin ismini yaziyoruz. sonra :: atomu ve sonra da fonksiyon ismi yaziliyor. Iki tane iki nokta üst üstenin yan yana getirilmesiyle olusturulan :: atoma bundan sonra çözünürlük operatörü (abk) diyecegiz. Bu operatörün ingilizce isminin scope resolution operator oldugunu da hatirlatatayim bu arada. 4

Date sinifinin parametre degiskeni olmayan ve geri dönüs degeri void türden olan (yani geri dönüs degeri üretmeyen) Display üye fonksiyonu da söyle tanimlanabilir: void Date::Display() // SINIF NESNELERI YOLUYLA SINIFIN VERI ELEMANLARINA VE ÜYE FONKSIYONLARINA ERISIM Yapilarda oldugu gibi, bir sinifa iliskin nesne yoluyla sinifin veri elemanlarina ve üye fonksiyonlarina nokta operatörü ile erisilebilir. Örnegin o bir sinif türünden nesne, m ise bu sinifa iliskin bir veri elemani ve func da bu sinifa iliskin bir üye fonksiyon olsun. Erisim asagidaki gibi gerçeklestirilir. veri elemani o. m sinif nesnesi Bir üye fonksiyon ancak sinif nesnesi ile çagrilabilir. Global fonksiyonlarda oldugu gibi disaridan dogrudan olarak çagrilamaz. Örnegin func isimli bir üye fonksiyonu varsa func(); biçiminde çagrilamaz. Eger böyle çagrilirsa global func fonksiyonunun çagrildigi anlasilir. Çagrilma islemi asagidaki gibi yapilabilir: üye fonksiyon o. func() sinif nesnesi Sinif türünden göstericiler yoluyla üye fonksiyonlar ok operatörü (->) ile çagrilabilirler. Bu durumu biraz daha ileride ele alacagim. ÜYE FONKSIYONLARIN AMACI KOD IÇERISINE YAZILMALARI C++ da global bir fonksiyonun parametre türleriyle kombine edilerek amaç kod içerisine yazildigini anlatmistim. Bir sinifin üye fonksiyonu da parametre türlerinin yani sira sinif ismiyle de kombine edilerek amaç kod içerisine yazilir. Bu durumda bir program içerisinde, ayni isimli ve ayni parametrik yapiya sahip global bir fonksiyon ile bir sinifa iliskin üye fonksiyon birlikte bulunabilir ve bu iki fonksiyonun amaç koda yazilmalarinda bir problem ortaya çikmaz. Örnegin Borland C++ 3.1 derleyicisinde void func(void) global fonksiyonu @func$qv biçiminde, X sinifina 5

iliskin void func(void) fonksiyonu ise @X@func$qv biçiminde amaç kod içerisine yazilmaktadir. Derleyiciler ayni isimli global fonksiyonlarla üye fonksiyonlari çagrilma biçimlerine bakarak ayirt edebilirler. Örnegin: a.func(); gibi bir çagirma, sinif nesnesi ile yapildigina göre, derleyici a hangi sinifa iliskinse func isimli fonksiyonun da o sinifin bir üye fonksiyonu oldugunu düsünecektir. Oysa çagrilma islemi, func(); biçiminde yapilsaydi, derleyici global olan func fonksiyonunun çagrildigini düsünecekti. C++ da fonksiyonlarin amaç kod içerisine yazilmalarinda herhangi bir standart söz konusu degildir. Örnegin her derleyici üye fonksiyonlarin isimlerini sinif ismiyle ve parametre türleriyle kombine ederek amaç kod içerisine yazar; ancak bu yazma isleminin kesin bir biçimi standart olarak belirlenmemistir. Bu nedenle farkli C++ derleyicileriyle derlenmis modüllerin birlestirilmesinde problemler çikabilir. Anahtar Notlar 1. Bir sinif nesnesi ile sinifin veri elemanlarina erisilmesi ve sinifin üye fonksiyonlarin çagrilmasi nokta operatörü ile yapilir. (Gösterici ile erisim -> operatörü ile yapilmaktadir.) 2. Bir program içerisinde ayni isimli ve ayni parametre yapisina sahip farkli siniflarin üye fonksiyonlari birarada bulunabilir. Üye fonksiyonlarla global fonksiyonlar ayni parametre yapisina sahip olsalar bile karismazlar. Peki bir sinif nesnesine iliskin veri elemanlari bellege ne sekilde yerlestiriliyor? Bir sinif nesnesi tanimlandiginda derleyici yalnizca sinifin veri elemanlari için bellekte yer ayirir. Sinifin üye fonksiyonlari sinif nesnesi içerisinde herhangi bir yer kaplamaz. Üye fonksiyonlar yalnizca mantiksal bakimdan sinif ile iliskilendirilmistir. Sinifin iki bölüm belirten anahtar sözcügü arasina yazilan veri elemanlari, ilk yazilan düsük adreste olacak biçimde ardisil(contigous) yerlestirilir. Bölümlerin birbirlerine göre yerlesim biçimleri standart olarak belirlenmemistir; derleyiciden derleyiciye degisebilir. Asagidaki örnegi inceleyiniz. class Sample int a; int b; int c; int d; protected: int e; int f; int g; int h; ; Burada Sample türünden bir sinif nesnesinin bellekte kapladigi alan 8 * sizeof(int) kadar olacaktir. Nesne içerisinde, a ile b, c ile d, e ile f ve g ile h veri elemanlari ardisil olarak 6

yerlestirilecektir. Ancak bu gruplarin kendi aralarindaki yerlesimleri derleyiciden derleyiciye degisebililir. Teknik Açiklama 1. Bölümler arasindaki yerlesim standart bir biçimde belirlenmemis olsa da derleyicilerin hemen hepsi daha yukari yazilan bölümleri düsük adrese yerlestirmektedir. Yani yukaridaki örnekte derleyicilerin büyük bölümü veri elemanlarini sirasiyla düsük adresten baslayarak yukaridan asagiya dogru yerlestirecektir. Bölümler arasi yerlesim standart olmadigina göre, nesnenin veri elemanlarinin yerlesimine ilskin yazilan kodlarda tasinabilirlik problemleri ortaya çikabilir. 2. Nesnenin bellekte kapladigi alan derleyicinin hizalama (alignment) islemleri etkin hale getirilmisse veri elemanlarinin toplam uzunlugundan fazla olabilir. SINIFLARDA TEMEL ERISIM KURALI Bir sinif nesnesi tanimlandiginda bu sinif nesnesi ile sinifin her bölümüne erisemeyiz. Hangi veri elemanina ya da hangi üye fonksiyona erisebilecegimiz veri elemanlarinin ya da üye fonksiyonlarin yerlestirilmis olduklari yer ile ilgilidir. Ancak sinif nesnelerinin ve üye fonksiyonlarin temel erisim kurallarini iki bölüme ayirarak inceleyecegiz. 1. Sinif Nesneleri Ile Erisim Kurali Bir sinif nesnesi ile disaridaki bir fonksiyondan (sinifin üye fonksiyonu olmayan bir fonksiyondan) ancak sinifin public bölümündeki veri elemanlarina ve üye fonksiyonlarina erisilebilir. Sinifin private ya da proteceted bölümlerindeki veri elemanlarina ve üye fonksiyonlarina global bir fonskiyon içerisinde sinif nesnesi yoluyla erisilemez. Örneklerde yine Date sinifimizi kullanalim: #include <stdio.h> #include <stdlib.h> class Date int day, month, year; int VerifyDate(void); void SetDate(int d, int m, int y); void DispDate(void); ; // Üye fonksiyon tanimlamalari... int main(void) Date date; date.day = 10; if (!date.verifydate()) printf( Geçersiz tarih \n ); exit(exit_failure); date.setdate(10, 10, 1997); date.displaydate(); // Geçersiz erisim! day private bölümde // Geçersiz erisim! VerifyDate private bölümde // Geçerli erisim, Setdate public bölümde // Geçerli erisim, DispDate public bölümde 7

Sinif nesnesi yoluyla sinifin yalnizca public bölümündeki veri elemanlarina ve fonksiyonlarina dogrudan erisebiliriz. Yukaridaki örnekte date.day veri erisimi ile date.verifydate() üye fonksiyonunun çagrilmasi bu nedenle geçerli degildir ve derleme asamasinda hata olusmasina yol açar. Fakat public bölümde bulunan SetDate ve DisplayDate üye fonksiyonlarinin çagrilmasi geçerlidir; herhangi bir probleme yol açmaz. Siniflardaki erisim kuralina uyulmamasindan dolayi olusan hatalara iliskin mesajlar Microsoft derleyicilerinde, xxx cannot access private member declared in Class YYY Borland derleyicilerinde ise, YYY ::xxx not accessible biçimindedir. 2. Üye Fonksiyonlarin Erisim Kurali Bir sinifin üye fonksiyonu hangi bölümde bildirilmis olursa olsun sinifin her bölümündeki veri elemanlarina erisebilir ve üye fonksiyonlari dogrudan çagirabilir. Örnegin, bildirimini yukarida verdigimiz Date sinifinin SetDate isimli üye fonksiyonu söyle tanimlanmis olsun. void Date::SetDate(int d, int m, int y) date = d; // Geçerli erisim month = m; // Geçerli erisim year = y; // Geçerli erisim if (!VerifyDate(d, m, y)) printf( Geçersiz tarih \n ); exit(exit_failure); // Geçerli erisim Gördügünüz gibi SetDate isimli üye fonksiyon içerisinde VerifyDate isimli üye fonksiyon dogrudan çagrilmistir. VerifyDate üye fonksiyonu, söz konusu tarihin geçerli bir tarih olup olmadigini test etmektedir. Eger tarih geçersizse program sonlandirilmistir. VerifyDate üye fonksiyonu ile day, month, year veri elemanlari sinifin private bölümünde olmasina karsin erisim geçerlidir. Çünkü bir üye fonksiyon kendi sinifinin her bölümüne erisebilir. Sinifin üye fonksiyonlari içerisinde tanimlanan ayni sinifa iliskin nesneler ile sinifin her bölümüne erisebilir. Örnegin Date sinifinin ProcessDate isimli bir üye fonksiyonu daha olsaydi, void Date::ProcessDate(void) Date x; x.day = day; // Geçerli erisim x.month = month; // Geçerli Erisim x.year = year; // GeçerlI Erisim if (!x.verifydate()) // Geçerli erisim printf( Geçersiz tarih \n ); exit(exit_failure); 8

x sinif nesnesi ile sinifin her bölümüne erisebilirdik. Çünkü x nesnesi sinifin üye fonksiyonu içerisinde tanimlanmistir. Bir fonksiyonun parametre parantezinin içi de fonksiyonun içi kabul edilir. Bu durumda bir üye fonksiyonun parametre degiskeni ayni sinif türünden bir nesne ise, dogal olarak parametre degiskeni ve nokta operatörüyle sinifin veri elemanlarina ya da üye fonksiyonlarina erisebiliriz. Anahtar Notlar 1. Bir sinif nesnesi kullanarak sinifin üye fonksiyonu olmayan bir fonksiyon içerisinden ancak sinifin public bölümündeki veri elemanlarina ve üye fonksiyonlarina erisebiliriz. 2. Bir sinifin üye fonksiyonu içerisinde sinifin her bölümündeki veri elemanlarina ve üye fonksiyonlarina erisilebilir. 3. Bir sinifin üye fonksiyonu içerisinde ayni sinif türünden bir nesne tanimlanmissa, o nesne ile sinifin her bölümüne erisilebilir. ÜYE FONKSIYONLARIN SINIFIN VERI ELEMANLARINA ERISMESI VE DIGER ÜYE FONKSIYONLARI ÇAGIRMASI Bir üye fonksiyonun kendi sinifinin veri elemanlarina ve üye fonksiyonlarina dogrudan erisebildigini gördük. Peki bu nasil oluyor? Bir fonksiyon kendi içinde tanimlanmayan bir yerel nesneye nasil ulasabilir? Asagidaki gibi bir Sample sinifi bildirilmis olsun: #include <stdio.h> class Sample int Set(int x, int y); void Disp(); int a, b; ; void Sample::Set(int x, int y) a = x; b = y; void Sample::Disp() printf( %d %d\n, a, b); Bir üye fonksiyon hangi sinif nesnesi ile çagrilmissa, üye fonksiyonun içerisinde kullanilan veri elemanlari da o nesneye ilskindir. Asagidaki örnege bakalim: int main(void) Sample m; m.set(10, 20); m.disp(); Sample n; n.set(30, 40); n.disp(); // Set üye fonksiyonundaki a ve b, m nesnesine iliskin // Disp fonksiyonundaki a ve b, m nesnesine iliskin // Set fonksiyonundaki a ve b, n nesnesine iliskin // Disp fonksiyonundaki a ve b, n nesnesine iliskin 9

Burada Set ve Disp üye fonksiyonlari içerisinde dogrudan kullandigimiz a ve b veri elemanlari bu fonksiyonlar hangi nesne ile çagrilmissa o nesnenin elemanlaridir. Disp üye fonksiyonu m.disp() çagirmasinda m nesnesinin veri elemanlarini, n.disp() çagirmasinda ise n nesnesinin veri elemanlarini kullanir. m.disp() m 10 a void Sample::Disp(void) printf( %d %d\n, a, b); n.disp() n 20 30 b a 40 b Bir üye fonksiyonun baska bir üye fonksiyonu çagirmasi durumunda, çagrilan üye fonksiyon çagiran üye fonksiyon ile ayni veri elemanlarini kullanir. Örnegin Sample sinifinin Set üye fonksiyonu Disp üye fonksiyonunu dogrudan çagrimis olsun: void Sample::Set(int x, int y) a = x; b = y; Disp(); Bu durumda Set üye fonksiyonu hangi sinif nesnesi ile çagrilmissa Disp üye fonksiyonu da onun veri elemanlarini kullanacaktir. Sample n; n.set(10, 20); isleminde Set ve Disp n sinif nesnesinin veri elemanlarini kullanacaktir. Yani Set ve Disp fonksiyonlarinin içerisindeki a ve b veri elemanlari n nesnesine iliskindir. Anahtar Notlar 1. Bir üye fonksiyon sinifin baska bir üye fonksiyonunu dogrudan çagirabilir. Bu durumda çagrilan üye fonksiyon çagiran üye fonksiyon ile ayni nesnenin veri elemanlarini kullanirlar. SINIF FAALIYET ALANI C dilinde faaliyet alanlarinin üç bölüme ayrildigini animsayiniz. Bunlar dardan genise dogru blok faaliyet alani (blok scope), fonksiyon faaliyet alani (function scope) ve dosya faaliyet alanlaridir. Blok faaliyet alani yalnizca bir blok içerisinde, fonksiyon faaliyet alani bir fonksiyonun her yerinde, dosya faaliyet alani ise tüm fonksiyonlar içerisinde taninma araligidir. C++ da bu faaliyet alanlarina ek olarak bir de sinif faaliyet alani (class scope) tanimlanmistir. Sinif faaliyet alani bir sinifin tüm üye fonksiyonlari içerisinde taninma araligidir. Sinifin veri elemanlari ve üye fonksiyonlari sinif faaliyet alani kuralina uyarlar. Sinif faaliyet alaninin darlik genislik 10

bakimindan fonksiyon faaliyet alani ile dosya faaliyet alani arasinda bulunduguna dikkat ediniz. Bu durumda C++ daki faaliyet alanlari dardan genise dogru, 1. Blok faaliyet alani (block scope) 2. Fonksiyon faaliyet alani (function scope) 3. Sinif faaliyet alani (class scope) 4. Dosya faaliyet alani (file scope) biçimindedir. Ayni isimli degiskenler konusunda su kurali animsatalim: C dilinde ve C++ da ayni faaliyet alanina iliskin ayni isimli birden fazla degisken tanimlanamaz fakat farkli faaliyet alanina iliskin ayni isimli birden fazla degisken tanimlanabilir. Bir blok içerisinde ayni isimli birden fazla degisken faaliyet gösteriyorsa o blok içerisinde dar faaliyet alanina sahip olana erisilebilir. Asagidaki örnegi inceleyin: #include <stdio.h> void Func(); // Prototip bildirimi int a = 50, b = 100; // Global degiskenler class X int Sample(int x, int y); void Func(); int a, b; ; void X::Func() printf( X sinifinin Func isimli üye fonksiyonu \n ); void Func() printf( Global Func isimli fonksiyon \n ), Bu örnekte hem global olarak hem de X sinifinin veri elemani olarak a ve b isimli degiskenler tanimlanmistir. Sinifin Sample üye fonksiyonu da söyle tanimlanmis olsun: void X::Sample(int a, int b) printf( %d\n, a); printf( %d\n, b); int a = 30; int b = 40; // Paramtere degiskeni olan a // Parametre degiskeni olan b printf( %d\n, a); printf( %d\n, b); Func(); // Blok içerisindeki a // Blok içerisindeki b // Üye fonksiyon olan Func 11

Simdi dört tane a ve b söz konusudur. Global olanlar, sinifin veri elemani olanlar, parametre degiskeni olanlar ve iç blokta tanimlanmis olanlar. Dar faaliyet alanina sahip olana erisme kuralina göre, iç blokta kullanilan a ve b o blokta tanimlanan a ve b degiskenleridir. Dis bloktakiler ise fonksiyonun parametre degiskenleridir. Çagrilan fonksiyon üye fonksiyon olan Func isimli fonksiyondur. Çünkü fonksiyon isimleri de degisken gibi ele alinir ve ayni faaliyet alani kuralinda uyar. Global fonksiyonlar dosya faaliyet alanina, üye fonksiyonlar ise sinif faaliyet alanina sahiptir. Simdi Sample fonksiyonunun parametre degiskenlerinin isimlerini degistirelim. void X::Sample(int x, int y) a = x; b = y; int a = 30; int b = 40; // Sinifin veri elemani olan a // Sinifin veri elemani olan b printf( %d\n, a); printf( %d\n, b); Func(); // Blok içerisindeki a // Blok içerisindeki b // Üye fonksiyon olan Func Simdi dis bloktaki a ve b sinifin veri elemanlari olan a ve b olarak ele alinacaktir. Peki bir üye fonksiyon içerisinde sinifin veri elemanlari ya da üye fonksiyonlariyla ayni isimli global degiskenlere ya da fonksiyonlara erismek mümkün olabilir mi? Iste çözünürlük operatörü ile bu durum mümkün hale getirilmistir. ÇÖZÜNÜRLÜK OPERATÖRÜ Çözünürlük operatörü iki : karakterinin :: biçiminde yan yana getirilmesiyle elde edilir. Çözünürlük operatörünün tek operandli önek (unary prefix) ve iki operandli araek biçiminde (binary infix) iki kulanimi vardir. Önce tek operandli önek biçim üzerinde duracagim: Çözünürlük Operatörünün Tek Operandli Önek Kullanimi Bu kullanim biçiminde :: operatörünün tek operandi vardir ve her zaman global degiskene erisimi gerçeklestirir. Asagidaki örnegi inceleyiniz. #include <stdio.h> int a = 10; int main(void) int a = 20; ::a = 50; printf( %d\n, a); int a = 30; ::a = 100; printf( %d\n, a); printf( %d\n, ::a); // Global a // Yerel a // Global a // Yerel a // Global a 12

:: operatörü ayni isimli hem yerel hem de global bir degiskenin tanimli oldugu durumda global olana erismek amaciyla kullanilir. Yukaridaki örnekte blok içlerinde a degiskeninin :: operatörü ile kullanilmasiyla global olan a degiskenine erisilmektedir. int a = 10; void main(void) int a = 20; ::a = 50; printf( %d\n, a); int a = 30; ::a = 100; printf( %d\n, a); printf( %d\n, ::a); Burada bir uyari yapmak istiyorum. :: operatörü bir üst blokta tanimli olana erisimi degil her zaman global olana erisimi saglamaktadir. Bir yukaridaki bloga erismenin ciddi bir faydasi yoktur. Oysa global degiskene erisim pek çok durumda gerekebilir. Tek operandli önek kullanim siklikla bir sinifin üye fonksiyonlari içerisinde sinifin veri elemanlari ile ayni isimli global degiskenlerin bulunmasi durumunda, global olana erisimi saglamak için kullanilir. Asagidaki örnegi inceleyiniz. #include <stdio.h> void Func(void); // Global fonksiyon class X void Func(void); void Sample(void); ; void Func(void) printf( Global Func isimli fonksiyon \n ); void X::Func(void) printf( X sinifinin Func isimli fonksiyonu \n ); void X::Sample(void) printf( X sinifinin Sample isimli fonksiyonu \n ); Func(); // X sinifinin üye fonksiyonu olan çagriliyor ::Func(); // Global olan çagriliyor 13

Sample üye fonksiyonunda, Func() Biçiminde normal olarak çagrilan dar faaliyet alani kuralina göre X sinifina iliskin olan Func fonksiyonudur. Halbuki ::Func(); biçiminde çagrilmis olan global Func fonksiyonudur. Bazi programcilar global olanla ayni isimli bir üye fonksiyon olmasa bile, üye fonksiyonlar içerisinde global fonksiyonlari okunabilirligi artirmak için :: operatörüyle çagrirlar. Örnegin, CMyDialog:: CMyDialog(void) hprocess = ::GetProcessHeap(); GetProcessHeap fonksiyonun CMyDialog sinifinin üye fonksiyonu olmadigini düsünelim. Bu durumda fonksiyonun :: operatörü ile çagrilmasina gerek yoktur degil mi? Çünkü faaliyet alanlarinin çakismasi söz konusu olmadigi için nasil olsa GetProcessHeap deyince global olan anlasilacaktir. Ancak programci kodu inceleyen kisiye yardimci olmak için durumu vurgulamak istemis olabilir. Siniflarin yogun olarak kullanildigi kütüphanelerde bu tür vurgulamalarla oldukça sik karsilasabilirsiniz. Çözünürlük Operatörünün Iki Operandli Araek Kullanimi Çözünürlük operatörü iki operandli araek (binary infix) olarak da kullanilmaktadir. Böyle bir kullanimda sol taraftaki operand bir sinif ismi, sag taraftaki operand ise sinifa iliskin bir veri elemani ya da üye fonksiyon ismi olmak zorundadir. Bu kullanimla her zaman sinifa iliskin olan veri elemanina ya da üye fonksiyona erisilir. Örnegin Date isimli bir sinifin int türden day, month, year isimli veri elemanlari olsun. SetDate bu sinifin bir üye fonksiyonu olmak üzere class Date void SetDate(int day, int month, int year); // int day, month, year; ; void Date::SetDate(int day, int month, int year) Date::day = day; Date::month = month; Date::year = year; Parametre degiskenlerinin isimleriyle sinifin veri elemanlarinin isimlerinin ayni olduguna dikkat edin. Bu durumda üye fonksiyon içerisinde sinifin veri elemanlarina erismek için iki operandli çözünürlük operatörü kullanilmistir. Kullanim biçimini inceleyiniz: 14

parametre degiskeni Date :: day = day; Sinif ismi Veri elemani Görüldügü gibi çözünürlük operatörünün iki operandli ara ek biçimi, sinifin veri elemanlari ile ayni isimli yerel degiskenler ya da parametre degiskenlerinin olmasi durumunda sinifin veri elemanlarina erisilmesi amaciyla kullanilmaktadir. Bunun disinda çözünürlük operatörünün siniflarin türetilmesi islemlerinde de benzer amaçlarla kullanildigini göreceksiniz. Anahtar Notlar 1. Çözünürlük operatörünün tek operandli önek biçimi global degiskenlere ve fonksiyonlara erismek için kullanilir. 2. Bir üye fonksiyon içerisinde sinifin veri elemanlari ile ayni isimli yerel degiskenler ya da parametre degiskenleri varsa çözünürlük operatörünün iki operandli araek biçimi ile sinifa iliskin olan degiskenlere erisim saglanir. SINIFIN BASLANGIÇ FONKSIYONLARI Bir sinif nesnesi yaratildiginda ismine baslangiç fonksiyonu (constructor) denilen bir üye fonksiyon derleyici tarafindan otomatik olarak çagrilir. Derleyici baslangiç fonksiyonlarini isimlerine bakarak tespit eder. Baslangiç fonksiyonlari sinif ismiyle ayni isimli üye fonksiyonlardir. Baslangiç fonksiyonlarinin parametrik yapisi herhangi bir biçimde olabilir. Ancak geri dönüs degerleri yoktur. Geri dönüs degerleri yoktur demekle geri dönüs degerlerinin void oldugunu anlatmak istemiyorum. Bu fonksiyonlarin geri dönüs degerleri diye bir kavramlari yoktur. Yazarken geri dönüs degeri yerine hiçbirsey yazilmaz. Asagidaki sinif bildirimini inceleyin: class Date Date(); void Disp(); int day, month, year; ; Date isimli sinifin baslangiç fonksiyonu hangisidir? Date(); prototipiyle bildirilmis olan degil mi? Bu fonksiyonun isminin sinif ismiyle ayni olduguna dikkat edin. Prototip bildiriminde geri dönüs degeri yerine hiçbir sey yazilmamistir. Bu durum yukarida da belirttigim gibi geri dönüs degerinin int ya da void oldugu anlamina gelmiyor. Pekiyi, Date sinifinin baslangiç fonksiyonu disarida nasil tanimlanacak? Digerlerinde oldugu gibi! Yani önce sinif ismi daha sonra :: operatörü ve daha sonra da fonksiyon ismi. 15

Sinif ismi Fonksiyon ismi Date::Date(void) //... Asagidaki örnekte Person isimli sinifin baslangiç fonksiyonu hangisidir dersiniz? class Person Person(const char *nm, int n); void Disp(void); void SetName(const char *nm); void SetNo(int n); // char *name; int no; ; // Baslangiç fonksiyonu Person sinifinin baslangiç fonksiyonu, Person(const char *nm, int n); prototipiyle bildirilmis olandir. Bu fonksiyon disarida söyle tanimlanabilir: Person::Person(const char *nm, int n) // Baslangiç fonksiyonlarinin parametrik yapisi herhangi bir biçimde olabilir. C++ da farkli parametre yapisina iliskin ayni isimli fonksiyonlar olabildigine göre, bir sinif da farkli parametre yapisina iliskin birden fazla baslangiç fonksiyonuna sahip olabilir. Örnegin Date isimli sinifin asagidaki gibi birden fazla baslangiç fonksiyonu olabilir. class Date Date(); Date(int d, int m, int y); // int day, int month, year; ; Yukaridaki Date sinifinda iki baslangiç fonksiyonu vardir: Parametre degiskeni olmayan ve üç parametre degiskenine sahip olan. Bir sinifin parametre degiskeni olmayan baslangiç fonksiyonuna varsayilan baslangiç fonksiyonu (default constructor) denir. Baslangiç fonskiyonlari sinif nesneleri yaratildiginda derleyici tarafindan otomatik olarak çagrilir. Yani derleyici önce sinif nesnesi için bellekte yer ayirir; daha sonra uygun olan baslangiç fonksiyonunu çagirir. Yerel degiskenlerin programin akisinin tanimlama noktasina geldiginde, global degiskenlerin ise programin bellege yüklenmesiyle yaratildigini animsayin. 16

Buna göre yerel bir sinif nesnesine iliskin baslangiç fonksiyonu nesnenin tanimlandigi yerde, global bir sinif nesnesine iliskin baslangiç fonksiyonu ise programin bellege yüklenmesiyle (yani main fonksiyonundan önce) çagrilacaktir. (main fonksiyonundan önce çalisan bir kod olabilir mi? Evet C++ da olabilir!) Bir sinifin birden fazla baslangiç fonksiyonu olabildigine göre, derleyici bunlardan hangisi çagiracagini nasil tespit edecek? Hangi baslangiç fonksiyonunun çagrilacagi nesnenin tanimlanma ifadesiyle belirlenmektedir. Eger nesne isminden sonra parantez açilmis ve bir arguman listesi yazilmissa, yazilan parametre listesine uygun parametre yapisina sahip baslangiç fonksiyonu çagrilir. Örnegin: Date x(6, 1, 1966); gibi bir tanimlamayla sinifin Date(int, int, int); parametre yapisina sahip olan baslangiç fonksiyonu çagrilacaktir. Benzer biçimde: Complex z(10.2, 5.3); gibi bir nesne tanimlamasinda da parametre yapisi Complex(double, double); biçiminde olan baslangiç fonksiyonu çagrilir. (Nokta içeren ve sonuna ek almamis olan sabitlerin double sabit olarak ele alindigini animsayiniz.) Peki, asagidaki örnekte Person isimli sinifin hangi baslangiç fonksiyonu çagrilir? Person y( Necati Ergin ); Stringlerin char türden bir adres belirttigini animsayiniz. Bu durumda y isimli sinif nesnesinin tanimlanmasiyla Person isimli sinifin char türünden gösterici parametresine sahip olan baslangiç fonksiyonu çagrilacaktir. Eger tanimlama islemi diger türden nesnelerde oldugu gibi parantez açilmadan yapilmissa, nesne yaratilirken varsayilan baslangiç fonksiyonu (yani parametresi olmayan baslangiç fonksiyonu) çagrilir. Örnegin: Date x; Person y; Complex z; Yukaridaki x, y ve z nesneleri için varsayilan baslangiç fonksiyonlari çagrilacaktir. Eger bir sinif nesnesi ilkdeger verilerek tanimlaniyorsa, ilkdeger olarak verilen türe uygun baslangiç fonksiyonu çagrilir. X bir sinif olmak üzere: X a = b tanimlamasiyla X a(b) tanimlamasi tamamen esdegerdir. Bu durumda örnegin: Person y = Kaan Aslan ; gibi bir tanimlamayla, Person y( Kaan Aslan ); tanimlamasi ayni anlamdadir. Bu biçimde yalnizca tek parametreli baslangiç fonksiyonlari çagrilabilir. Zaten sinif nesnelerine birden fazla degerle ilkdeger verilemez. Sinif nesnelerine 17

yapilarda oldugu gibi küme parantezleri arasinda ilkdeger verme de söz konusu degildir. Örnegin, X a = 10, 20, 30; // Error! a, X türünden bir sinif nesnesi ise bu biçimde ilkdeger verilemez. (Aslinda uyumlu siniflar biçiminde isimlendirilen özel bazi siniflara küme parantezleri ile ilkdeger verilebilmektedir. Ancak bu konuyu ileride ele alacagim) Asagidaki sinif bildirimini inceleyiniz ve izleyen örnegi yazarak çalistirin: #include <stdio.h> #include <time.h> class Date Date(); Date(int d, int m, int y); void Display(); int day, month, year; ; Date::Date() time_t timer; struct tm *p; timer = time(null); p = localtime(&t); day = p -> tm_mday; month = p -> tm_mon + 1; year = p -> tm_year + 1900; Date::Date(int d, int m, int y) day = d; month = m; year = y; void Date::Display() printf("%02d/%02d/%04d\n", day, month, year); int main() Date x; x.display(); Date y(28, 10, 1998); y.display(); 18

return 0; Örnegimizde, Date x; tanimlamasiyla sinifa iliskin varsayilan baslangiç fonksiyonu çagrilacaktir. Varsayilan baslangiç fonksiyonu sistem tarihini alarak sinifin veri elemanlarina yerlestirir. Örnegin, baslangiç fonksiyonu çalistirildiginda x nesnesinin veri elemanlari söyle doldurulmus olsun. (Benim uygulamayi son denedigim tarih 02/01/2002 di!) x 2 1 2002 day month year Örnegimizde daha sonra, x.disp(); çagirmasinin yapildigini görüyorsunuz. Disp fonksiyonu ile yazdirilan day, month ve year, x nesnesinin veri elemanlaridir degil mi? Yerel sinif nesnelerine iliskin baslangiç fonksiyonlari, programin akisi nesnenin tanimlama noktasina geldiginde çagrilacagina göre, Date y(28, 10, 1998); gibi bir tanimlamayla Date(int, int, int); prototipine uygun olan baslangiç fonksiyonu çagrilacaktir. Bu fonksiyon parametre olarak girilen degerleri sinifin veri elemanlarina yerlestirmektedir. Bu durumda y nesnesinin veri elemanlari baslangiç fonksiyonu çagrildiktan sonra söyle olacaktir: y 28 10 1998 day month year Örnegimizde daha sonra, y.disp() ile y nesnesinin veri elemanlarinin yazdirildigini görüyorsunuz. Global degiskenler programin bellege yüklenmesiyle yaratildiklarina göre global sinif nesnelerine iliskin baslangiç fonksiyonlari da main fonksiyonundan önce çalistirilmaktadir. Asagidaki örnegi inceleyin: #include <stdio.h> class X 19

int a; X(int n); void Disp(); ; X::X(int n) a = n; printf( X sinifinin baslangiç fonksiyonu \n ); void X::Disp() printf( %d\n, a); X z = 20; int main() printf( main fonksiyonu basladi \n ); x.disp(); Örnegimizde z sinif nesnesine iliskin baslangiç fonksiyonu main fonksiyonundan önce çagrilacaktir. Ekranda sunlari görmeniz gerekir: X sinifinin baslangiç fonksiyonu main fonksiyonu basladi 20 Eger birden fazla global sinif nesnesi tanimlanmissa baslangiç fonksiyonlarinin çagrilma sirasi yukaridan asagiya dogrudur. Yani daha yukarida tanimlanmis sinif nesnesinin baslangiç fonksiyonu daha önce çagrilir. Baslangiç fonksiyonlari da default argümana sahip olabilir. Asagida örnekte verilen Complex sinifini inceleyin: class Complex double real, imag; Complex(double r, double i = 0); void Disp(); ; Complex::Complex(double r, double i) real = r; imag = i; void Complex::Disp() printf("%.2lf", real); if (imag!= 0) printf("+%.2lfi", imag); putchar('\n'); 20

int main() Complex x(10); // Complex x(10, 0) x.disp(); Complex y(10, 20); y.disp(); return 0; Bir baslangiç fonksiyonunun tüm parametre degiskenleri default deger aliyorsa o baslangiç fonksiyonu default baslangiç fonksiyonu olarak da kullanilabilir. Örnegin, Complex sinifinin baslangiç fonksiyonu asagidaki gibi bildirilmis olsun: class Complex Complex(double r = 0, double i = 0); // ; Asagidaki tanimlamalarin hepsi geçerlidir: Complex x; // Complex(0, 0) ile ayni anlamda Complex y(10); // Complex(10, 0) ile ayni anlamda Complex z(10, 20); Bir sinifin baslangiç fonksiyonu olmayabilir. Bu durumda, varsayilan baslangiç fonksiyonu varmis gibi nesne tanimlanabilir. Örnegin: class Point void Set(int a, int b); void Disp(); int x, y; ; görüldügü gibi Point sinifinin hiç baslangiç fonksiyonu yok. Bu durumda varsayilan baslangiç fonksiyonu varmis gibi nesne tanimlamamiz herhangi bir hataya yol açmaz. Point pt; // Geçerli Sinifin en az bir baslangiç fonksiyonu varsa ama varsayilan baslangiç fonksiyonu yoksa, bu durumda varsayilan baslangiç fonksiyonu çagrilacak biçimde nesne tanimlanirsa hata olusur. Örnegin, class Point Point(int a, int b); void Set(int a, int b); void Disp(); int x, y; ; 21

gibi bir sinif bildiriminden sonra, Point pt; // Error! Biçiminde bir nesne tanimlayamayiz. Fakat tabi, Point pt(10, 20); gibi bir nesne tanimlamasi geçerlidir. SINIFIN BITIS FONKSIYONU Bir sinif nesnesi bellekten bosaltilacagi zaman derleyici tarafindan çagrilan üye fonksiyona bitis fonksiyonu (destructor) 2 denir. Nasil baslangiç fonksiyonu nesne yaratildiginda çagriliyorsa, bitis fonksiyonu da nesnenin bellekten bosaltilacagi zaman çagrilmaktadir. Destructor sözcügü Ingilizce yikan, yok eden anlamina gelmeketedir. Bitis fonksiyonunun ismi sinif ismiyle aynidir; ancak ismin önüne ~ atomu eklenmistir. Yani bitis fonksiyonunun ismi ~sinif_ismi biçimindedir. Asagidaki sinif bildirimini inceleyiniz. class Person Person(const char *nm); // Baslangiç fonksiyonu // Diger üye fonksiyonlar ~Person(); // Bitis fonksiyonu char *name; int no; ; Person sinifinin bitis fonksiyonu, ~Person(); prototipi ile belirtilmis olandir. Tanimlanmasi da kurala uygun olarak, Sinif ismi Bitis fonksiyonu oldugunu belirtiyor Fonksiyon ismi Person::~Person() //... biçiminde yapilir. Bitis fonksiyonlarinin da baslangiç fonksiyonlarinda oldugu gibi geri dönüs degerleri tanimli degildir. Geri dönüs degerinin türü yerine hiç birsey yazilmaz. Bu durum onlarin int ya da void geri dönüs degerine sahip oldugu anlamina gelmez. Sinifin bitis fonksiyonu bir tanedir ve parametresi void olmak zorundadir. (Yani parametresi olmamak zorundadir. C++ da parametre parantezi içerisine void yazmakla hiçbir sey 22

yazmamanin ayni anlama geldigini animsayiniz.) Bu durumda sinifin birden fazla bitis fonksiyonu da olamaz. Sinifin bitis fonksiyonu sinif nesnesi bellekten bosaltilacagi zaman çagrilmaktadir. Yerel degiskenlerin program akisinin tanimlanma blogunu bitirmesiyle, global degiskenlerin ise programin sonlanmasiyla bellekten bosaltildigini animsayiniz. Bu durumda yerel sinif nesnelerine iliskin bitis fonksiyonlari tanimlama blogunun sonunda, global sinif nesnelerine iliskin bitis fonksiyonlari da main fonksiyonu sonlandiginda çagrilmaktadir. Asagidaki örnegi inceleyiniz. #include <stdio.h> #include <stdlib.h> class FileType FileType(const char *fname); void Type(); ~FileType(); FILE *p; ; FileType::FileType(const char *fname) if ((fp = fopen(nm, "r")) == NULL) printf("cannot open file...\n"); exit(exit_failure); void FileType::Type() int ch; fseek(f, 0L, SEEK_SET); while ((ch = fgetc(f))!= EOF) putchar(ch); FileType::~FileType() fclose(f); int main() FileType x = "c:\\autoexec.bat"; FileType y = "c:\\config.sys"; y.type(); x.type(); return 0; 23

Verdigim örnekte FileType sinifinin baslangiç fonksiyonu dosyayi açiyor. Açilan dosyaya iliskin FILE türünden adres f isimli veri elemanina yaziliyor. Bitis fonksiyonu da açilmis olan dosyayi kapatiyor. Dosyanin açilmasi ve kapatilmasi islemlerinin baslangiç ve bitis fonksiyonlari tarafindan otomatik olarak yapildigini görüyorsunuz. Örnegimizdeki baslangiç ve bitis fonksiyonlarinin çagrilma yerlerine dikkat ediniz. Yerel sinif nesneleri için bitis fonksiyonlari tanimlandiklari bloklarin sonlarinda çagrilacaktir. #include "ftype.cpp" void main(void) FileType x = "c:\\autoexec.bat"; FileType y = "c:\\config.sys"; y.type(); x.type(); Baslangiç fonk. Baslangiç fonk. Bitis fonk. Bitis fonk. C++ da baslangiç ve bitis fonksiyonlarina iliskin her zaman geçerli olan söyle bir kural vardir: Bitis fonksiyonlari baslangis fonksiyonlari ile ters sirada çagrilirlar. Yani baslangiç fonksiyonu daha önce çalistirilan sinif nesnesinin bitis fonksiyonu daha sonra çalistirilmaktadir. Örnegin a nesnesinin baslangiç fonksiyonu b nesnesinin baslangiç fonksiyonundan daha önce çagrilmissa a nesnesinin bitis fonksiyonu da b nesnesinin bitis fonksiyonundan daha sonra çagrilir. Yukaridaki örnekte gördügünüz gibi, x nesnesinin baslangiç fonksiyonu akis dikkate alindiginda y nesnesinin baslangiç fonksiyonundan daha önce çagrilmistir. Pekiyi, bitis fonksiyonlari için ne söyleyebilirsiniz? Bu özellik ayni faaliyet alani içerisindeki sinif nesnelerinde daha önemli olmaktadir. Örnegin, Foo a, b; // burada baslangiç fonksiyonlari önce a sonra b biçiminde, bitis fonksiyonlari ise ters sirada yani önce b sonra a biçiminde çagrilacaktir. Global sinif nesnelerine iliskin bitis fonksiyonlari main fonksiyonundan sonra çagrilir. Yukarida verdigim kurala göre, birden fazla global sinif nesnesi varsa baslangiç fonksiyonlarinin çagrilma sirasina ters sirada bitis fonksiyonlari çagrilacaktir. Yani kaynak kod içerisinde en asagida tanimlanan nesnenin bitis fonksiyonu en önce çagrilir. Asagidaki programda baslangiç ve bitis fonksiyonlari hangi sirada çagrilir? class X // ; X a; X b; int main() X c; X d; 24

// X e; //.. Burada programin akisi dikkate alindiginda baslangiç fonksiyonlarinin çagrilma sirasi a, b, c, d, e biçiminde, bitis fonksiyonlarinin çagrilma sirasi ise e, d, c, b, a biçiminde olur. Bir fonksiyon return anahtar sözcügü ile sonlandirilirsa, fonksiyon sonlandirilmadan önce, o noktaya kadar tanimlanmis bütün yerel sinif nesneleri için bitis fonksiyonlari çagrilir. X bir sinif olmak üzere örnegin, int sample(void) X a; // X b; // if (func()) return -1; // X c; // return 0; Yukaridaki sample isimli fonksiyonda iç blokta func fonksiyonu çagrilarak geri dönüs degeri test edilmistir. Eger bu fonksiyonun geri dönüs degeri sifir disi bir degerse fonksiyon sonlandirilmistir. Derleyici fonksiyon sonlanmadan önce, yaratilmis olan a ve b nesneleri için bitis fonksiyonlarini da çagiracaktir. c nesnesinin programin akisinin return isleminin yapildigi noktaya gelene kadar tanimlanmadigini görüyorsunuz. Tabii bu durumda c için bitis fonksiyonu çagrilmayacaktir. Bir sinif bitis fonksiyonuna sahip olmak zorunda degildir. Bitis fonksiyonu varsa çagrilir; yoksa çagrilmaz. (Ya da bazi durumlarda derleyici tarafindan sizin için yazilarak çagrilir. Bu durumu ileride ele alacagim). Zaten sonraki konuda ele alacagimiz gibi, her sinifin bitis fonksiyonuna sahip olmasi gerekmez. Fakat baslangiç fonksiyonu ile yapilanlarin otomatik olarak geri alinmasi isteniyorsa bitis fonksiyonunun yazilmasi anlamlidir. Son olarak herhangi bir fonksiyon içerisinde tüm programin exit fonksiyonu ile sonlandirilmasi üzerinde duralim. exit fonksiyonu nerede çagrilmis olursa olsun, program içerisinde tanimlanmis tüm global sinif nesneleri için eger varsa bitis fonksiyonlari çagrilir. Tabii exit fonksiyonu ile programin sonlandirilmasi durumunda yerel sinif nesneleri için bitis fonksiyonlari çagrilmaz. BASLANGIÇ VE BITIS FONKSIYONLARI NE AMAÇLA KULLANILIR? Baslangiç fonksiyonlari sinifin veri elemanlarina belirli ilkdegeri vermek ve çesitli ilk islemleri yapmak amaciyla kullanilirlar.. Bir sinif nesnesi tanimlandiginda çesitli ilk islemlerin baslangiç fonksiyonlari tarafindan gizli bir biçimde yapilmasi algilamayi kolaylastirir. Böylece birtakim ilk islemler sinif nesnesini tanimlayan kisi tarafindan degil, onu yazan kisi tarafindan ilk elden yapilmis olur. Bitis fonksiyonu ise baslangiç fonksiyonu ile yapilan ilk islemlerin geri alinmasi için kullanilirlar. Örnegin, baslangiç fonksiyonu bir dosyayi açmissa, bitis fonksiyonu dosyayi 25

kapatabilir. Ya da baslangiç fonksiyonu seri portu çesitli degerlerle ayarlamis olabilir; bu durumda bitis fonksiyonu da bu ayarlari eski haline getirebilir. Ancak en sik karsilasilan durum, baslangiç fonksiyonunun new operatörü ile dinamik olarak bellekte bir yer ayirmasi ve bitis fonksiyonunun da ayrilan yeri delete operatörü ile bosaltmasidir. Asagidaki örnegi inceleyiniz: #include <stdio.h> #include <string.h> #include <stdlib.h> class Array Array(int length); void Display(); int GetItem(int index); void SetItem(int index, int val); int GetMax(); ~Array(); int *parray; int size; ; Array::Array(int length) parray = new int[size = length]; Array::~Array() delete [] parray; void Array::Display() for (int i = 0; i < size; ++i) printf("%d\n", parray[i]); int Array::GetItem(int index) if (index < 0 index >= size) printf("geçersiz index: %d\n", index); exit(1); return parray[index]; 26

void Array::SetItem(int index, int val) if (index < 0 index >= size) printf("geçersiz index: %d\n", index); exit(exit_failure); parray[index] = val; int Array::GetMax() 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.setitem(i, rand()); x.display(); printf("en Büyük Eleman: %d\n", x.getmax()); Örnegimizde int türden bir dizi bir sinifla temsil edilmistir. Baslangiç fonksiyonunda dizi için tahsisat yapilmis; bitis fonksiyonunda da bu alan bosaltilmistir. GetItem ve SetItem fonksiyonlarinin sinir kontrolü yaparak diziye eleman yerlestirme ve diziden eleman çekme islemlerini yaptigini görüyorsunuz. Ayrica, GetMax dizinin en büyük elemanini bulmakta, Display ise dizinin bütün elemanlarini yazdirmaktadir. SINIF TÜRÜNDEN ADRESLER VE GÖSTERICILER Bir sinif nesnesinin adresi alinabilir. Elde edilen adresin türü nesnenin iliskin oldugu sinifin türündendir ve ayni türden bir göstericiye atanmalidir. Örnegin yukaridaki örnegimizde belirtilen Date isimli sinifa iliskin gösterici tanimlanmis olsun. Date *p; ile p göstericisinin gösterdigi yer, yani *p, Date sinifi türündendir. Baska bir deyisle, p göstericisinin gösterdigi yerde Date sinifi türünden bir nesne vardir. Derleyici bu adresten 27

baslayarak ilk sizeof(int) kadar byte i sinifin day veri elemani, sonraki iki sizeof(int) kadar byte i ise sirasiyla month ve year veri elemanlari olarak ele alacaktir. Simdi bir sinif nesnesinin adresinin bir sinif göstericisine atandiginda neler oldugunu adim adim inceleyelim. (Asagidaki sekil int türünün 4 byte oldugu varsayimiyla çizilmistir. 00501FC0 yalnizca konuya açiklik getirmek amaciyla kullanilan rastgele bir adrestir.) Date x; // Date sinifinin veri elemanlari için yer ayriliyor. x... day month year... 00501FC0 00501FC4 00501FC8 Date *p; p = &x; // p göstericisinin gösterdigi yer Date sinifi türünden ele alinacak // x sinif nesnesinin adresi p göstericisine ataniyor 00501FC0 p *p ile x ayni nesne... day month year... 00501FC0 00501FC4 00501FC8 Artik p adresinden baslayan bilgiler x sinif nesnesinin veri elemanlari olarak yorumlanir. Bu durumda *p ifadesi ile x ayni nesneleri belirtecektir. Sinif türünden bir gösterici yoluyla sinifin veri elemanlarina ve üye fonksiyonlarina -> operatörü ile erisilebilir. Örnegin artik: p -> Disp(); ile Disp üye fonksiyonu çagrilabilir. Bu durumda Disp üye fonksiyonu p adresindeki (yani p adresinden baslayan) veri elemanlarini kullanir. Daha açik bir anlatimla, x.disp(); gibi bir çagirmada Disp üye fonksiyonu x nesnesinin veri elemanlarini kullaniyor. Yani Disp içerisinde kullanilan day, month, year degiskenleri x sinifinin veri elemanlaridir. Iste, p ->Disp(); çagirmasiyla da Disp üye fonksiyonu p adresindeki veri elemanlarini kullanir. p adresinde p = &x islemi ile x nesnesinin adresi olduguna göre, Disp fonksiyonu da x in veri elemanlarini kullanacaktir degil mi? Yani iki islem esdegerdir. 28

Tabii -> operatörü yerine öncelik parantezi ve nokta operatörlerini de kullanabilirsiniz. (*p).disp() ; ile p->disp(); esdegerdir. Sinif göstericisi yoluyla sinifin veri elemanlarina yine -> operatörü ile erisilebilir. Ancak veri elemanlarinin erisilebilir olmasi (yani sinifin public bölümün bildirilmis olmasi) gerekir. Örnegin, p->day = 10; gibi bir ifadenin geçerli olmasi için day veri elemaninin sinifin public bölümünde olmasi gerekir. Asagidaki kodu yazarak test ediniz. int main(void) Date x; Date *p; p = &x; p -> Disp(); Sinifin baslangiç fonksiyonu yalnizca sinif nesnesi yaratilirken çagrilmaktadir. Bir sinif türünden gösterici tanimlandiginda baslangiç fonksiyonu çagrilmaz. Örnegin, Date date; Date *p; // Baslangiç fonksiyonu çagrilir, çünkü nesne yaratilmis! // Baslangiç fonksiyonu çagrilmaz, çünkü gösterici yaratilmis! SINIF TÜRÜNDEN REFERANSLAR Sinif türünden referanslar da söz konusu olabilir. Bunun için referansi ayni türden bir sinif nesnesi ile ilkdeger vererek tanimlamak gerekir. Örnegin, Complex a(10, 20); Complex &r = a; Burada Complex sinifi türünden r isimli referans yine Complex sinifi türünden a nesnesinin kendisiyle ilkdeger verilerek tanimlanmistir. Derleyici bu durumda ilkdeger olarak verilen a nesnesinin adresini r referansina yerlestirecektir. Referans yoluyla sinifin veri elemanlarina ve üye fonksiyonlarina (tipki yapilarda oldugu gibi) nokta operatörü ile erisilir. Örngin Complex sinifinin Disp isimli bir üye fonksiyonu oldugunu varsayalim. Bu üye fonksiyonu r referansi ile, r.disp(); biçiminde çagirabiliriz. Bir referans ile bir üye fonksiyon çagrildiginda üye fonksiyon referansin içerisindeki adreste bulunan veri elemanlarini kullanir. Örnegin yukaridaki çagirmada Display üye fonksiyonu r referansinin içerisindeki adreste bulunan veri elemanlarini kullanacaktir. Bu referansin içerisinde a sinif nesnesinin veri elemanlarinin adresi bulunmaktadir, degil mi? Ilk deger verme isleminden sonra r ile a ifadelerinin tamamen ayni anlama geldigine dikkat edin. Sinif türünden bir referansa sabitle ya da baska türden bir nesneyle ilkdeger verme islemi bazi durumlarda geçerli olabilir. Bu konu ileride ele alinacaktir. Asagidaki örnegi yazarak çalistirin. 29