![]() |
||
|
|
#1 (permalink) |
|
Bir Sınıf Türünden Nesnenin Tanımlanması
Genel biçimi: [class] <sınıf_ismi> <nesne_ismi>; class Sample x; Sample y; class anahtar sözcüğü yazılmayabilir. C++’ta yapı türünden nesne tanımlarken struct anahtar sözcüğü de kullanılmayabilir. Bir sınıf nesnesi için sınıfın toplam veri elemanları kadar yer ayrılır. /*-----class1.cpp-----*/ #include <stdio.h> class Sample { private: int a, b; public: void fonk(void); }; void main(void) { Sample x; printf("%d\n", sizeof(x)); } /*-----------------------*/ Üye Fonksiyonları Tanımlanması Üye fonksiyonları prototipleri sınıf bildirimi içerisine yerleştirilir, tanımlamaları dışarıda aşağıdaki gibi yapılır. [geri dönüş değerinin türü] <sınıf isim> :: <fonksiyon ismi> ([parametreler]) void Sample::fonk(void) { } İki tane iki nokta üstüste C++’a özgü bir operatördür. Üye fonksiyonlar amaç koda parametre türleri ve sınıf isimleriyle kombine edilerek yazılırlar. Yani aynı isimli ve aynı parametre yapısına sahip bir üye fonksiyonu ve global bir fonksiyon tanımlanabilir. Hiçbir sınıfa ait olmayan fonksiyonlara global fonksiyon denir. Sınıfın Veri Elemanlarına ve Üye Fonksiyonlarına Erişim Sınıfın veri elemanlarına ve üye fonksiyonlarına nokta operatörüyle erişilir. Bir üye fonksiyonu ancak aynı sınıf türünden bir nesneyle çağırılabilir. Eğer nesne olmadan çağırılırsa global bir fonksiyonun çağırıldığı anlaşılır. X.fonk(); /*üye fonksiyonu çağırılmış*/ fonk(); /*global fonkiyon çağırılmış*/ /*-----class2.cpp-----*/ #include <stdio.h> class Sample { public: int a, b; public: void fonk(void); }; void Sample::fonk(void) { printf("I'm sample fonk..\n"); } void fonk(void) { printf("I'm global fonk..\n"); } void main(void) { class Sample X; X.a = 10; X.b = 20; X.fonk(); fonk(); } /*-----------------------*/ Bir üye fonksiyon içerisinde sınıfın hangi bölümünde tanımlanmış olursa olsun bütün veri elemanları ve üye fonksiyonlarına doğrudan erişilebilir. Yani sınıfın veri elemanları sınıfın üye fonksiyonları arasında ortak olarak kullanılmaktadır. Bir üye fonksiyon içerisinde kullanılan üye fonksiyonları o üye fonksiyon hangi sınıf nesnesiyle çağırılmışsa o sınıf nesnesinin elemanları olur. /*-----class3.cpp-----*/ #include <stdio.h> class Sample { public: int a; public: void fonk1(int x); void fonk2(void); }; void Sample::fonk1(int x) { printf("I'm sample fonk1..\n"); a = x; } void Sample::fonk2(void) { printf("%d\n", a); } void main(void) { class Sample X; X.fonk1(50); Sample Y; Y.fonk1(100); X.fonk2(); Y.fonk2(); } /*-----------------------*/ Bir üye fonksiyonu içerisinde sınıfın bir diğer üye fonksiyonu da doğrudan çağırılabilir. Sınıfın a üye fonksiyonu X nesnesiyle çağırılmış olsun, a üye fonksiyonu içerisinde b üye fonksiyonu doğrudan çağırılabilir. Bu durumda b üye fonksiyonu içerisinde kullanılan veri elemanları X sınıf nesnesine ilişkindir. /*-----class4.cpp-----*/ #include <stdio.h> class Sample { public: int a; public: void fonk1(int x); void fonk2(void); }; void Sample::fonk1(int x) { printf("I'm sample fonk1..\n"); a = x; fonk2(); } void Sample::fonk2(void) { printf("%d\n", a); } void main(void) { class Sample X; X.fonk1(50); } /*-----------------------*/ Sınıf Faaliyet Alanı(class scope) C’de dardan genişe doğru 3 tür faaliyet alanı vardır: 1.Blok faaliyet alanı 2.Fonksiyon faaliyet alanı 3.Dosya faaliyet alanı C’de ve C++’ta aynı faaliyet alanına ilişkin birden fazla değişken aynı isimle tanımlanamaz. Ancak farklı faaliyet alanına ilişkin aynı isimli birden fazla değişken tanımlanabilir. Bir blok içerisinde birden fazla aynı isimli değişken faaliyet gösteriyorsa o blok içerisinde dar faaliyet alanına sahip olan erişilebilir. C++’ta sınıf faaliyet alanı diye isimlendirilen ayrı bir faaliyet alanı daha tanımlanmıştır. Sınıf faaliyet alanı fonksiyon faaliyet alanı ile dosya faaliyet alanı arasında bir alana sahiptir. Sınıf faaliyet alanı yalnızca bir sınıfın tüm üye fonksiyonları arasında tanınma aralığıdır. Sınıfın veri elelamanları ve üye fonksiyon isimleri sınıf faaliyet alanına uyarlar. Bir sınıfın veri elemanıyla aynı isimli sınıfın üye fonksiyonu içerisinde aynı isimli bir yerel değişken tanımlanabilir. Bu durumda fonksiyon içerisindeki blokta yerel olana erişilir. Benzer biçimde bir üye fonksiyon içerisinde bir fonksiyon çağırılmışsa çağırılan fonksiyon ile aynı isimli hem global hem de bir üye fonksiyon varsa dar faaliyet alanı kuralına göre üye fonksiyon çağırıldığı varsayılır. Çözünürlük Operatörü(: (scope resolution operator):: operatörüne çözünürlük operatörü denir. Bu opertörün hem binary-infix hem de unary-prefix olarak kullanılan tipleri vardır. 1.Binay infix resolution operatörü: Bu kullanımda sol tarafındaki operandın bir sınıf ismi, sağ tarafındaki operandın ise veri elemanı ya da fonksiyon ismi olması gerekir. Bu operatör sınıfın faaliyet alanı probleminden dolayı gizlenmiş olan veri elemanına ya da üye fonksiyonuna erişimini sağlar. void Sample::fonk1(int a) { printf(“Sample fonk1..\n”); Sample::a = a; /*sınıfın veri elemanı olan a’ya parametre a’yı ata*/ } 2.Unary prefix resolution operatörü: Bu durumda operand global bir değişken ya da fonksiyon ismi olabilir. Bu haliyel bu operatör faaliyet alanı probleminden dolayı global olana erişimi sağlar. Bu operatör öncelik tablosunun en yüksek düzeyinde bulunur. Başlangıç ve Bitiş Fonksiyonları 1.Başlangıç Fonksiyonları(constructors) Bir sınıf destesi tanımlandığında derleyici tarafından otomatik olarak çağırılan fonksiyona sınıfın başlangıç fonksiyonu denir. Yerel bir sınıf nesnesi programın akışı tanımlama noktasına geldiğinde, global bir sınıf nesnesiyse program belleğe yüklenir yüklenmez yaratılır. Başlangıç fonksiyonun ismi sınıf ismiyle aynı olmalıdır. Başlangıç fonksiyonlarının geri dönüş değeri gibi bir kavramı yoktur. Yani geri dönüş türü yerine bir şey yazılmaz. bu durum int ya da void anlamına gelmez. Başlangış fonksiyonları içerisinde return anahtar sözcüğü kullanılabilir, ancak yanına bir ifade yazılamaz. C++’ta farklı parametre yapısına sahip birden fazla başlangıç fonksiyonu olabilir. Parametresi olmayan(yani void olan) başlangış fonksiyonuna default başlangıç fonksiyonu(default constructor) denir. Eğer sınıf nesnesi nesne isminden sonra parantez açılmadan yani normal bir biçimde tanımlanmış ise (örneğin: X n bu durumda varsayılan başlangıç fonksiyonu çağırılır. Eğer nesne isminden sonra bir parantez açılır ve içerisine bir parametre listesi yazılırsa (örneğin: X n(10) parametre listesine uygun olan başlangıç fonksiyonu çağırılır. Uyarı: Nesne isminden sonra parantez açılıp içine hiçbirşey yazılmazsa bu durumda varsayılan başlangıç fonksiyonu çağırılmaz. Bu ifade bir fonksiyon prototipi anlamına gelir. Örneğin: X a(); /*parametresi olmayan, X türünden bir fonksiyonun prototipi*/ Global sınıf nesnelerine ait başlangıç fonksiyonları main fonksiyonundan önce çağırılır. Daha yukarıda tanımlanan daha önce çağırılacak bir biçimde sıralama söz konusudur. 2.Bitiş Fonksiyonu(destructor) Bir nesne faaliyet alanını bitirmesiyle bellekten silinir. Yerel değişkenler programın akışı tanımlandıkları bloğun sonunda, global değişkenler ise programın bitimiyle bellekten silinirler. Bir sınıf nesnesi bellekten silineceği zaman otomatik olarak çağırılan fonksiyona bitiş fonksiyonu(destructor function) denir. Bitiş fonksiyonunun ismi sınıf ismiyle aynıdır, anck başına bir ~ sembolü getirilir. Bitiş fonksiyonunun da geri dönüş değeri gibi bir kavramı yoktur. Bitiş fonksiyonu en az ve en fazla bir tane olabilir. Parametresi void olmak zorundadır. Yani parametresi olmamak zorundadır. Varsayılan bitiş fonksiyonu diye bir kavram yoktur. Global bir sınıf nesnesine ait bitiş fonksiyonu programın sonucunda main bittikten sonra yani main’in sonunda çalıştırılır. Başlangıç ve bitiş fonksiyonlarının çağırılma sıraları her zaman terstir. a ve b herhangi türden iki sınıf nesnesi olmak üzere başlangıç fonksiyonları önce a sonra b olacak şeklinde çağırılıyorsa bitiş fonsiyonları önce b sonra a şeklinde çağırılır(LIFO sistemi). Başlangıç ve Bitiş Fonksiyolarının Bulundurulma Kuralı Sınıfın bitiş fonksiyonu olmak zorunda değildir. Yani varsa çağırılır yoksa çağırılmaz. Bir sınıf nesnesinin tanımlanma biçimine uygun bir başlangıç bir fonksiyonu olmak zorundadır. Ancak sınıfın hiçbir başlangıç fonksiyonu yoksa ve nesne varsayılan başlangıç fonksiyonu çağırılacak biçimde tanımlanmışsa bu durum istisna olarak hata oluşturmaz. Ancak sınıfın herhangi bir başlangıç fonksiyonu varsa fakat varsayılan başlangıç fonksiyonu yoksa varsayılan fonksiyonu çağıracak biçimde yapılacak bir tanımlama hata ile sonuçlanır. Başlangıç ve Bitiş Fonksiyonlarının Kullanılma Nedenleri Nesne yönelimli programlama da bir sınıf belirli bir amacı gerçekleştiren bir kütüphane olarak ele alınabilir. Örneğin seri port işlemlerini yapan bir sınıf tasarlanabilir. Fare işlemleri için ayrı bir sınıf yazılabilir. Bu sınıfların faydalı işlemleri yapan bir takım üye fonksiyonları olmalıdır. Bu üye fonksiyonlar sınıfın veri elemanlarını ortak olarak kullanırlar. Bir sınıf bir takım yararlı işleri yapmaya aday ise o yararlı işlemleri gerçekleştirmek için bazı hazırlık işlemleri gerekebilir. Örneğin seri port ile ilgili işlem yapan bir sınıfta seri portun set edilmesi, fare işlemleri yapan sınıfta farenin reset edilmesi dosya işlemleri yapan bir sınıfta dosyanın açılması bu tür hazırlık işlemleridir. Bu hazırlık işlemleri sınıfın başlangıç fonksiyonu içerisinde yapılırsa sınıfı kullanan kod küçülür, ayrıntılar göz ardı edilir ve algılama iyileştirilir(abstraction). Örneğin dosya işlemleri yapan sınıfın başlangıç fonksiyonu içerisinde dosya açılabilir. Nesne tanımlanır tanımlanmaz hazırlık işlemlerinin otomatik olarak yapılması sınıfı kullanan kişilerin de işlerini kolaylaştırır. Bitiş fonksiyonu başlangıç fonksiyonuyla yapılan hazırlık işlemlerinin otomatik bir biçimde geri alınması için kullanılır. Örneğin dosya işlemlerini yapan sınıfın bitiş fonksiyonu otomatik olarak kapayabilir. Seri port işlemlerini yapan sınıfın bitiş fonksiyonu port ayarlarını eski durumuna getirebilir. Tabii bazı durumlarda hazırlık işlemlerinin geri alınması gerekmeyebilir. Yani başlangıç fonksiyonunun olması bitiş fonksiyonunun olmasını mantıksal bakımdan gerekli kılmaz. Sınıflarda Temel Erişim Kuralları Temel erişim kuralı sınıf bölümlerinin ne anlama geldiğiyle ilgilidir. İki kural vardır: 1.Bir sınıf nesnesi yoluyla dışarıdan nokta ya da ok operatörünü kullanarak sınıfın yalnızca public bölümünde bildirilen veri elemanlarına ya da fonksiyonlarına erişilebilir. Private veya protected bölümlerine erişilemez. 2.Sınıfın üye fonksiyonu hangi bölümde bildirilmiş olursa olsun sınıfın her bölümündeki veri elemanlarına ve üye fonksiyonlarına erişebilir. Yani üye fonksiyonlar içerisinde sınıfın her bölümündeki veri elemanlarını kullanabilir ve üye fonksiyonlarını çağırabiliriz. Genellikle sınıfın veri elemanları sınıfın rivate bölümünde üye fonksiyonları ise public bölümde tutulur. Böylece veri elemanlarına dışarıdan doğrudan erişilemez. Dışarıdan doğrudan üye fonksiyonlara erişilir. Üye fonksiyonları veri elemanlarına erişirler. Yani veri elemanlarına doğrudan değil üye fonksiyonlar yoluyla erişilmesi istenmiştir. Eğer private bölgedeki veri elemanlarının değerlerini almak ya da bunlara değer yerleştirilmek istenirse bunlarla ilişki kuran bir grup get ve set fonksiyonu yazmak gerekir. Yeniden kullanılabilirlik(reusability) nesne yönelimli programlama tekniğinin anahtar kavramlarından birisidir. Bu kavram yazılmış olan bir kodun özellikle de bir sınıfın başka projelerde tekrar yazılmadan kullanılması anlamına gelir. Veri Elemanlarının private, Üye Fonksiyonlarının public Kısmına Yazılması Genellikle sınıflarda veri koruması istendiği zaman sınıfın veri elemanları private bölgeye üye fonksiyonları ise public bölgeye yazılırlar. Sınıfın veri elemanlarının private bölgeye yerleştirilmesi dışarıdan onlara doğrudan erişimi engeller. Onlara public bölgedeki bir grup üye fonksiyon ile erişiriz. Normalde tasarlanmış olan bir sınıf çok değişik ve uzun kodlarda kullanılabilir. Yani sınıfı kullanan kodlar sınıfın kendi kodlarından çok daha fazladır. Eğer veri elemanlarını private bölgeye yerleştirirsek o veri elemanlarının genel yapısında değişiklik olduğunda sınıfı kullanan kodları değiştirmek zorunda kalmayız. Yalnızca prototipleri aynı kalmak üzere sınıfın üye fonksiyonlarını yeniden yazmak zorunda kalırız. Oysa veri elemanları puıblic bölgeye yerleştirilseydi, dışarıdan bu elemanlara doğrudan erişilebilirdi ve veri yapısı değiştiğinde onu kullanan tüm kodları değiştirmek gerekirdi. Çeşitli veri elemanlarını ve üye fonksiyonları private bölgeye yerleştirmekle onları sınıfı kullanan kişinin algısından uzak tutarız. Kişiler erişemeyecekleri bilgileri incelemezler. Bu durumda nesne yönelimli programlama tekniğinde veri gizleme(data hiding) denir. Tabi veri elemanlarının private bölgeye yerleştirilmesi bunlara erişimi zorlaştırır. Çünkü erişim doğrudan değil, ara birim üye fonksiyonlarla yapılır. Sınıfın veri yapısı değiştirilmeyecekse veri elemanları doğrudan public bölgeye de yerleştirilebilir. Bu durumda onları doğrudan kullanmanın bir zararı olmaz. /*----date2.cpp----*/ /*----date3.cpp----*/ Dinamik Tahsisat Yapan Sınıflar Pek çok sınıf başlangıç fonksiyonu içerisinde bir veri elemanı için dinamik tahsisat yapar. Tahsis edilen bu dinamik bölgenin bitiş fonksiyonu içerisinde otomatik olarak sisteme iade edilmesi istenir. /*----consdest.cpp----*/ Bir dosyanın başlangıç fonksiyonu içinde açılması ve bitiş fonksiyonunda otomatik olarak kapatılması gibi durumlara sıkça rastlanır. /*----clasfile.cpp----*/ Bazen bitiş fonksiyonunda otomatik olarak yapılan geri alma işlemi bir üye fonksiyon ile bilinçli olarak da yapılabilir. Böylece bitiş fonksiyonundaki geri alma işlemi geçersiz hale gelir. Çünkü geri alma işlemi daha önce geçekleşmiştir. O halde bitiş fonksiyonu içerisinde geri alma işlemi daha önce yapılmışsa geri alma işlemini yapmamak gerekir. bunu sağlamak için çeşitli veri elamanlarından faydalanılabilir. File::File(void) { f = NULL; } File::~File(void) { if (f) fclose(f); } Sınıf Türünden Göstericiler ve Adresler Bir sınıf nesnesinin veri elamanları tıpkı bir yapı gibi belleğe adrışıl bir biçimde yerleştirilir. Sınıfın aynı bölümündeki veri elemanları o bölüm içerisinde ilk yazılan düşük anlamlı adreste olacak biçimde ardışıl olarak yerleştirilirler. Ancak bölümlerin sırası için herhangi bir zorunluluk standartlara konulmamıştır. Yani bölüm içleri ilk yazılan düşük anlamlı adreste olacak biçimde yerleştirilir, ancak bölümlerin kendi aralarında nasıl yerleştirileceği derleyiciden derleyiciye değişebilir. Bölümler arası yerleşim de ardışıl olmak zorundadır. Ancak derleyicilerin hemen hepsi bölüm farkı gözetmeksizin ilk yazılan elemanın düşük anlamlı adreste olacağı biçimde yerleşim kullanırlar. Bir sınıf nesnesinin adresi alınabilir. Elde edilen adresin sayısal bileşeni sınıf veri eleman bloğunun başlangıç adresidir. Bir sınıf türünden değişkenin adresi aynı türden bir sınıf göstericiye yerleştirilmelidir. Bir sınıf göstericisi yoluyla sınıfa ilişkin bir üye fonksiyon ok operatörüyle ya da * ve nokta operatörüyle çağırılabilir. Date x(10, 10, 1999); Date *p; p = &x; x.Disp(); /* Disp() fonksiyonu çağırılır */ p -> Disp(); /* Disp() fonksiyonu çağırılır */ (*p).Disp(); /* Disp() fonksiyonu çağırılır */ Bir üye fonksiyon sınıfa ilişkin bir gösterici kullnılarak ok operatörüyle çağırıldığında üye fonksiyon o göstericiyel belirtilen adreste bulunan veri elemanlarını kullanır. p -> Disp(); Disp() üye fonksiyonu p göstericisinin içerisinde bulunan veri elemanlarını kullanır. /*-----date4.cpp-----*/ Yine gösterici yoluyla sınıfın veri elemanlarına erişilir. Tabii bu elemanların public bölgede olması gerekir. Sınıf Türünden Referanslar Bir sınıf türünden referans aynı türden bir sınıf nesnesiyle ilk değer verilerek tanımlanabilir. Bu durumda derleyici ilk değer olarak verilen sınıf nesnesinin adresini referansa yerleştirir. Bir referans yoluyla sınıfın veri elemanlarına ve üye fonksiyonlarıyla nokta operatörü kullanılarak erişilir. Bir referans ile bir üye fonksiyonu çağırıldığında üye fonksiyon referansın içerisinde bulunan adresteki veri elemanlarını kullanır. /*-----date5.cpp-----*/ Sınıf Türünden Değişkenlerin Fonksiyonlara Geçirilmesi Bir sınıf tıpkı yapı gibi bileşik bir nesnedir. Bu durumda yapılarda olduğu gibi fonksiyona geçirmede iki teknik kullanılabilir. Sınıfın kendisinin geçirilmesi yöntemi özel durumlar dışında kötü bir tekniktir. Adres yöntemiyle fonksiyona geçirme tekniği tercih edilmelidir. Adresle geçirme işlemi C++’ta iki biçimde yapılabilir: 1.Gösterici kullanılarak: Yani fonksiyon bir sınıf nesnesinin adresiyle çağırılır. Fonksiyonun parametre değişkeni de aynı sınıf türünden bir gösterici olur. Bu durumda fonksiyon içerisinde sınıfn veri elemanlarına ve üye fonksiyonlarına ok operatörüyle erişilir. /*-----date6.cpp-----*/ 2.Referans kullanılarak: Bu durumda fonksiyon bir sınıf nesnesinin kendisiyle çağırılır. Fonksiyonun parametre değişkeni aynı türden bir referans olur. Fonksiyon içerisinde veri elemanlarına ve üye fonksiyonlarına nokta operatörüyle erişilir. /*-----date7.cpp-----*/ Bu iki yöntem arasında etkinlik farkı yoktur. Date *p; p - > Disp(); İfadesi syntax bakımından geçerlidir. Ancak bir gösterici hatası söz konusudur. Disp() fonksiyonu p göstericisinin içerisindeki rastgele yerdeki veri elemanlarını kullanır. Bir sınıf türünden referans ya da gösterici tamnımlandığında başlangıç fonksiyonu çağırlmaz. Çünkü başlangıç fonksiyonu nesnenin kendisi tanımlandığında çağırılmaktadır. Sınıf Türünden Dinamik Alan Tahsis Edilmesi new operatörüyle bir sınıf türünden heap üzerinde dinamik bir alan tahsis edilebilir. Tahsis edilen alanın başlangıç adresi aynı türden bir göstericiye atanmalıdır. Madem ki new operatörüyle heap üzerinde bir nesne yaratılıyor o halde yaratılan nesne için başlangıç fonksiyonu çağırılır. Bir sınıf türünden dinamik alan tahsis edildiğinde tahsis edilme işleminin hemen ardından derleyici tarafından otomatik olarak tahsis edilen alan için başlangıç fonksiyonu çağırılır. Eğer sınıf isminden sonra bir parantez açılmazsa varsayılan başlangıç fonksiyonu, açılır ve bir parametre listesi yazılırsa parametre listesine uygun olan başlangıç fonksiyonu çağırılır. X *p; p = new X; /*default başlangıç fonksiyonu çağırılır*/ p = new X(a, b, c); /*parametreleri uyan başlangıç fonksiyonu çağırılır */ p = new X(); /*default başlangıç fonksiyonu çağırılır */ /*-----date8.cpp-----*/ C++’ta dinamik bellek yönetiminin dinamik bellek fonksiyonlarıyla değil de new ve delete ile yapılmasının nedeni başlangıç fonksiyonunun otomatik olarak çağırılmasını sağlamak içindir. Sınıf Türünden Yaratılmış Dinamik Bir Alanın Boşaltılması Dinamik olarak tahsis edilmiş bir sınıf delete operatörüyle sisteme iade edilebilir. delete operatörü alanı serbets bırakmadan önce bitiş fonksiyonunu otomatik olarak çağırmaktadır. /*-----date9.cpp-----*/ new operatörüyle bir sınıf türünden bir dizi alan tahsis edilebilir. p = new X[n]; Bu durumda derleyici n * sizeof(X) kadar alanı dinamik olarak tahsis eder ve yaratılmış olan n nesnenin her biri için default başlangıç fonksiyonunu çağırır. Bir dizi için alan tahsis edilmesi durumunda tahsis edilen sınıf nesneleri için başka bir başlangıç fonksiyonu çağırılamaz. p = new X[n] (a, b, c); /*Geçerli değildir. Tür dönüşümü olarak düşünülür*/ Ancak bir dizi sınıf nesnesi için dinamik tahsisat işlemine çok nadir rastlanır. Bir dizi sınıf nesnesi için tahsis edilmiş olan alanın delete ile silinmesi durumunda her bir sınıf nesnesi için ayrı ayrı bitiş fonksiyonu çağırılır. Böle bir durumda eğer yanlışlıkla silme işlemi köşeli parantez kullanılmadan yapılırsa(örneğin: delete p tüm dinamik alan serbest bırakılır fakat yalnızca ilk yaratılmış nesne için bitiş fonksiyonu çağırılır. /*-----date10.cpp-----*/ Sınıf Türünden Dinamik Tahsisat İşleminin Anlam ve Önemi class X { public: X(int n); void Disp(void); private: int x; }; X:X(int n) { x=n; } void X: isp(void){ printf(“%d\n”,x); } X::~X(void) { printf(“I ** a destructor\n”); } X *p; void main (void) { { } } Normal bir sınıf nesnesi için başlangıç ve bitiş fonksiyonlarının çağrılacağı nokta, nesnenin tanımlandığı yere göre belirlenmektedir. Örneğin yerel bir nesne için bitiş fonksiyonu kesinlikle tanımlama bloğnun sonunda çağrılır. Oysa tahsisat işlemi dinamik olarak yapılırsa, nesnenin yaratılması ve bellekten silinmesi, başlangıç ve bitiş fonksiyonlarının çağrılması programcının istediği noktada yapılabilir. Dinamik olarak tahsis edilmiş sınıf nesnesi için delete operatörü ile silme yapılmamışsa tahsis edilmiş olan alan normal olarak programın bitiminde sisteme iade edilir. Ancak bu iade edilme işlemi sırasında bitiş fonksiyonu çağrılmaz. Dinamik olarak tahsis edilmiş bir alan için bitiş fonksiyonunun çağrılması ancak delete işlemi ile mümkün olabilir. sınıf nesnesi için dinamik tahsisat yapıldığı halde başlangıç fonksiyonun çağrılmaması aşağıdaki gibi sağlanabilir. p= (X *) new char [sizeof(X)] CONST üye fonksiyonlar Standart C’de bir fonksiyonun const olması tanımlı ve geçerli değildir. Oysa C++’ds bir sınıfın üye fonksiyonu const olabilir. (C++’da global bir fonksiyon const olamaz, sadece sınıf üye fonksiyonları const olabilir) Bir üye fonksiyonu const yapabilmek için, fonksiyon prototipinde ve tanımlama sırasında parametre parantezi kapatıldıktan sonra const anahtar sözcüğü yazılmalıdır. (Not const anahtar sözcüğünün hem prototipte hem de tanımlama cümlesinde yazılması zorunludur). Const üye fonksiyon içerisinde sınıfın üye elemanları değiştirilemez. Class X { public: X(int n); void set(int n); void Disp (void) const; ~X(void); private: int x; }; X::X(int n) { x=n; } void X: isp(void) const{ printf(“%d\n”,x); x=10; } X::~X(void) { printf(“I ** a destructor\n”); } void main(void) { X a(20); //hata verecek a.disp(); } Const üye fonksiyonlar, const üye fonksiyonlarda olduğu gibi okunabilirliği artırmak için kullanılabilir. Class X { public: X(int n); void set(int n); void Disp (void) const; ~X(void); private: int x; }; X::X(int n) { x=n; } void X::set(int n) { x=n; } void X: isp(void) const{ printf(“%d\n”,x); x=10; } X::~X(void) { printf(“I ** a destructor\n”); } void main(void) { X a(20); //hata verecek a.disp(); a.set(50); a.disp(); } Const bir fonksiyon içerisinde const olmayan bir fonksiyon çağrılamaz, çünkü const olmayan bir fonksiyon sınıfın veri elemanlarını kullanabilir. Const bir üye fonksiyonun içerisinde sınıfın bütün üye elemanlarının const olduğu kabul edilir. Örneğin böyle bir üye fonksiyon içerisinde sınıfın bir veri elemanının adresi const olmayan bir göstericiye atanamaz. Not: C++’da const bir değişkenin adresi const olmayan bir göstericiye atanamaz. Sınıfın başlangıç ve bitiş fonksiyonları const üye fonksiyonu olarak tanımlanamaz. Yasak. Const Sınıf Nesneleri void main(void) { const X a(10); } Bir sınıf nesnesi const olabilir. Bu durumda sınıf nesnesinin veri elemanları her hangi bir biçimde değiştirilemez. Const bir sınıf nesnesi ile ancak const bir üye fonksiyon çağrılabilir, const olmayan bir üye fonksiyon çağrılamaz. Not: Const tamamen derleyici için olan bir kavramdır. Const bir sınıf göstericisi ya da referansı söz konusu olabilir. Tabii bu gösterici ya da referansla ancak const üye fonksiyonlar çağırılabilir. Özellikle bir fonksiyonun parametre değişkeninin const bir sınıf göstericisi ya da const bir sınıf referansı olma durumuna sıklıkla rastlanır. String Sınıfı C’de yazılar üzerinde işlem yapmak için karakter dizileri kullanılır. Ancak karakter dizilerinin normal bir dizi biçiminde tanımlanması genellikle kullanılan yöntem olsa da önemli problemleri vardır. Örneğin dizinin uzunluğu sabit ifadesi olmak zorundadır. Böylece yazıların büyütülmesi gibi işlemler verimsiz bir biçimde yapılır. Çünkü işlemler en kötü olasılıkla uzun diziler açılarak yürütülmek zorundadır. Bu da belleğin verimsiz kullanılması anlamına gelir. Tabii dinamik bellek yönetimiyle yer kaplama bakımından etkinlik problemi giderilebilir. Ancak işlemsel karmaşıklık artar, kod büyümesi oluşur. Ancak C++’ta yazı işlemleri için özel tasarlanmış string sınıfları kullanılabilir. Bu sınıflar yazıları dinamik olarak saklarlar. Dinamik bellek işlemleri üye fonksiyonlar tarafından otmatik olarak yapıldığı için algılsal düzeyde karmaşıklık oluşmaz. Bir sınıf kullanılarak yazılar üzerinde işlemler yapmak için MFC sınıf sisteminde Cstring isimli bir sınıf tasarlanmıştır. Yine son yıllarda ANSI C++ içerisine dahil edilen standart bir string sınıfı vardır. Standardizasyon konusundan problemleri olmasına karşın yazı işlemlerini yapan bir string sınıfı her türlü derleyici sisteminde ve sınıf kütüphanelerinde kullanılmak üzere hazır bulundurulmaktadır. Böyle bir sınıfın tasarımı için C++’ın pek çok konusundan faydalanılmaktadır. Kurs içerisinde çeşitli konular görülürken bu sınıf tekrar tekrar görülecektir. String Sınıfın Tasarımı String sınıfın en az iki private veri elemanı olmalıdır. Birincisi yazının başlangıç adresini tutan karakter türünden bir adrestir. Yazı için alan dinamik olarak tahsis edilerek başlangıç adresi bu göstericiye yerleştirilecektir. İkinci veri elemanı yazının uzunluğunu tutan int türünden bir değişken olabilir. Tabii aslında yazının sadece başlangıç adresinin bilinmesi uzunluğunun istenildiği zaman bulunabileceği anlamına gelmektedir. Ancak yazı uzunluğuna duyulan gereksinimin fazlalığı ve hızın yer kaplamaya göre tercih edilmesi böyle bir veri elemanının bulunmasını anlamlı hale getirmektedir. Böyle bir sınıftan ilk istenecek şey dinamik yapılan tahsisatların üye fonksiyonlar tarafından gizlice gerçekleştirilmesidir. Sınıfn başlangıç fonksiyonu new operatörüyle tahsisat yapabilir ve bitiş fonksiyonu bu tahsisatı serbest bırakabilir. Bir Sınıfın Başka Bir Sınıf Türünden Veri Elemanına Sahip Olması Durumu Bir sınıf başka bir sınıf türünden veri elemanına sahipse o sınıfın üye fonksiyonları içerisinde eleman olan sınıf nesnesine erişilebilir. Ancak o sınıf nesnesinin private ve protected bölümlerine erişilemez. Sınıfın başlangıç fonksiyonu bir üye fonksiyonuymuş gibi dışarıdan çağırılamaz, nesne yaratılırken otomatik olarak çağırılır. O halde bir sınıfın başka sınıf türünden veri elemanına sahip olması durumunda bu veri elemanına başlangıç fonksiyonu içerisine nasıl ilk değer atanacaktır? Bunu sağlamak için C++’ta eleman olan sınıfın başlangıç fonksiyonu eleman nesne için elemana sahip sınıfın başlangıç fonksiyonunun ana bloğunun başında gizlice çağırılır. Eğer “:” syntax’i ile bir belirleme yapılmamışsa eleman olan sınıfın default başlangıç fonksiyonu çağırılır. Eğer “:” ile veri elemanının ismi yazılıp parantez içerisine bir parametre listesi belirtilmişse, eleman b parametre listesine uygun başlangıç fonksiyonu ile ilk değe alır. X bir sınıf a ise başka bir sınıf türünden X sınıfının bir veri elemanı ise bu veri elemanı için istenilen bir başlangıç fonksiyonunun çağırılması aşağıdaki syntax ile yapılır. X::X(...) : a(..) { ... } Bir parametre değişkeninin faaliyet alanı tanımlanma noktasından fonksiyonun sonuna kadar olan bölgedir. Yani elemana ilişkin “:” ifadesinde fonksiyon parametreleri doğrudan kullanılabilir. /*-----claincla.cpp-----*/ #include <stdio.h> #include <time.h> #include <string.h> class Date { int day, month, year; public: Date(void); Date(int d, int m, int y); void Disp(void) const; }; class Person { char *name; Date bdate; public: Person(const char *nm, int d, int m, int y); void Disp(void) const; ~Person(void); }; Date: ate(int d, int m, int y){ day = d; month = m; year = y; } Date: ate(void){ long t; t = time(NULL); struct tm *p = localtime(&t); day = p -> tm_mday; month = p -> tm_mon + 1; year = p -> tm_year + 1900; } void Date: isp(void) const{ printf("%02d/%02d/%04d\n", day, month, year); } void Person::Person(const char *nm, int d, int m, int y) : bdate(d, m, y) { name = new char[strlen(nm) + 1]; strcpy(name, nm); } void Person: isp(void) const{ printf("%s\n", name); bdate.Disp(); } Person::~Person(void) { delete[] name; } void main(void) { Person X("Ali Serçe", 10, 12, 1990); X.Disp(); } /*-------------------------*/ Başlangıç fonksiyonlarının çağırılma sıraları önce elemana ilişkin sınıfın başlangıç fonksiyonu sonra elemana sahip sınıfın başlangıç fonksiyonu biçimindedir. C++’ta her zaman başlangıç ve bitiş fonksiyonlarının çağırılıma sırası terstir. Bu durumda elemana ilişkin sınıfın bitiş fonksiyonu elemana sahip bitiş fonksiyon bloğunun sonunda otomatik olarak çağırılır. Yani elemana sahip sınıfın bitiş fonksiyonu daha önce çağırılmaktadır. Bir sınıfın başka sınıf türünden birden fazla veri elemanı varsa bu veri elemanları için başlangıç fonksiyonları çağırılma sırası sınıf bildiriminde ilk yazılan önce olacak biçimde yapılır. “:” syntax’i ile belirtilen sıra öneli değildir. class Y { ......... ........ }; class X { ......... ......... Y a, b; }; X:X(....) : b(...), a(...) { ........ ........ } /*önce a’nın parametreleriyle belirtilen */ /*Y sınıfının başlangıç fonksiyonu çağırılır. */ Bitiş fonksiyonlarının çağırılma sırası başlangıç fonksiyonuna göre ters sırada olmak zorundadır. Yani örnekte önce b için bitiş fonksiyonu çağıılır. Bir Sınıfın başka Bir Sınıf Türünden Gösterici Veri Elamanına Sahip Olması Durumu Bu duruma diğer durumdan daha sıklıkla rastlanmaktadır. Tabii gösterici veri elemanı için başlangıç fonksiyonu çağırılmaz. Ancak elemana sahip sınıfın başlangıç fonksiyonu içerisinde bu gösterici için dinamik tahsisat yapılmalıdır. Elemana sahip bitiş fonksiyonu içerisinde de delete operatörüyle başlangıç fonksiyonunda tahsis edilen alan serbest bırakılmalıdır. Bu serbest bırakma ile bitiş fonksiyonu da kendiliğinden çağırılacaktır. /*-----claincl2.cpp-----*/ #include <stdio.h> #include <time.h> #include <string.h> class Date { int day, month, year; public: Date(void); Date(int d, int m, int y); ~Date(void); void Disp(void) const; }; class Person { char *name; Date *bdate; public: Person(const char *nm, int d, int m, int y); void Disp(void) const; ~Person(void); }; Date: ate(int d, int m, int y){ day = d; month = m; year = y; } Date: ate(void){ long t; t = time(NULL); struct tm *p = localtime(&t); day = p -> tm_mday; month = p -> tm_mon + 1; year = p -> tm_year + 1900; } void Date: isp(void) const{ printf("%02d/%02d/%04d\n", day, month, year); } Date::~Date(void) { printf("I ** a Date destructor..\n"); } void Person::Person(const char *nm, int d, int m, int y) { name = new char[strlen(nm) + 1]; strcpy(name, nm); bdate = new Date(d, m, y); } void Person: isp(void) const{ printf("%s\n", name); bdate -> Disp(); } Person::~Person(void) { delete[] name; delete bdate; } void main(void) { Person X("Ali Serçe", 1, 2, 1990); X.Disp(); } /*-------------------------*/ --------------------------------------------------------------------------------------------------------- Altın harflerle yaz mahlasımı. halvetim kasvet, kem gözlere şiş!... Cadü ya herru!...ya merru!...kafkef, gölge harâmilerine bir selam çak!... Abile ğatladı, demlenir simam, nüşinrevan'dan handan ummam ben. Ahu-yi felek mum, ben şamdan. düşmez kalkmaz bir Allah'tır uyan!... |
|
|
|
|
| Sayfayı E-Mail olarak gönder |
| Sponsorumuz |
![]() |
| Bookmarks | |||||||||||||
Facebook
|
Google
|
Yahoo
|
Live
|
Digg
|
Reddit
|
del.icio.us
|
StumbleUpon
|
Bluedot
|
Blinklist
|
Netvouz
|
Spurl
|
Mister-Wong
|
Furl
|
| Tags |
| c++[uyumluluk] |
| Konuyu Toplam 1 Üye okuyor. (0 Kayıtlı üye ve 1 Misafir) | |
| Seçenekler | |
| Stil | |
|
|
Benzer Konular
|
||||
| Konu | Konuyu Başlatan | Forum | Cevaplar | Son Mesaj |
| C++[uyumluluk] 1 | Baytar | Bilgisayar Programlama | 0 | 28.09.08 14:23 |