![]() |
||
|
|
#1 (permalink) |
|
2. Fonksiyon çağırma yoluyla ve gösterici kullanılarak:
Bu durumda fonksiyonun parametre değişkeni taban sınıf türünden bir göstericidir. Fonksiyon türemiş sınıf nesnesinin adresiyle çağırılır. Bu biçimde atama işlemi en sık rastlanan atama işlemidir. void fonk(A *p) { p -> DispA(); } void main(void) { B n(10, 20); fonk(&n); } 3. Açıkça ve referans kullanılarak yapılan atamalar: Taban sınıfa ilişkin bir referans türemiş bir sınıfa ilişkin bir nesneyle ilk değer verilerek tanımlanabilir. Derleyici bu durumda türemiş sınıf nesnesinin taban sınıf veri eleman bloğunun adresini taban sınıf referansına atar. { B n(10, 20); A &r = n; /*Geçerli*/ r.Disp(); /*Geçerli*/ } 4. Fonksiyon çağırma yoluyla ve referans kullanılarak: Bu durumda fonksiyonun parametre değişkeni taban sınıf türünden bir referanstır. Fonksiyon da türemiş sınıf nesnesinin kendisiyle çağırılır. void fonk(A &p) { p.DispA(); } void main(void) { B n(10, 20); fonk(n); } Taban Sınıf Göstericisine Türemiş Sınıf Nesnesin Adresinin Atanmasının Faydaları Bir dizi türetme söz konusu olduğunda türetme içerisindeki her sınıfın ortak veri elemanları söz konusu olabilir. Örneğin şöyle bir türetme söz konusu olsun: [IMG]file:///C:/DOCUME%7E1/naim/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image021.jpg[/IMG] (D B'den, E C'den, B ve C de A'dan türemiş sınıflar olsun). Örneğin burada türetilmiş sınıfların hepsi A sınıfın içermektedir. Yani bu sınıfların hepsi A sınıf gibi de davranabilmektedir. Burada A veri elemanları üzerine genel işlemler yapan bir fonksiyon söz konusu olsun: void fonk(A *p) { } Türemiş sınıfların herhangi birine ilişkin nesnenin adresiyle bu fonksiyon çağırılabilir. Yani böylece türden bağımsız olarak işlem yapabilen fonksiyonlar yazılabilmektedir. Taban Sınıf Göstericisine Türemiş Sınıf Nesnesinin Atanmasına İlişkin Birkaç Örnek 1. Bir işletmede çalışan kişiler gruplanarak bir sınıf ile temsil edilmiş olsun. [IMG]file:///C:/DOCUME%7E1/naim/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image023.jpg[/IMG] Çalışan hangi gruptan olursa olsun onun genel özellikleri üzerinde işlem yapan ProcessPersonalInfo() fonksiyonu olsun: ProcessPersonalInfo(Employee *p); Bu fonksiyona hangi sınıf türünden nesne verirsek verelim o sınıfın genel çalışan özellikleri üzerinde işlemler yapılabilir. { Manager x(....); Salesperson y(...); ProcessPersonalInfo(&x); ProcessPersonalInfo(&y); } 2. MFC sınıf sisteminde her türlü pencere üzerinde işlem yapabilen bir CWnd sınıf vardır. Editbox, puchbutton ve dialog pencereleri de bir çeşit penceredir. Bu özel pencereler üzerinde işlem yapabilen CWnd üzerinden türetilmiş ayrı sınıflar vardır. [IMG]file:///C:/DOCUME%7E1/naim/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image025.jpg[/IMG] Pencerenin türü ne olursa olsun, onun genel pencere özelliği üzerinde işlemler yapan global bir ProcessWnd() fonksiyonu yazıldığını düşünelim. Bu fonksiyon her türlü pencere üzerinde işlem yapabilecektir. void ProcessWnd(CWnd *p) { } { CButton button; CDialog dialog; ProcessWnd(&button); ProcessWnd(&dialog); } Sınıfın Static Veri Elemanları Veri elemanları sınıf içerisinde static anahtar sözcüğüyle bildirilebilir. Böyle bildirilmiş static veri elemanları sınıfın kendi veri elemanlarına dahil edilmez. /*-----static.cpp-----*/ #include <stdio.h> class X { static int x; int a; public: void fonk(void) { } }; void main(void) { X n; printf("%d\n", sizeof(X)); /*2*/ } /*--------------------*/ Sınıfın static veri elemanları aslında bir çeşit global değişkendir, yalnızca sınıf ile ilişkilendirilmiştir. Yani adeta yalnızca sınıfın erişebildiği global bir değişkendir. Bunlar C'ce normal global değişkenler gibidirler, yani static ömürlüdürler. Sınıfın static veri elemanlarından bir tane bulunur. Bu elemana bir sınıf elemanıymış gibi erişilir. /*-----static1.cpp-----*/ #include <stdio.h> class X { public: static int x; int a; void fonk(void) { } }; int X::x; void main(void) { X n; X z; n.x = 50; printf("%d\n", z.x); /*50*/ } /*---------------------*/ Sınıfın static veri elemanı ayrıca dışarıda global bir biçimde tanımlanmak zorundadır. Bu tanımlama veri elemanı sınıfın hani bölgesinde olursa olsun tanımlanmak zorundadır. Sınıfın static veri elemanı normal bir veri elemanıymış gibi erişim kuralına uyar. /*-----static2.cpp-----*/ #include <stdio.h> class X { private: static int x; int a; public: X(int r) { a = r; ++x; } int GetCount(void) { return x; } }; int X::x = 0; void main(void) { X a(10); X b(20); printf("%d\n", b.GetCount()); } /*---------------------*/ Sınıfın static veri elemanına hangi sınıf nesnesiyle erişildiğinin hiçbir önemi yoktur.Bu nedenle sınıfın static veri elemanına doğrudan sınıf nesnesi olmadan sınıf ismi ve çözünürlük operatörüyle de erişilebilir. Sınıf_ismi::static_veri_elemanı_ismi Tabii bu erişimin geçerli olabilmesi için veri elemanının public bölgede olması gerekir. Genellikle sınıfın static veri elemanı public bölgeye yerleştirilir ve dışarıdan bu biçimde erişilir. Static Veri Elemanları Neden Kullanılır Bazen bir sınıf global değişkene gereksinim duyabilir. Ama o global değişken yalnızca o sınıf için anlamlı olabilir. Eğer bu değişken sınıfın static veri elemanı yapılırsa yalnızca bir sınıfla ilişkilendirilmiş olur. Algılama iyileştirilir. Sınıfın static veri elemanı bir dizi biçiminde olabilir. Örneğin tarihlerin yazdırılması için kullanılacak, ayların isimlerini tutacak gösterici dizisi global yerine sınıfın static veri elemanı biçiminde alınabilir. Böylece hem her sınıf nesnesi içierisinde ayrıca yer kaplamaz, hem de sınıfa ilişkilendirilmiş olur. /*-----static4.cpp-----*/ class Date { private: static char *mon[12]; int day, month, year; public: /* .... .... .... */ void Disp(void); }; char * Date::mon[12] = {"Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"}; /*---------------------*/ Sınıfın Static Üye Fonksiyonları Sınıfın static üye fonksiyonları normal bir üye fonksiyonu gibidir. Ancak bu fonksiyonlara this göstericisi geçirilmez, yani bu fonksiyonlar içerisinden sınıfın veri elemanlarına erişilemez. Yani adeta sınıfla ilişkilendirilmiş global bir fonksiyon gibidirler. Yani sınıfın veri elemanlarını kullanmayan ancak mantıksal olarak sınıfa ilişkili olan global fonksiyonlar sınıfın static fonksiyonu yapılabilir. Sınıfın static üye fonksiyonuna this göstericisi geçirilmediğine göre bu fonksiyonun da özel olarak bir nesne ile çağırılmasının anlamı kalmaz. Sınıfın static üye fonksiyonu sınıf ismi belirtilerek çözünürlük operatörüyle doğrudan çağırılabilir. Tabii erişim kuralı bakımından public bölgede bildirilmiş olması gerekir. Sınıfın static üye fonksiyonu içerisinde sınıfın static olmayan bir üye fonksiyonu çağırılamaz, static olmayan bir veri elemanı kullanılamaz. Ama sınıfın static veri elemanları kullanılabilir ve static üye fonksiyonları çağırılabilir. Farklı sınıfların aynı isimli static üye fonksiyonları ya da veri elemanları bulunabilir. /*-----static5.cpp-----*/ #include <stdio.h> class X { private: int a; static int b; public: X(int n) { a = n; ++b; } void Disp(void); static void Fonk(void); }; int X::b = 0; void X: isp(void){ printf("%d\n", a); } void X::Fonk(void) { printf("%d\n" ,b); } void main(void) { X n1(50); X n2(60); X::Fonk(); } /*---------------------*/ Arkadaş Fonksiyonlar Global bir fonksiyonu bir sııfın arkadaş fonksiyonu yapabilmek için prototipinin önüne friend sözcüğü getirilmelidir. Arkadaş fonksiyonlar dışarıda normal global fonsiyonlardır. Ancak erişim bakımından ayrıcalıklı fonksiyonlardır. Arkadaş fonksiyonlar erişim bakımından ayrıcalıklıdır. Bir arkadaş fonksiyon içerisinde arkadaş olunan sınıfa ilişkin bir nesne tanımlanırsa o nesne yoluyla sınıfın her yerine erişilir. Bir fonksiyon birden fazla sınıfın arkadaş fonksiyonu olabilir. Arkadaş fonksiyon bildirimi sınıfın herhangibir bölümünde yapılabilir. Hangi bölümde yapıldığının hiçbir etkisi yoktur. Arkadaş fonksiyonlar sınıfın private ve protected bölümlerinin korunmasını zayıflatır. Yani sınıfın veri yapısı değiştirildiğinde bu fonksiyonların da yeniden yazılması gerekir. /*-----static6.cpp-----*/ #include <stdio.h> class X { int a; public: X(int n) { a = n; } void Disp(void) const; friend void fonk(void); }; void X: isp(void) const{ printf("%d\n", a); } void fonk(void) { X n(20); printf("%d\n", n.a); } void main(void) { fonk(); } /*-------------------------*/ Başka bir sınıfın bi üye fonksiyonu da bir sınıfın arkadaş fonksiyonu yapılabilir. friend Y::sample(void); Fonksiyonun parametre parantezi de fonksiyona dahildir. Yani genellikle arkadaş fonksiyonu parametresi arkadaş olunan sınıfına ilişkin bir gösterici ya da referans olur, fonksiyon içerisinde bu sınıfın her bölümüne erişilir. class X { private: int a; public: friend void fonk(const X *p); }; void fonk(const X *p) { printf("%d\n", p->a); } void main(void) { X n(20); fonk(&n); } Arkadaş Fonksiyonlar Ne Zaman Kullanılır? Bazı tasarımlarda az sayıda fonksiyon sınıfın private bölümü üzerinde yoğun işlemler yapıyor olabilir. Bu az sayıda fonksiyon için arabirim public üye fonksiyonlar yazmak etkin görünmeyebilir. İşte bu durumlarda arkadaş fonksiyonlarla erişim kuralı bozulabilir. Tabii arkadaş fonksiyonların aşırı ölçüde kullanılması korunmayı azaltarak private bölgeyi anlamasız hale getirebilir. Friend fonksiyon bildirimi global fonksiyonlar için prototip yerine de geçer. Arkadaş Sınıflar Bir sınıf bütün olarak arkadaş sınıf olarak bildirilebilir. class X { int a; public: X(int n) { a = n]; friend class Y; }; Bu durumda sınıfın bütün elemanları arkadaş kabul edilir.Yani o sınıfın tüm üye fonksiyonları içerisinde arkadaş olunan sınıfa ilişkin nesne tanımlanırsa o nesne yoluyla sınıfın her tarafına erişilebilir. class Node { int data; Node *next; friend class LList; }; class LList { Node *head; public: void Add(int d); void Delete(void); }; Değişkenler ve Sınıf Faaliyet Alanı C'de ve C++'ta aslında yalnızca nesnelerin değil her türden değişkenlerin faaliyet alanı vardır. Örneğin bir blok içerisinde bir typedef ismi bildirilirse o typdef ismi o bloğun dışından kullanılamaz. Bir sınıf içerisinde typedef, enum, struct, başka bir sınıf ya da union bildirimi yapılabilir. Bu bildirimere ilişkin isimlere ancak o sınıfların üye fonksiyonları içerisinden doğrudan erişilebilir(yani sınıf ismi belirtmeden). Ancak bu isimler sınıfn public bölümündeyse sınıf ismi ve çözünürlük operatörüyle erişim sağlanabilir. Örneğin: class X { public: typedef int *PINT; ... ... ... }; void main(void) { PINT n; /*Error*/ X::PINT n; /*Doğru kullanım*/ } Bir sınıfın içerisinde başka bir sınıf ya da yapı bildirimi de yapılabilir. Bu durumda içerde bildirilmiş olan sınıf yalnızca dışarıda bildirilmiş sınıf içerisinden doğrudan kulanılabilir. Örneğin: class X { public: class Y { ... ... }; ... ... }; void main(void) { Y n; /*Error*/ X::Y n; /*Doğru kullanım*/ } Bir sınıf bir sınıfı içeriyorsa aralarında hiçbir veri elemanı içerme gibi bir durum yoktur(yani C'deki iç içe yapılarda olduğu gibi değildir). Aslında bu iki sınıf tamamen birbirlerinden farklı bağımsız olarak dışarıda bildirilebilecek iki sınıftır. İçeride bildirilmiş sınıf yalnızca dışarıdaki sınıf içerisinde isim bakımından doğrudan kullanılabilir. Sanal Fonksiyonlar Taban sınıf ve türemiş sınıflarda aynı isimli fonksiyonlar varsa, çağırma çözünürlük operatörüyle yapılmamışsa, eğer taban sınıf nesnesi ya da göstericisine ilişkin bir çağırma söz konusuysa taban sınıfın fonksiyonu doğal olarak çağırılacaktır. Sınıfın bir üye fonksiyonu sanal fonksiyon yapılabilir. Bir üye fonksiyonu sanal fonksiyonu yapabilmek için fonksiyon prototipinin önüne virtual anahtar sözcüğü getirilir. virtual anahtar sözcüğü sadece prototip bildirirken kullanılır, fonksiyon tanımlanırken kullanılmaz. Bir üye fonksiyon sanal yapılırsa o sınıfın türemiş sınıflarında bulunan aynı isimli , aynı prototipe sahip tüm fonksiyonlar da sanal olur. Yani virtual anahtar sözcüğü yazılmasa da yazılmış gibi işlem görür. Aynı prototipe sahip olması demek geri dönüş değerlerinin parametre yapılarının ve fonksiyon isimlerinin aynı olması demektir(const'luk dahil). Türemiş sınıf nesnesinin adresi taban sınıf göstericisine atanır ve bu gösterici yoluyla sanal fonksiyon çağırılırsa adresi alınan nesne hangi sınıfa aitse o sınıfın sanal fonksiyonu çağırılır. Taban sınıfın türemiş sınıfa erişmesi ancak bu koşullarda mümkün olmaktadır. Bir dizi türetme yapıldığında türemiş sınıflardan birine ilişkin nesnenin adresi taban sınıflardan birine ilişkin göstericiye geçirilebilir ve bu gösterici yoluyla sanal fonksiyon çağırılabilir. Sınıf ismi belirtilerek sanal fonksiyon çağırılırsa sanallık özelliği kalmaz. /*-----virtual.cpp-----*/ Sanal Fonksiyonların Program İçerisindeki Çağırılma Biçimleri Program içerisinde sanal fonksiyon şu biçimlerde çağırılabilir: 1. Türemiş sınıf nesnesinin adresinin açık bir biçimde taban sınıf nesnesine atanması yoluyla void main(void) { B z(10, 20); A *p; p = &z; p -> Disp(); } 2. Fonksiyonun parametre değişkeni taban sınıf türünden bir göstericidir. Fonksiyon da türemiş sınıf nesnesinin adresiyle çağırılır. Bu gösterici yoluyla sanal fonksiyon çağırılabilir. void fonk(A *p) { p -> Disp(); } void main(void) { B n(10, 20); fonk(&n); } 3. Taban sınıf türünden bir referans türemiş sınıf türünden nesneyle ilk değer verilerek tanımlanır. Bu referans yoluyla sanal fonksiyon çağırılabilir. Bu durumda türemiş sınıfa ilişkin sanal fonksiyon çağırılacaktır. void main(void) { B n(10, 20); A &r = n; r.Disp(); } 4. Fonksiyonun parametre değişkeni taban sınıf türünden bir referans olur. Fonksiyon da türemiş sınıfın nesnesinin kendisiyle çağırılır. Fonksiyon içerisinde bu referans yoluyla türemiş sınıfa ilişkin sanal fonksiyon çağırılır. void fonk(A &r) { r.Disp(); } void main(void) { B n(10, 20); fonk(n); } 5. Türemiş sınıf türünden bir nesne ile taban sınıfa ilişkin bir üye fonksiyonu çağırılırsa, bu üye fonksiyon içerisinde de sanal fonksiyon çağırılırsa üye fonksiyon hangi sınıfa ilişkin nesne ile çağırılmışsa o sınıfa ilişkin sanal fonksiyon çağırılır. void A::fonk(void) { Disp(); } void main(void) { B n(10, 20); n.fonk(); } Tabii üye fonksiyon içerisinde çağırma işlemi çözünürlük operatörü ve sınıf ismi beliritilerek yapılırsa sanallık özelliği kalmaz. Taban sınıf sanal fonksiyona sahip olduğu halde türemiş sınıf sanal fonksiyona sahip olmayabilir. Yani türemiş sınıf için sanal fonksiyonun tanımlanması zorunlu değildir. Sanal fonksiyona sahip olmayan türemiş sınıfa ilişkin bir sınıf nesnesinin adresi taban sınıf göstericisine atanır ve bu gösterici yoluyla sanal fonksiyon çağırılırsa türemiş sınıfın sanal fonksiyona sahip ilk taban sınıfının sanal fonksiyonu çağırılır. Sanal fonksiyon çağırılabilmesi için türetme biçiminin public olması gerekir. Bir sanal fonksiyon çağırıldığında gerçekte çağırılacak olan türemiş sınıfın sanal fonksiyonu sınıfın herhangi bir bölümünde olabilir. Ancak çağırılma ifadesindeki nesneye ilişkin sınıfın sanal fonksiyonu public bölümde olmak zorundadır. Örneğin : A *p; C n; p = &n; p -> fonk(); Burada fonk sanal bir fonksiyon olsun, gerçekte çağırılacak olan fonk C sınıfının fonksiyonudur. C sınıfının fonk sanal fonksiyonu sınıfın herhangi bir bölümünde bildirilmiş olabilir, ancak çağırmanın mümkün olabilmesi için A sınıfının fonk sanal fonksiyonunun public bölümde bildirilmiş olması gerekir. Sanal Fonksiyon Çağırmanın Nedenleri Sanal fonksiyon çağırmanın iki faydalı nedeni vardır: 1. Bir sınıfın işlevini değiştirmek, 2. Türden bağımsız işlemler yapılmasına olanak sağlamak. Örneğin A gibi bir sınıf varsa, bu sınıf belirli işlemleri yapıyorsa, bu sınıfa hiç dokunmadan sınıfın yaptığı işlemler üzerinde değişiklik yapılması sağlanabilir. Sanal Fonksiyon Kullanılmasına İlişkin Örnekler 1. İngilizce yazılar üzerinde işlem yapan bir CString sınıfı olsun. Bu sınıfın yazıları karşılaştıran, büyük harf ya da küçük harfe dönüştüren üye fonksiyonları olsun. Yazıların karşılaştırılması ve harf dönüşümünün yapılması dile bağlı bir durumdur. CString sınıfının Cmp isimli karşılaştırma fonksiyonu işlemini yaparken iki karakteri karşılaştıran sanal Cmphr fonksionunu çağırıyor olsun. CString sınıfından bir sınıf türetilir, CmpChr sanal fonksiyonu bu sınıf için yeniden yazılırsa, artık Cmp fonksiyonu türemiş fonksiyonunun CmpChr fonksiyonunu çağıracak ve işlemler başka bir dile göre düzgün yapılabilecektir. 2. Bir dizinin Array isimli bir sınıfla temsil edildiğini düşünelim. Bu sınıfın sıraya dizme işlemini yapan sanal bir sort fonksiyonu olsun. Bazı üye fonksiyonlar da bu fonksiyonu çağrarak sort işlemini gerçekleştirsin. Sort algoritması çok çeşitli olabilir. Bu sınıftan bir sınıf türetilerek bu sanal fonksiyon başka bir sort algoritmasını kullancak biçimde yeniden yazılabilir. Bu durumda bizim istediğimiz sort algoritmasıyla işlemler yürütülecektir. 3. MFC sınıf sisteminde her türlü pencere işlemleri CWnd sınıfı tarafından yapılmaktadır. Dialog penceresi de özel bir tür penceredir. Dialog penceresi işlemleri CWnd sınıfından türetilen CDialog sınıfı ile yapılmaktadır. Her dialog penceresi diğerinden farklı özelliklere sahip olabilir. O işlemler de CDailog sınıfından türetilen sınıfla temsil edilir. [IMG]file:///C:/DOCUME%7E1/naim/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image027.jpg[/IMG] Dialog penceresini görünür hale getirmek için CDialog sınıfının DoModal() fonksiyonu çağırılır. CDialog sınıfının OnOk ve OnCancel sanal fonksiyonları vardır. CWnd sınıfından türetilen bir sınıfa ilişkin bir nesne tanımlandığında CWnd sınıfının başlangıç fonksiyonu ile yaratılan nesnenin adresi MFC sistemi tarafından global bir biçimde saklanır. Ne zaman bir dialog penceresinde OK ya da CANCEL tuşlarına basılırsa MFC saklamış olduğu adresle OnOk ya da OnCancel fonksiyonlarını çağırır. Eğer biz bu fonksiyonları yeniden yazarsak bizimki çağırılacaktır. Tabii orijinal OnOk ve OnCancel fonksiyonları kritik bazı işlemleri de yapmaktadır. Bu durumda bu fonksiyonların doğrıdan çağırmaları da gerekebilir. void MyDialog: nOK(void){ .... .... CDialog::OnOK(); } Sanal Fonksiyonların Mekanizmasının Kurulması Bir türemiş sınıf nesnesinin adresi taban sınıf göstericileriyel dolaştırılmış olabilir ve en sonunda sanal fonksiyon çağırılsa bile nesnenin orijinaline ilişkin sanal fonksiyon çağırılacaktır. peki derleyici bu olayı derleme zamanı sırasında belirleyebilir mi? Bu olayın derleme sırasında tespit edilmesi mümkün değildir. Gerçekte hangi sanal fonksiyonun çağırlıacağını belirlemek ancak runtime sırasında kurulacak bir mekanizmayla mümkün olabilir. Bu mekanizmanın runtime sırasında kurulmasına ingilizce late binding denilmektedir. class A { public: virtual void fonk1(void); virtual void fonk2(void); }; class B : public A { public: virtual void fonk1(void); virtual void fonk2(void); }; class C : public B { virtual void fonk1(void); virtual void fonk2(void); }; void sample(B *p) { sample2(p); } void sample2(A *p) { p -> fonk1(); } void main(void) { C x; B *t; t = &x; sample(t);/*C'nin sanal fonksiyonu çağırılacak*/ } A Sınıfının Sanal Fonksiyon Tablosu Sıra No Adres 1 &A::fonk1() 2 &A::fonk2() B Sınıfının Sanal Fonksiyon Tablosu Sıra No Adres 1 &B::fonk1() 2 &B::fonk2() C Sınıfının Sanal Fonksiyon Tablosu Sıra No Adres 1 &C::fonk1() 2 &C::fonk2() Derleyici bu mekanizmayı kurabilmek için her sınıfa ilişkin bir sanal fonksiyon tablosu yaratır. Bu sanal fonksiyon tablolarında ilgili sınıfın sanal fonksiyonlarının adresleri bulunur. Sanal fonksiyona sahip bir sınıfa ilişkin bir nesne tanımlandığında o nesne için bir gizli gösterici kadar daha fazla yer ayrılır. Bu gizli göstericide nesne hangi sınıfa ilişkinse o sınıfa ilişkin sanal fonksiyona ilişkin tablonun adresi tutulur. Bu gizli göstericinin nesnenin neresinde tutulduğu standart olarak belirlenmemiştir. Ancak nesnenin en düşük anlamlı adreslerinde genel olarak tutulmaktadır. Bu durumda bir sanal fonksiyon çağırıldığında aşağı seviyeli şu işlemler yapılır: 1. Sanal fonksiyon tablo göstericisi alınır ve tablonun yeri bulunur. 2. Sanal fonksiyon tablosunda ilgili sanal fonksiyonun adresi bulunur. 3. Sanal fonksiyon çağırılır. Sanal fonksiyon çağırmanın yer ve zaman maliyeti söz konusudur. Çünkü sanal fonksiyon tabloları ve nesne içerisindeki sanal fonksiyon tablo göstericisi ek yer kaplamaktadır. Ayrıca tablolara bakarak sanal fonksiyon çağırıldığı için ek makina komutlarına gereksinim duyulur. Bu da zaman kaybına sebep olur. Operatör Fonksiyoları C'de iki yapı değişkeni birbirleriyle aritmetik işlemlere ve karılaştırma işlemlerine sokulamaz. Ancak aynı türden iki yapı değişkeni birbirlerine atanabilir. C'de olmayan bir veri türü üzerinde işlem yapabilmek için o veri türü önce bir yapı ile temsil edilir. Sonra işlemler yapan arabirim fonksiyonlar yazılır. typedef struct _COMPLEX { double real, image; } COMLEX; void AddComp(COMPLEX *p1, COMPLEX *p2, COMPLEX *result); COMPLEX a = {3, 4}, b = {8, 2}, c; AddComp(&a, &b, &c); C++'ta operatör fonksiyonları ya bir sınıfın üye fonksiyonu biçiminde yapılır, ya da global fonksiyon biçiminde olur. Üye Fonksiyon Biçiminde Tanımlanan Operatör Fonksiyonları Genel Biçimi: [geri dönüş değeri] operator <operatör sembolü> ([parametreler]); Operatör fonksiyonları aslında normal fonksiyonlardır. Yalnızca fonksiyon ismi olarak operator anahtar sözcüğü ile operatör sembolü gelir. /*-----operfonk.cpp-----*/ #include <stdio.h> class A { int a; public: A(int x) { a = x; } int operator +(int x); }; int A: perator +(int x){ return a + x; } void main(void) { A n(5); int z; z = n.operator +(2); printf("%d\n", z); } /*------------------------*/ Operatör sembolüne ilişkin yazılan operatör fonksiyonu eğer binary bir operatöre ilişkinse bir parametreye sahip olması, unary bir operatöre ilişkinse parametreye sahip olmaması gerekir. Bir operatör fonksiyonunun kısa biçimde çağırılması da söz konusudur. "a.operator <sembol>(b)" ile "a <sembol> b" eşdeğerdir. Zaten operatör fonksiyonlarının kullanılma amacı onları kısa biçimde çağırarak sanki normal bir operatör işlemi yapılıyormuş gibi okunabilirliği arttırmaktır. C++ Derleciyilerinin İfadeleri Yorumlama Biçimi C++ derleyicileri bir operatörle karşılaştığında önce operandların türlerini araştırır. Eğer operandlar C'nin normal türlerine ilişkinse işlemi normal olarak gerçekleştirir. Eğer operandlardan biri bir sınıfa ilişkinse uygun bir operatör fonksiyonu araştırır. Öyle bir operatör fonksiyonu bulursa çağırır. Bulamazsa işlem error ile sonuçlandırılır. Farklı parametre yapılarına sahip aynı sembole ilişkin operatör fonksiyonları bulunabilir. Sınıfın Dosya Organizasyonu Normal olarak bir sınıf iki dosya halinde yazılır. Header dosyası içerisine sınıf bildirimi sembolik sabitler ve çeşitli bildirimler yerleştirilir. Bu başlık dosyasına tipik olarak #ifndef _İSİM_H_ #define _İSİM_H_ #endif biçiminde çeşitli bildirimler yerleştirilir. Bunun dışında inline fonksiyonlar da buraya yerleştirilmelidir. CPP dosyasının içerisine sınıfın bütün üye fonksiyonlarının tanımlamaları yazılır. Bu dosya içerisinden header file include edilir. CPP derlenerek library içerisine yerleştirilebilir. Ancak bu sınıf başka yerlerden kullanılacaksa header dosyasının kullanan kod içerisinde include edilmesi gerekir. Bir sınıfın üye fonksiyonlarının hepsi library içerisine yerleştirilebilir. Ancak derleme aşamasında header dosyasının derleyicinin bilgilendirilmesi için bulundurulması gerekir. Karşılaştırma Operatörlerine İlişkin Operatör Fonksiyonlarının Yazılması Karşılaştırma operatörlerine ilişkin operatör fonksionlarının parametreleri ya da geri dönüş değerleri herhangibir biçimde olabilir. Ancak geri dönüş değerlerinin int türünden olması ve koşul sağlanıyorsa 1 değerine, sağlanmıyorsa 0 değerine geri dönmesi en uygun olab durumdur. /*-----date.h-----*/ /*-----date.cpp-----*/ CString Sınıfında Karşılaştırma Operatör Fonksiyonlarının Kullanılması Yazı işlemlerini yapan CString sınıfı yazıların karşılaştırılması için bir grup karşılaştırma operatör fonksiyonuna sahip olabilir. /*-----cstring.h-----*/ /*-----cstring.cpp-----*/ +, -, *, / Operatörlerine İlişkin Operatör Fonksiyonlarını Bu operatör fonksiyonlarının parametreleri ve geri dönüş değerleri herhangi bir biçimde olabilir. Ancak en çok karşılaşılan durum fonksiyonun bir sınıfa geri dönmesi durumudur. Bir fonksiyonun bir sınıf nesnesiyle geri dönmesi C'de fonksiyonun bir yapıya geri dönmesi anlamına gelir. C'de fonksiyonlar yapıların kendisine geri dönebilirler. Bu durumda bu fonksiyonların geri dönüş değerlerinin aynı türden bir yapı değişkenine atanması gerekir. C'de bu durum çok rastlanan ve tavisye edilen bir durum değildir. Ancak C++'ta bu durum yani bir fonksiyonun bir sınıfın kendisine geri dönmesi durumu çok rastlanan bir durumdur. Böyle bir fonksiyonun geri dönüş değeri aynı türden bir sınıf nesnesine atanmalıdır. Çünkü C++'ta aynı türden iki sınıf nesnesi birbirine atanabilir. /*-----complex.cpp-----*/ ++ ve -- Operatörlerine İlişkin Operatör Fonksiyonları Bu operatörler tek operandlı oldukları için bu operatörlere ilişkin operatör fonksiyonlarının da parametresiz yazılması gerekir. Geri dönüş değerleri herhangi bir türden olabilse de en çok rastlanan durum fonksiyonun *this ile geri dönmesi ve geri dönüş değerinin de kendi sınıf türünden bir referans olmasıdır. Bu operatörlerin prefix version'ları parametre parantezi içerisi void yazılarak veya boş bırakılarak yazılır. Postfix verison'u için parametre parantezinin içerisine int anahtar sözcüğü yazılarak bırakılır. Buradaki int anahtar sözcüğü parametre anlamına gelmez. Zaten yanına değişken ismi de yazılmaz. Operatörün postfix kullanılacağına ilişkin bir gösterim biçimidir. ++ ve -- Operatörlerinin Tarih Sınıfında Kullanılması Bu operatör fonksiyonlarının yazımı için 1-1-1900'den geçen gün sayısını tarih bilgisine çeviren bir fonksiyona gereksinim vardır. ++ ve -- operatör fonksiyonlarını yazmak için RevDays() fonksiyonundan faydalanabiliriz. Eğer bu operator fonksiyonlarının yalnızca prefix version'ları yazılırsa postfix için de kullanılabilir. Yani postfix kullanımda aynı operatör fonksiyonu çağırılacaktır. Postfix verison'ları yazılsa bile arttırma işleminin ifadenin sonunda yapılmasını programcı sağlamak zorundadır. /*-----date.h-----*/ /*Ekleme Revdays(), ++, -- */ /*-----date.cpp-----*/ new ve delete Operatör Fonksiyonlarının Yazımı new ve delete operatör fonksiyonları bir sınıfın üye fonksiyonları biçiminde yazılabilir. Bu durumda bir sınıf türünden bir tahsisat yapıldığında ya da tahsis edilmiş bir alan serbest bırakıldığında bu sınıfın üye fonksiyonlarıyla ilgili işlemler yürütülür. new operatör fonksiyonu aşağıdaki gibi tanımlanmak zorundadır. void * operator new(size_t size); size_t yerine unsigned int de yazılabilir ama bu tür aslında derleyicileri yazanlar tarafından herhangi bir tür olarak tanımlanabilecek bir tür belirtmektedir. Hemen hemen bütün derleyicilerde size_t unsigned int anlamındadır. Benzer biçimde delete operatör fonksiyonu da şu şekilde yazılmak zorundadır. void operator delete(void *ptr); /*-----opernew.cpp-----*/ Bir sınıfın new ve delete operatör fonksiyonları yazılmışsa bu sınıf türünden yapılan new ve delete işlemlerinde bu operatör fonksiyonları kullanılır. Ama yine normal olarak başlangıç ve bitiş fonksiyonları çağırılacaktır. Benzer biçimde sınıfa ilişkin birden fazla sınıf nesnesi için tahsisat yapabilmek için köşeli parantezli operatör fonksiyonları da yazılabilir. Köşeli parantezli new ve delete operatör fonksiyonları şöyle bildirilmek zorundadır. void * operator new [](unsigned size); void operator delete [](void *ptr); new X[10];/*default constructor çağırılır*/ Global Operatör Fonksiyonları Normal olarak x bir sınıf nesnesi n de C'nin normal türlerine ilişkin bir nesne olmak üzere x + n gibi bir işlem sınıfın uygun bir + operatör fonksiyonuyla gerçekleştirilebilir. Oysa n + x gibi bir işlem + operatörünün değişme özelliği olmasına rağmen gerçekleştirilemez. Çünkü bu işlem n.operator(x) anlamına gelir, bu da mümkün değildir. Global operatör fonksiyonları bu durumu mümkün hale getirmek için tasarlanmıştır ve üye operatör fonksiyonlarını işlevsel bakımdan kapsar. Global operatör fonksiyonu normal bir global fonksiyon gibi yazılır. Ama operatör sembolü binary bir operatçre ilişkinse iki parametre, unary bir operatöre ilişkinse tek parametre almak zorundadır. Global Operatör Fonksiyonlarının Date Sınıfıyla İlişkin Uygulamaları /*-----date.h-----*//*ekleme yapıldı*/ /*-----date.cpp-----*/ Derleyici bir kullanım biçimine uyguna hem üye operatör fonksiyonlarını hem de global operatör fonksiyonlarını araştırır. Eğer her ikisi de varsa bu durum bir iki anlamlılık hatası oluşturur. Üye operatör fonksiyonu olarak yazılabilen tüm operatör fonksiyonları global operatör fonksiyonu olarak da yazılabilir. Genellikle global operatör fonksiyonları kolay erişim bakımından arkadaş operatör fonksiyon biçiminde yazılır. /*-----cstring.h-----*//*ekleme yapıldı*/ /*-----cstring.cpp-----*/ Global new ve delete Operatör Fonksiyonları Aslında new ve delete işlemi yapıldığında çağırılacak global operatör fonksiyonu vardır ve bu fonksiyon derleyiciyi yazanlar tarafından kütüphane içerisine yerleştirilmiştir. Eğer programcı bu operatör fonksiyonlarını yeniden yazarsa kütüphanedeki değil programcının yazdığı çağırılacaktır. new ve delete operatör fonksiyonları aşağıdaki prototipe uygun yazılmak zorundadır. void * operator new(unsigned size); void * operator new [](unsigned size); void operator delete(void *ptr); void operator delete [](void *ptr); /*-----glop_new.cpp-----*/ Derleyici global new operatör fonksiyonuna parametreyi new operatörünün kullanımındaki tahsisat miktarı olarak geçirir. Yani örneğin: p = new int; ile p = operator new(sizeof(int)); arasında fark yoktur. new operatörü kullanıldığında aslında derleyici ifadeyi global new operatörü fonksiyonunu çağırma ifadesine dönüştürür. Aslında new bir fonksiyon çağırma işlemi anlamına gelir. Kütüphane içerisinde new ve delete operatör fonksiyonlarının köşeli parantezli fonksiyonları da vardır. Ancak bunlar global köşeli parantezsiz new ve delete operatör fonksiyonlarını çağırırlar. void * operator [] (unsigned size) { .... p = operator new(sizeof(size)); .... } Biz köşeli parantezli new ve delete operatör fonsksiyonlarını yazmasak bile yazdığımız new ve delete operatör fonksiyonları yine çağırılacaktır. new int[n]; işlemi ile operator new [](sizeof(int) * n); aynıdır. Eski derleyicilerde(borland v3.1 gibi) new ve delete operatörlerinin köşeli parantezli versionlarına ilişkin operatör fonksiyonlarını yazmak geçerli değildir. Başlangıç Fonksiyonu İle Nesne Yaratılması C++'ta X bir sınıf ismi olmak üzere başlangıç fonksiyonu çağırıyormuş gibi X(...) ifadesi ile derleyici X sınıfı türünden geçici bir nesne yaratır. Bu nesne için uygun constructor fonksiyonunu çağırır. İfade bitince bu nesneyi serbest bırakarak bitiş fonksiyonunu çağırır. Örneğin: a = X( ) + n; işleminde önce X sınıfı türünden geçisi bir nesne tanımlanır. Sonra bu nesne + operatör fonksiyonuyla işlem görür ve a nesnesine atanır. Yaratılmış olan geçisi nesneye ilişkin bitiş fonksiyonu ifadenin bitmesiyle çağırılacaktır. Örneğin: Create(...., CRect(10, 10, 20, 20), ....);/*Buradaki işlem Create fonksiyonunun çağırılmasıdır*/ Burada önce CRect türünden geçici bir sınıf nesnesi yaratılır, Create fonksiyonuna parametre olarak geçirilir, CReate fonksiyonu bitince yaratılan geçici nesne için destructor çağırılır. Tür Dönüştürme Operatör Fonksiyonu Bu fonksiyon yalnızca sınıfın üye fonksiyonu biçiminde yazılabilir. Global olarak yazılamaz. Genel biçimi: operator <tür> (void); Tür dönüştürme operatör fonksiyonlarının geri dönüş gibi bir kavramı yoktur(tıpkı başlangıç ve bitiş fonksiyonları gibi). Parametresi void olmak zorundadır. C'de ve C++'ta tür dönüştürme işlemi 3 biçimde yapılır: 1. Atama işlemi ile(sağ taraf değeri sol taraf değerinin türüne dönüştürülerek atama işlemi gerçekleştirilir), 2. Tür dönüştürme operatörüyle, 3. İşlem öncesi otomatik tür dönüştürmeleri biçiminde. Bir sınıf başka bir sınıfa ya da C'nin normal türlerine dönüştürüleceği zaman sınıfın ilgili türe ilişkin tür dönüştürme operatörü varsa çağırılarak işlemler gerçekleştirilir. /*-----complex.cpp-----*/ /*Ekleme yapıldı*/ Tür dönüştürme operatör fonksiyonlarının geri dönüş değerleri synatx içerisinde belirtilmemiş olsa da aslında geri dönüş değerleri vardır ve bu tür operatör fonksiyonunun ilişkin olduğu türdür. Bir sınıfın ifade içerisinde başka bir türe dönüştürülmesi gerektiğinde derleyici önce ilgili türe dönüştürme operatör fonksiyonunun olup olmadığını kontrol eder. Varsa işlemini bu operatör fonksiyonunu çağırarak yapar. Tür dönüştürme operatör fonksiyonu ile sınıf herhangi bir türden herhangi bir adrese de dönüştürülebilir. /*-----cstring.h-----*/ /*Ekleme yapıldı*/ /*-----cstring.cpp-----*/ Tür Dönüştürme Operatör Fonksiyonlarında İki Anlamlılık Durumu C'nin normal türlerinde dönüşüm yapan tek bir dönüştürme yapan fonksiyonu varsa Bu fonksiyon kullanılarak her türe dönüşüm yapılması sağlanabilir. Örneğin Complex sınıfının yalnızca double türüne dönüşüm yapan bir operatör fonksiyonu varsa aşağıdaki kod hataya yol açmaz: Complex b; int a; a = b; Bu örnekte eğer Complex sınıfının hem double hem de int türüne dönüşüm uygulayan operatör fonksiyonları olsaydı int olan seçilecekti. Eğer bu sınıfın hem double hem de long türüne dönüşüm yapan operatör fonksiyonları olsaydı bu işlem iki anlamlılık hatasına yol açardı. Bir sınıfın aynı işlemi yapabilecek hem normal bir operatör fonksiyonu hem de tür dönüştürme operatör fonksiyonu bir arada bulunabilir. Bu durumda normal operatör fonksiyonu öncelikli olarak ele alınır. Ancak bu tür durumlarda okunabilirlik gereği tür dönüştürme operatöründen faydalanılarak işlem gerekirse açıkça belirtilebilir. class X { public: int operator +(int x); operator int(void); }; void main(void) { X a; b = a + 10; /* + operatör fonksiyonu ile işlem yapılır*/ b = (int) a + 10; /* tür dönüştürme operatörüyle yapılır* } Başlangıç Fonksiyonu Yoluyla Dönüştürme Yapılması C'nin normal türlerinden sınıf türlerine dönüşüm yapılabilir. Örneğin: class X { private: int x; public: X (int n); X operator +(X &r); } void main(void) { X n; n + 10; /* eş değeri n + (X ) 10; veya n + X(10); */ } Burada önce int türü X sınıfı türüne dönüştürülür, daha sonra n ile toplama işlemi yapılır. Bu dönüştürme işleminde sınıfın başlangıç fonksiyonu kullanılır. Yani bu başlangıç fonsiyonu ile geçici bir nesne yaratılacak, işlem bittikten sonra sınıfa ilişkin bitiş fonksiyonu çağırılacaktır. Aslında (X) 10; ile X(10); ifadeleri arasında fark yoktur. Tabii böyle bir dönüşümün mümkün olabilmesi için sınıfın tek parametreli başlangıç fonksiyonu bulunmak zorundadır. Özetle bir işlemin bir operandı bir sınıf nesnesiyse bu işlem derleyici tarafından 3 biçimde yapılabilir: 1. Sınıfın açık bir biçimde tanımlanmış normal ve uygun bir operatör fonksiyonu ile, 2. Tür dönüştürme operatörü fonksiyonu ile(yani sınıfın C'nin normal türlerine dönüştürülmesiyle) 3. C'nin normal türünün başlangıç fonksiyonu yoluyla geçici bir sınıf nesnesine dönüştürülmesiyle. Bu yöntemlerin üçünün de aynı zamanda mümkün olması halinde yapılacak en iyi şey açık bir syntax ile hangi yöntemin tercih edildiğinin belirtilmesidir. X a; int n; 1. a.operator + (n); 2. (int ) a + n; 3. a + (X ) n; Eğer özellikle böyle bir belirtme yapımamışsa yukarıdaki öncelik sıraları dikkate alınır. X a = X(n); işlemi C++'ta geçerli bir işlemdir. Ancak burada iki kez nesne yaratılmaz. Yani geçici nesne kesinlikle yaratılmayacaktır. Bu işlem tamamen X a(n); işlemiyle eş değerdir. Tek Parametreli Başlangıç Fonksiyonlarına Sahip Sınıflara İlişkin Nesnelerin İlk Değer Verilme Syntax'i İle Tanımlanması X a = b; gibi bir ilk değer verme işlemi tamamen X a(b); ile eş değerdir. Bu eş değerlik şuradadan gelmektedir: 1. X a = b; 2. X a = X(b); 3. X a(b); Örneğin: CString x = "Ali"; ile CString x("Ali"); eşdeğerdir. [ ] Operatörüne İlişkin Operatör Fonksiyonunun Yazımı Bu operatör fonksiyonunun geri dönüş değeri herhangi bir biçimde olabilir, ancak geri dönüş değerinin referans olması en uygun durumdur. Fonksiyonun parametresi tamsayı türlerine ilişkin olmak zorundadır ve köşeli parantez içindeki sayıyı belirtir. a bir sınıf nesnesi olmak üzere; a[n] ile a.operator[](n) aynı anlama gelir. Örnek: Sınıf kontrolünün yapıldığı diziyi temsil eden örnek bir sınıf. /*-----kosedizi.cpp-----*/ Atama Operatör Fonksiyonları ve Kopya Başlangıç Fonksiyonu Aynı türden iki sınıf nesnesi birbirlerine atanabilir. Böyle bir atama işleminde C'deki yapılarda olduğu gibi karşılıklı veri elemanları birbirlerine kopyalanır. Ancak bazı sınıflarda ve özellikle gösterici veri elemanına sahip sınıflarda karşılıklı veri elemanlarının atanması istenen bir durum değildir. Böyle sınıflarda iki sınıf nesnesi birbirine atandığında gösterici içerisindeki adresler birbirine atanır. Yani gösterici veri elemanları aynı yeri gösteriri hale gelirler. Dar faaliyet alanına ilişkin nesne iin bitiş fonksiyonu çağırıldığında geniş faaliyet alanına sahip nesnenin gösterdiği alan da silinecektir. { CString x("Ali"); { CString y("Veli"); y = x; }/*Bu aşamada x'in gösterdiği bilgi silindiğinden gösterici hatası ortaya çıkacaktır. */ } Oysa böyle bir atama işleminde yapılacak en iyi işlem adreslerin değil adreslerin içeriğinin kopyalanması olacaktır. İşte bu tür uygulamalarda böyle atama işlemlerinin probleme yol açmaması için atama operatör fonksiyonu yazılmalıdır. Atama operatör fonksiyonu yalnızca sınıfın üye fonksiyonu olarak yazılabilir. Atama operatör fonksiyonunun parametresi herhangi bir türden olabilir. Ancak geri dönüş değerinin aynı sınıf türünden bir referans olması en uygun durumdur. tasarımı zorlaştırmamamk amacıyla void biçimde de alınabilir. /*-----cstring.h-----*//*Ekleme yapıldı*/ /*-----cstring.cpp-----*/ Her sınıf için ayrıca atama operatör fonksiyonu yazılmasına gerek yoktur. Kopya Başlangıç Fonksiyonu(copy constructor) Bir sınıfın kendi sınıfı türünden bir referans parametresine sahip başlangıç fonksiyonuna kopya başlangıç fonksiyonu denir. Kopya başlangıç fonksiyonunun parametresi const bir referans da olabilir. Bu durumda C++'ta 3 tür başlangıç fonksiyonu vardır: 1. Default başlangıç fonksiyonu (parametresi void) 2. Kopya başlangıç fonksiyonu (parametresi kendi sınıfı türünden referans) 3. Sıradan başlangıç fonksiyonu (parametresi herhangi bir tür) Kopya başlangıç fonksiyonu derleyici tarafından 3 durumda çağırılır: 1. Bir sınıf nesnesinin kendi türünden bir sınıf nesnesiyle ilk değer verilerek tanımlandığı durumlarda. Örneğin: X n = a;/*a X türünden bir sınıf nesnesi*/ 2. Bir fonksiyonun parametresi bir sınıf türünden nesnenin kendisiyse fonksiyon da aynı sınıf türünden başka bir sınıf nesnesinin kendisiyle çağırılmışsa parametre değişkeni kopya başlangıç fonksiyonu çağırılarak oluşturulur. 3. Fonksiyonun geri dönüş değeri bir sınıf türündense return ifadesiyle geçici bölgede nesne yaratılırken kopya başlangıç fonksiyonu çağırılır. Bir sınfın atama fonksiyonu ve kopya başlangıç fonksiyonu yazılmak zorunda değildir. Eğer yazılmamışsa karşılıklı veri elemanları birbirine atanır. Kopya başlangıç fonksiyonunun yazılmasının gerekçesi atama operatör fonksiyonun yazılması gerekçesiyle aynıdır. Yani bir sınıf için atama fonksiyonunun yazılması gerekiyorsa mutlaka kopya başlangıç fonksiyonunun da yazılması gerekir. Sanal Bitiş Fonksiyonları(virtual destructor) Bir sınıfın bitiş fonksiyonu sanal olabilir. Aslında ne zaman bir türetme yapılacaksa taban sınıfın bitiş fonksiyonu sanal yapılmalıdır. Taban sınıfın bitiş fonksiyonu sanal yapılırsa o sınıftan türetilen tüm sınıfların bitiş fonksiyonları otomatik olarak sanal kabul edilir. Normal olarak delete operatörünün operandı hangi sınıfa ilişkin bir adres ise o sınıfa ilişkin bitiş fonksiyonu çağırılır. Ancak bazı durumlarda adrese ilişkin sınıfın bitiş fonksiyonu değil de nesnenin orijinaline ilişkin sınıfın bitiş fonksiyonunun çağırılması gerekir. [IMG]file:///C:/DOCUME%7E1/naim/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image029.jpg[/IMG] B sınıfı A sınıfından türetilmiş olsun; { A *p; p = new B(n); delete p; } Burada normal olarak p göstericisi A sınıfına ilişkin olduğu için delete p; işlemindeA sınıfının bitişi fonksiyonu çağırılır. Oysa B sınıfına ilişkin bitiş fonksiyonunun çağırılması uygun olan durumdur. İşte taban sınıf bitiş fonksiyonu sanal yapılırsa B sınıfına ilişkin bitiş fonksiyonu çağırılır. Bu bitiş fonksiyonu kendi içerisinde zaten A sınıfının bitiş fonksiyonunu da çağıracaktır. Bir sınıf kendisinden türetme yapılacak şekilde tasarlanıyorsa mutlaka bitiş fonksiyonu sanal yapılmalıdır. iostream Sınıf Sistemi Bu sınıf sistemi ekran, klavye ve dosya işlemleri için türetilmiş bir dizi sınıftan oluşur. [IMG]file:///C:/DOCUME%7E1/naim/LOCALS%7E1/Temp/msohtmlclip1/01/clip_image031.jpg[/IMG] istream sınıfı klavye ve dosyadan okuma yapmak için gereken veri elemanlarına ve üye fonksiyolarına sahiptir. ostream sınıfı ise ekrana ve dosyaya bilgi yazmak için gereken veri elemanlarına ve üye fonksiyonlarına sahiptir. ios sınıfı okuma ve yazma işlemlerinde kullanılan temel ve ortak veri elemanlarını ve üye fonksiyonlarını bulunduran bir sınıftır. ostream sınıfının her türden parametreye sahip bir grup **** (sola shift) operatör fonksiyonu vardır. Bu operatör fonksiyonları parametreleri ile belirtileni ekrana yazdırırlar. Yani ostream sınıfı türünden bir sınıf nesnesi tanımlanır ve bu operatör fonksiyonları kullanılırsa ekrana yazdırma yapılabilir. ostream x; x **** 100; Ancak zaten kütüphane içerisinde cout isimli bir nesne tanımlanmıştır. Yani bu nesne kullanılarak ekrana yazdırma yapılabilir. iostream sınıf sisteminin bütün bildirimler iostream.h içerisindedir. Sınıfların üye fonksiyonları kütüphane içerisindedir. ostream sınıfının **** operatör fonksiyonlarının geri dönüş değerleri yine ostream türünden bir referanstır. Böylece bu operatör fonksiyonu birden fazla eleman için kombine edilebilir. /*-----cout.cpp-----*/ #include <iostream.h> void main(void) { int a = 123; cout **** "Value=" **** 20 **** '\n'; } /*-------------------*/ Sınıf İçerisinde Başka Bir Sınıf, Yapı, typedef ve enum Bildirimlerinin Bulunması Bir sınıfın içerisinde yapı, başka bir sınıf, enum vs. bildirimleri yapılabilir. Genel olarak sınıf içerisinde bildirilen bütün değişken isimleri(yapı, sınıf, enum sabitleri gibi) dışarıdan ancak çözünürlük operatörü kullanılarak sınıf ismiyle erişilebilir. Tabii bu erişimin geçerli olabilmesi için bildirimin sınıfın public bölümde yapılmış olması gerekir. Yani sınıf içerisinde bildirilen bütün değişkenler sınıf faaliyet alanına sahip olur. Sınıfın üye fonksiyonları içerisinde doğrudan, dışarıdan ancak sınıf ismi ve çözünürlük operatörüyle çağırılabilir. class X { private: int x; public: typedef unsigned int WORD; void func(void); }; void X::fonk(void) { WORD x; /*Doğru*/ } void main(void) { WORD x; /*Yanlış*/ X::WORD x; /*Doğru*/ } iostream Sınıf Sisteminde Formatlı Yazdırma İşlemleri ios sınıfının protected bölümünde long bir x_flags isimli bir değişken vardır. ostream sınıfının **** operatör fonksiyonları x_flags değişkeninin bitlerine bakarak yazdırma işleminin nasıl yapılacağını anlarlar. Bu değişkenin değerini alan ve değişkenin değerini değiştiren iki public üye fonksiyonu vardır: long flags(); long flags(long); x_flags değişkeninin ilgili bitlerini set edebilmek için ios sınıfı içerisinde bütün bitleri 0 yalnızca bir biti 1 olan çeşitli sembolik sabitler enum sabiti biçiminde tanımlanmıştır. Örneğin bitlerden birisi yazma işleminin hex sistemde yapılıp yapılmayacağını belirler. O bitin set edilip eski hale getirilmesi şöyle yapılabilir. #include <iostream.h> void main(void) { long x; x = cout.flags(); cout.flags(x | ios::hex); /*hex biçimde yazılmasını sağlar*/ cout.flags(x & ~ios::hex); /*hex biçimde yazılmamasını sağlar*/ } x_flags değişkeninin uygun bitleri 1 yapılarak yazdırma işlemi çeşitli biçimlere çekilebilir. /*-----cout2.cpp-----*/ #include <iostream.h> void main(void) { int a = 100; long x; cout **** a **** '\n'; x = cout.flags(); cout.flags(x & ~ios::hex | ios::hex); cout **** a **** '\n'; } /*--------------------*/ Burada yapılan işlemi tek aşamada yapan setf isimli bir üye fonksiyon da vardır. long setf(long); long unsetf(long); setf fonksiyonu önce x_flags içerisindeki değeri alır, bunu parametresiyle or işlemine sokarak işlemi bir hamlede gerçekleştirir. /*-----cout3.cpp-----*/ #include <iostream.h> void main(void) { int a = 100; cout **** a **** '\n'; cout.setf(ios::hex); cout **** a **** '\n'; cout.unsetf(ios::hex); cout **** a **** '\n'; } /*--------------------*/ ios sınıfının protected x_width elemanı yazma işlemi için kaç karakter alanı kullanılacağını belirlemekte kullanılır. Bu elemanla ilişki kuran iki fonksiyon vardır. int width(void); int width(int w); Benzer biçimde x_precision noktadan sonra kaç basamak yazılacağını belirlemekte kullanılır. /*-----cout4.cpp-----*/ #include <iostream.h> void main(void) { double x = 3.52534; cout **** x **** '\n'; cout.precision(10); cout **** x **** '\n'; } /*--------------------*/ --------------------------------------------------------------------------------------------------------- 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] 3 | Baytar | Bilgisayar Programlama | 0 | 28.09.08 14:24 |
| C++[uyumluluk] 2 | Baytar | Bilgisayar Programlama | 0 | 28.09.08 14:24 |
| C++[uyumluluk] 1 | Baytar | Bilgisayar Programlama | 0 | 28.09.08 14:23 |