Yazan : Şadi Evren ŞEKER

Bu yazının amacı, C++ dili için fonksiyon üzerine bindirme (function overriding veya method overriding) konusunu açıklamaktır.

Üzerine bindirme işlemi, basitçe aralarında miras (inheritance) ilişkisi bulunan iki sınıf için kullanılabilir. C++ dilindeki, nesneler arasında kurulan bu ilişki modeli hakkında daha detaylı bilgi için, bu bağlantıya tıklayabilirsiniz.

Öncelikle iki sınıf tanımlayıp bu iki sınıf arasında miras ilişkisi kurulmalıdır. Bu miras ilişkisinden sonra, sınıflardan birisi, diğerine bazı fonksiyonlarını miras bırakacaktır. İşte fonksiyon üzerine bindirme de tam olarak burada devreye girer. Şayet miras alınan fonksiyonlar alt sınıfta yeniden tanımlanmak isteniyorsa, bu duruma üzerine bindirme (overriding) ismi verilir.

Örneğin, dikdörtgen olarak tanımlı bir sınıfımız bulunsun.

class dikdortgen{
public:
   int x,y;
   int boy,en;
public:
   int alan();
};
int dikdortgen::alan(){
   return en *boy;
}

Yukarıdaki bu tanıma uyan diğer bir iki boyutlu şekil ise, kare’dir. Bilindiği üzere kare kenarları birbirine eşit bir dikdörtgendir. O halde kare sınıfını aşağıdaki şekilde tanımlayabiliriz.

class kare:public dikdörtgen{ // kare sınıfı, dikdörtgen sınıfından miras alır.
public:
   int alan();
};
kare::alan(){
   return en * en ;
}
};

Yukarıdaki kare sınıfında görüldüğü üzere, her kare’nin zaten sahip olduğu x,y koordinatları ve en, boy bilgisi yeniden kodlanmamış bu bilgiler miras ile alınmıştır. Alan hesaplamak için yazılan alan() fonksiyonu ise, yeniden yazılmıştır. Bunun sebebi kare sınıfında sadece tek kenar bilgisinin yeterli olması ve bu durumda alan hesabının değişmesidir.

Şayet kare sınıfı için, yeniden alan fonksiyonu yazılmasaydı, dikdörtgen sınıfının alan fonksiyonunu kullanabilirdi. Ancak buradaki yeniden yazılan aynı isme ve parametrelere sahip olan alan fonksiyonu, üzerine yükleme işlemi yapmıştır (overriding).

Yukarı Atamak (Upcasting)

Miras ilişkisi içerisinde bulunan iki sınıftan tanımlanan iki nesne arasında atama yapılabilir. Örneğin yukarıdaki kodu ele alalım ve bu kodla oluşan sınıf diyagramını çizmeye çalışalım:

Yukarıdaki bu çizimden anlaşılacağı üzere, her kare bir dikdörtgendir ifadesi kullanılabilir. Bu ifade aynı zamanda dikdörtgen sınıfının, kare sınıfının atası olduğu, veya kare sınıfının, dikdörtgen sınıfının bir çocuğu olduğu şeklinde de yorumlanabilir.

Yukarıdaki bu ilişki şeklinde, bizim için dikdörtgen sınıfı üst, kare sınıfı ise alt sınıf olarak kabul edilebilir. Bu durumda kare sınıfından bir nesnenin, dikdörtgen sınıfından bir nesneye atanmasına upcasting ismi verilir. Burada nesne olduğu gibi kalmakta ancak nesne göstericisi (object referrer) farklı bir sınıftan olmaktadır. Bu işlemin kod olarak karşılığı aşağıdaki şekildedir:

dikdörtgen *d = new kare();

Görüldüğü üzere kare sınıfından tanımlanan bir nesne, dikdörtgen sınıfından bir nesne göstericisi tarafından gösterilmiştir. Bu kullanım, tip belirsizliği yaşanan durumlarda sıklıkla konu olur. Örneğin kodlamak istediğimiz fonksiyon, bir parametre olarak dikdörtgen alıyor ve bu dikdörtgenin orjine (merkez 0,0 noktasına) uzaklığını ölçüyor olsun. Bu durumda fonksiyonumuz aşağıdaki şekilde olabilir:

int mesafe(dikdörtgen *d){
   return sqrt(x*x+y*y);
}

Yukarıdaki bu fonksiyon, her türlü dikdörtgeni kabul etmektedir. O halde bu fonksiyona bir dikdörtgen nesnesi veya bir kare nesnesi, parametre olarak verilebilir.

Upcasting işlemi, sanal fonksiyonların kullanımı açısından önem taşır.

Sanal Fonksiyonlar (Virtual Functions)

Yukarı atama işlemi sırasında yaşanan bir problem, yukarı yükleme işlemi sonrasında, nesnenin bir fonksiyonu çağırıldığında, ata sınıfından mı çocuk sınıfından mı fonksiyonun çalışacağıdır.

Örneğin, yukarıdaki miras konusunu ele alalım. Dikdörtgen sınıfından bir nesne göstericisinin içerisine, kare sınıfından bir nesne atanmıştır. Şimdi bu atamadan sonra

d->alan();

şeklinde bir fonksiyon çağırımı yapılırsa, acaba kare sınıfının mı, dikdörtgen sınıfının mı alan fonksiyonu çalışacaktır?

Herhangi özel bir durum olmaksızın, yukarıda yazdığımız kodlar için, d->alan() çağrımı, ata sınıfında (base class, ancestor, parent) olan alan fonksiyonunu çalıştıracaktır.

Peki acaba çocuk sınıftaki alan fonksiyonun çalışmasını isteseydik kodu nasıl değiştirmemiz gerekirdi?

Sınıf tanımı sırasında, ata sınıfta bulunan fonksiyonu tanımlarken başına virutal eklenmesi, bu sınıfın ileride bir şekilde üzerine yükleme yapıldığında (override), ve ayrıca yukarı atama yapıldığında (upcasting), üzerine yükleme yapılan fonksiyon tarafından çalıştırılacağıdır.

Örnek olarak yukarıdaki sınıf tanımını aşağıdaki şekilde değiştirirsek,

class dikdortgen{
public:
   int x,y;
   int boy,en;
public:
virtual int alan(){
   return en*boy;
}
};

Bu yeni tanımdan sonra yukarı atama yapılırsa,

dikdörtgen *d = new kare();

artık d göstericisinin alan fonksiyonu çağırıldığında,

d->alan();

çalışacak olan alan fonksiyonu, kare sınıfındaki alan fonksiyonudur. Çünkü dikdörtgen sınıfında bulunan alan fonksiyonu sanal bir fonksiyondur (virtual function).

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir