Fonksiyonları Aşırı Yükleme / Function Overloading (C++)

Fonksiyonları Aşırı Yükleme / Function Overloading (C++)

Sınıflardan sonra C++’ın belki de en önemli ve en yaygın özelliği fonksiyonların aşırı yüklenmesi(function overloading)dir. Fonksiyonları aşırı yükleyerek sadece C++’a bir çeşit polimorfizm(çok biçimlilik) kazandırmış olmuyoruz, aynı zamanda C++ programlama ortamını dinamik olarak genişletebilmenin temellerini de oluşturuyoruz. Aşırı yükleme çok önemli bir konu, bu nedenle burada kısa bir giriş yapalım.

C++’da, iki veya daha çok fonksiyon, argümanlarının tipleri veya argüman sayısı, ya da her ikisi farklı olduğu sürece aynı adı paylaşabilirler. İki veya daga fazla fonksiyon aynı adı paylaşıyorsa onlar aşırı yüklenmiştir denir. Aşırı yüklenmiş fonksiyonlar, birbirleriyle alakalı işlemlerin aynı adla çağırılmasına izin vererek programın karmaşıklığını azaltabilir.

Bir fonksiyonu aşırı yüklemek oldukça kolaydır : fonksiyonun gerekli olan tüm versiyonlarını deklare edin ve tanımlayın. Derleyici, otomatik olarak fonksiyonu çağırırken kullanılan argümanların sayısına ve tipine göre fonksiyonun doğru versiyonunu seçecektir.

NOT:  C++’da operatörleri aşırı yüklemek de mümkündür. Fakat operatörlerin aşırı yüklenmesini tam olarak anlamadan önce C++’ı daha iyi kavramamız gerekiyor.

 

Örnekler:

1) Fonksiyonların aşırı yüklenmesinin temel nedenlerinden biri, bir arabirime birden fazla metot felsefesini oluşturan tasarım polimorfizmini elde etmektir. Biliyoruz ki C’de, sadece işledikleri veri tipleri farklı fakat birbiriyle bağlantılı birkaç fonksiyonu kullanmak pek yaygındır. Bu durumun klasik C standart kütüphanesindedir. Daha önce belirttiğimiz gibi, kütüphanede abs( ), labs( ) ve fabs( ) fonskiyonları bulunmaktadır. Bunlar sırasıyla bir tamsayının mutlak değerini, long tamsayının mutlak değerini ve bir reel sayının mutlak değerini vermektedir.

Fakat üç farklı veri tipinin olması nedeniyle üç ayrı adın kullanılması, durumu olduğundan daha karmaşık hale getiriyor. Her üç durumda da mutlak değer döndürülüyor; sadece verinin tipi değişiyor. C++ da bu durumu, örnekte de görüldüğü gibi üç veri tipi için bir arada aşırı yükleme yaparak düzeltebiliriz.

 

#include <iostream>
using namespace std;

//abs( )'ı üç şekilde aşırı yükleyin
int abs(int n);
long abs(long n);
double abs(double n);

int main()
{
   cout << " -10'un mutlak değeri: " << abs(-10) <<  "\n\n";
   cout << " -10L'nin mutlak değeri: " << abs(-10L) <<  "\n\n";
   cout << " -10.01'in mutlak değeri: " << abs(-10.01) <<  "\n\n";

   return 0;
}

//int sayılar için abs()
int abs(int n){
   cout << "Tamsayılar için abs() \n";
   return n<0 ? -n : n;
}

//long tamsayılar sayılar için abs()
long abs(long n){
   cout << "Long sayılar için abs() \n";
   return n<0 ? -n : n;
}

//double sayılar için abs()
double abs(double n){
   cout << "Double sayılar için abs() \n";
   return n<0 ? -n : n;
}

