Nesneye Dayalı Programlama DERS 6 Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Türemiş Sınıflarda Bilinirlik Alanı Bilinirlik alanı (scope) bir ismin derleyici tarafından tanınabildiği program aralığıdır. C ve C++ da bir blok içinde aynı isimli birden fazla değişken biliniyorsa o blok içinde aynı isimli değişken kullanıldığında dar bilinirlik alanına sahip olana erişilir. Dar bilinirlik alanındaki isim kendisini kapsayan daha geniş bilinirlik alanında bulunan ismi maskeler.Onun görülmesini engeller. Aynı kural C++’ da türetme işlemlerini kapsayacak biçimde geçerlidir. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Önce şu soruyu soralım: Türetme durumunda taban sınıf türemiş sınıf tarafından erişilebildiğine göre taban sınıftaki bir isim mi daha dar bilinirlik alanına sahiptir, türemiş sınıftaki bir isim mi? Cevap: Türemiş sınıftaki isim. Çünkü taban sınıftaki isimler hem kendi sınıfı içinde hem de türemiş sınıf içinde kullanıldığına göre daha geniş bir bilinirlik alanına sahiptir. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Örnek: class Base { protected: int b; public: Base():b(0){} void display()const; }; #include <iostream> void Base::display()const { std::cout << "b = " << b << std::endl; } class Der: public Base { Der():b(1){} void set(int); void display() const; void func() const; void Der::set(int val) b = val; //Der::b = val; void Der::display()const void Der::func()const display(); Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Burada Der sınıfının set üye işlevi içinde kullanılan b ismi, daha dar bilinirlik alanına sahip olan Der sınıfının b isimli elemanına ilişkindir. Der sınıfının b isimli elemanı Base sınıfının b isimli elemanını maskeler yani görünmesini engeller.(Tabii eğer Der sınıfının b isimli bir elemanı olmasaydı burada kullanılan b, Base sınıfına ilişkin olurdu.) Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Ayrıca Base sınıfının display üye işlevi içinde kullanılan b elemanı Base sınıfına ilişkin olandır. Zaten taban sınıf türemiş sınıfa erişemediğine göre hiçbir biçimde bunun Base sınıfına ilişkin olması söz konusu değildir. Türemiş sınıf içinde aynı isimli taban sınıf elemanlarına ve işlevlerine çözünürlük işleci ile erişilebilir. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Örneğin bu kez Der sınıfının iki parametreli set isimli bir üye işlevi daha olduğunu düşünelim: İşlevde doğrudan kullanılan b, bilinirlik alanı kurallarına göre Der sınıfına ilişkin olandır. Ancak çözünürlük işleci ile, yani Base::b ifadesi ile kullanılan b, Base sınıfına ilişkin olandır. İki terimli çözünürlük işlecini taban ve türemiş sınıflarda aynı isimli elemanlar ya da işlevler varken taban sınıfın elemanlarına erişmek amacıyla bu biçimde kullanılabilir. void Der::set(int x, int y) { b = x; Base::b = y; } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Bilinirlik alanı kuralları işlev isimleri için de geçerlidir. Türemiş ve taban sınıfta aynı isimli işlevler varsa dar bilinirlik alanı kuralına göre türemiş sınıftaki anlaşılır. Örneğin Der sınıfının func işlevi içinde çağrılan display işlevi Der sınıfına ilşkin olandır. Çözünürlük işleci, aynı amaçla üye işlevler için de kullanılabilir. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Örneğin türemiş sınıf olan Der sınıfının func isimli üye işlevi içinde bu kez taban sınıfın display isimli işlevi çağrılmak istenseydi bu aşağıdaki gibi yapılabilirdi: Dar bilinirlik alanı kuralı dışarıdan sınıf elemanlarına erişirken de geçerlidir. Yukarıda verdiğimiz türetme işlemi için aşağıdaki kod yazılmış olsun: void Der::func() { Base::display(); } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli int main() { Der der_object, *der_ptr; der_object.display(); //1 der_object.Base::display(); //2 der_ptr = &der_object; der_ptr->display(); //3 der_ptr->Base::display(); //4 Base base_object; base_object.display(); //5 return 0; } Yukarıdaki main işlevinde yorum satırlarıyla işaretlenen işlev çağrılarına bakalım: //1 Der sınıfının display işlevi çağrılır. //2 Base sınıfının display işlevi çağrılır. //3 Der sınıfının display işlevi çağrılır. //4 Base sınıfının display işlevi çağrılır. //5 Base sınıfının display işlevi çağrılır. Taban sınıf türemiş sınıfa erişemez! Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Bilinirlik alanı ile erişilebilirlik aynı şeyler değildir. C++’da her zaman önce isim araması daha sonra erişim denetimi yapılır. Bu kuralın etkisini yandaki gibi bir türetme işleminde değerlendirelim: class Base{ public: int x; }; class Der: public Base { void func() { Der der_object; der_object.x = 0; //Geçersiz! } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli C++'a yeni başlayanlar func işlevi içindeki der_object.x = 0; atamasının geçerli olması gerektiğini düşünürler. “Hem türemiş sınıfta hem de taban sınıfta x isimli bir eleman olduğuna göre, önce türemiş sınıfta bulunan x isimli elemana erişilmeye çalışılır. Türemiş sınıfta bulunan x sınıfın private bölümünde bulunduğu için bu erişim gerçekleşmez. Ancak taban sınıfın public bölümünde bulunan x’e erişilir. Ancak durum böyle değildir. Önce isim araması yapılır. Türemiş sınıf olan Der sınıfının bilinirlik alanında x ismi bulunduğunda isim arama sona erdirilir. Aranan isim bulunmuş ve x isminin türemiş sınıf bilinirlik alanındaki x olduğu anlaşılmıştır. Artık ne nedenle olursa olsun yeniden bir isim araması yapılmaz. Şimdi bu x'e erişimin geçerli olup olmadığı sorgulanır. Türemiş sınıfın x isimli elemanı türemiş sınıfın private bölümünde olduğu için erişim geçersizdir. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Türemiş Sınıf İçinde Yapılan using Bildirimi Türemiş ve taban sınıfların farklı bilinirlik alanı oluşturduklarını biliyorsunuz. Türemiş sınıf bilinirlik alanı içinde bildirilen bir isim, taban sınıf içinde de kullanılmışsa, taban sınıftaki isim maskelenir. Maskelenme istenmiyorsa türemiş sınıf içinde using bildirimi yapılabilir. Sınıf içi using bildirimiyle taban sınıflara ilişkin maskelenen isimler görünür kılınır. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Böylece taban sınıf ve türemiş sınıf işlevleri farklı bilinirlik alanında olsalar bile yüklenebilir. Yandaki kodu inceleyelim: #include <iostream> class Base { public: void foo(){std::cout << "Base::foo()" << std::endl;} }; class Der: public Base { using Base::foo; void foo(int){std::cout << "Der::foo(int)" << std::endl;} int main() { Der der_object; der_object.foo(); der_object.foo(25); return 0; } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Der sınıfının tanımı içinde yapılan using bildirimiyle Base sınıfının foo isimli işlevi Der sınıfı içinde görülebilir hale getiriliyor. Böylece: der_object.foo(); çağrısı söz konusu olduğunda void Base::foo(); işlevi de çağrıya aday işlev olarak belirlenir. using bildirimiyle taban ve türemiş sınıfın foo işlevleri ayrı bilinirlik alanlarına ait olmasalar da yüklenebilir. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Şimdi de aşağıdaki koda bakalım: main işlevi içinde çağrılan, Der sınıfının foo işlevidir. Der sınıfının foo işlevinin gizli parametre değişkeni Der & türünden iken, Base sınıfının foo işlevinin gizli parametre değişkeni Base & türündendir. #include <iostream> class Base { public: void foo(){std::cout << "Base::foo()" << std::endl;} }; class Der: public Base { using Base::foo; void foo(){std::cout << "Der::foo()" << std::endl;} int main() { Der der_object; der_object.foo(); return 0; } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Erişilebirliğin Tekrar Tanımlanması Taban sınıfının public üyelerinin erişilebilirliği türemiş sınıf içerisinde yeniden tanımlanabilir. Private olarak miras alırsanız, taban sınıfın bütün public üyeleri private olur.Bunlardan herhangi birinin görülebilir olmasını isterseniz, türemiş sınıfın public kısmında sadece isimlerinin başına using kelimesini kullanmanız yeterli. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli class Base{ private: int k; public: int i; void f(); }; class Derived : private Base{ // All members of Base are private now int m; using Base::f; // f() is public again , i is still private void fb1(); int main(){ Base b; Derived d; b.i=5; // OK public in Base d.i=0; // ERROR private inheritance b.f(); // OK d.f(); // OK return 0; Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Atama operatörü ve miras Taban sınıfın atama operatörü, türemiş sınıfın atama operatörü olamaz. Örnek: class String{ protected: int size; char *contents; public: const String & operator=(const String &); // assignment operator: // Other methods }; const String & String::operator=(const String &in_object) { size = in_object.size; delete[ ] contents; // delete old contents contents = new char[size+1]; strcpy(contents, in_object.contents); return *this; } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli String2 sınıfı String sınıfından türemiştir.Eğer atama operatörü gerekliyse şu şekilde yazılmalıdır. class String2 : public String{ // String2 is derived from String int size2; char *contents2; public: const String2 & operator=(const String2 &); // assignment operator for String2 : // Other methods }; // **** Assignment operator for String2 **** const String2 & String2::operator=(const String2 &in_object) { size = in_object.size; // inherited size delete[ ] contents; contents = new char[size + 1]; // inherited contents strcpy(contents, in_object.contents); size2 = in_object.size2; delete[ ] contents2; contents2 = new char[size2 + 1]; strcpy(contents2, in_object.contents2); return *this; } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Bir önceki örnekte, String(taban) sınıfın verileri protected olmalıdır. Aksi halde String2(türemiş) sınıfın metodları onlara erişemez.String2 sınıfı için atama operatörü yazmanın en iyi yolu, String(taban) sınıfın atama operatörünü çağırmaktır. //** Assignment operator ** const String2 & String2::operator=(const String2 & in_object) { String::operator=(in_object); // call the operator= of String (Base) cout<< "Assignment operator of String2 has been invoked" << endl; size2 = in_object.size2; delete[] contents2; contents2 = new char[size2 + 1]; strcpy(contents2, in_object.contents2); return *this; } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Çoklu Türetme (multiple inherritence) Bir sınıfın birden fazla taban sınıfa sahip olması durumudur. Çoklu türetme durumunda başlangıç fonksiyonlarının çağrılması bildirimde ilk yazılan taban sınıfın başlangıç fonksiyonu önce çağrılacak biçimdedir. Örneğin class C:public A, public B { böyle bir bildirimde önce a, b, c şeklinde başlangıç fonksiyonları çağrılır. Bitiş fonksiyonlarıda c, b, a olacak şekilde tam tersdir. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Türemiş sınıf nesnesi bütün taban sınıf nesnelerinide içerir. Taban sınıf nesnelerinin tüenmiş sınıf içindeki organizasyonu standart olarak belirlenmemiştir. Ancak bildirimde genllikle bildirimde ilk yazılan taban sınıfın veri elemanları düşük anlamlı adreste oacak biçimde ardışıl bir yerleşim söz konusudur. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Tekrarlı Taban Sınıflar class Gparent { }; class Mother : public Gparent class Father : public Gparent class Child : public Mother, public Father Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Mother ve Father sınıflarının ikiside Gparent sınıfından türemiştir, ve Child sınıfı Mother ve Father sınıflarından türemiştir.Hatırlayalım ki, her bir nesne miras yoluyla, taban sınıfın altnesnesini içererek yaratılır. Mother nesnesi ve Father nesnesi Gparent’in alt nesnelerini, Child nesnesi Mother and Father’ın alt nesnelerini, dolayısıyla Child nesnesi biri Mother’dan biri Father’dan türemiş, Gparentin iki alt nesnesini içerir Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli class Gparent { protected: int gdata; }; Child’dan şu şekilde ulaşmaya çalışırsanız hata verir. class Child : public Mother, public Father public: void Cfunc() int temp = gdata; // ERROR: ambiguous } Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Derleyici gdata’ya verilen referansın belirsiz oldugunu söyliyecektir.Hangi çeşit gdata’ya ulaşacağını bilemez, Gparent’dan miras alan Mother alt nesnesinden gelen gdata mı, yoksa Gparent’dan miras alan Father alt nesnesinden gelen gdatamı. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli
Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli Sanal Taban Sınıflar Virtual kelimesi kullanarak oluşturulur. Örneğin Mother ve Father Gparent’dan türeme yaparken: class Gparent { }; class Mother : virtual public Gparent class Father : virtual public Gparent class Child : public Mother, public Father Virtual kelimesi compilera sonradan gelen türemiş sınıflar içinden sadece bir tanesinin alt nesnesinin miras alınmasını söyler. Bu belirsizlik problemini çözer. Harran Üniversitesi Bilgisayar Mühendisliği Yrd.Doç.Dr.Nurettin Beşli