Bu programda, her veri tipi için bir tane olmak üzere abs() adında üç fonksiyon tanımlıyoruz. main( )‘in içinde abs( ) üç ayrı argüman tipi kullanılarak çağırılır. Derleyici, argüman olarak kullanılan verinin tipine göre otomatik olarak fonksiyonun doğru versiyonunu çağırır. Program aşağıdaki sonucu verir:

Tam sayılar için abs()

-10’un mutlak değeri: 10

Long  sayılar için abs()

-10L’nin mutlak değeri: 10

Double sayılar için abs()

-10.01’in mutlak değeri: 10.01

Bu örnek çok basittir, fakat yine de fonksiyonların aşırı yüklenmesinin ne kadar değerli olduğunu göstermektedir. Genel bir iş sınıfı tek bir adla tanımlandığından birbirine benzer üç farklı adın (bu durumda abs( ), fabs( ) ve labs( )) oluşturduğu karmaşıklık engellenmiş olur. Şimdi sadece tek bir adı, genel işi tanımlayan adı hatırlamanız gerekiyor. Fonksiyonun (yani metodun) çağırılması gereken uygun versiyonunu seçmek derleyiciye kalıyor. Böylece karmaşıklık azalıyor ve polimorfizmin kullanılmasıyla üç ad bire düşüyor.

Polimorfizmin kullanılması bu örnekte önemsiz gibi görünse de çok büyük programlarda “bir birime birden çok metod” yaklaşımının oldukça etkili olduğunu görebilirsiniz.

2) Fonksiyonların aşırı yüklenmesi ile ilgili bir başka örnek. Bu örnekte date( ) fonksiyonu veriyi katar veya üç tamsayı şeklinde alabilecek şekilde aşırı yüklenmiştir. Her iki durumda da fonksiyon kendisine gönderilen tarihi göstermektedir.

#include <iostream>
using namespace std;

void date(char *date); //katar şeklinde tarih
void date(int ay, int gun, int yil); //sayı şeklinde tarih

int main()
{
   date("8/23/99");
   date(8,23,99);

   return 0;
}

//Katar şeklinde tarih
void date(char *date){
   cout << "Date: " << date << "\n";
}

//Tamsayı şeklinde tarih
void date(int ay, int gun, int yil){
   cout << "Tarih: " << gun << "/"  << ay << "/" << yil << "\n";
}

Fonksiyonların aşırı yüklenmesi için verdiğimiz bu örnek, bir fonksiyon için en doğal arabirimin nasıl sağlandığını bize gösteriyor. Tarihin hem kadar şeklinde hem de gün/ay/yıl ‘ı gösteren üç tam sayı şeklinde temsil edilmesile çok sık karşılaşıldığından duruma göre en uygun şekli seçmekte serbestsiniz.

3) Şimdiye kadar argümanlarının veri tipleri farklı olan aşırı yüklenmiş fonksiyonlar gördünüz. Fakat, aşağıdaki örnekteki gibi, argümanlarının sayıları farklı fonksiyonlar da aşırı yüklenebilirler:

#include <iostream>
using namespace std;
void f1(int a);
void f1(int a, int b);

int main()
{
   f1(10);
   f1(int a, int b);
   return 0;
}

void f1(int a){
   cout << "f1(int a)\n";
}
void f1(int a, int b){
   cout << "f1(int a, int b)\n";
}

4) Döndürülen sonuç tipinin farklı olması fonksiyonun aşırı yüklenebilmesi için yeterli değildir. Eğer iki fonksiyonun, sadece döndürüldüğü veri tipleri farklıysa derleyici doğru fonksiyonu her zaman seçemeyebilir. Örneğin aşağıdaki program yanlıştır, çünkü içerisinde belirsizlik vardır:

//bu yanlıştır ve derlenemez.
int f1(int a);
double f1(int a);
.
.
.
f1(10); // derleyici hangi fonksiyonu çağıracak?

Açıklamada belirttiğimiz gibi, derleyici f1( ) ‘in hangi versiyonunu çağıracağını bilemez.

Bir Cevap Yazın