İşlev Yükleme
Aynı isimde birden fazla işlev tanımlamaya işlev yükleme denir. İsimleri aynı olan bu işlevlerin ayırt edilebilmeleri için parametrelerinin birbirlerinden farklı olmaları gerekir.
Bu kullanımda "yükleme" sözcüğünün "aynı isme yeni görev yükleme" anlamına geldiğini düşünebilirsiniz.
Aşağıda aynı isimde ama parametreleri farklı işlevler görüyorsunuz:
import std.stdio; void bilgiVer(double sayı) { writeln("Kesirli sayı: ", sayı); } void bilgiVer(int sayı) { writeln("Tamsayı : ", sayı); } void bilgiVer(string dizgi) { writeln("Dizgi : ", dizgi); } void main() { bilgiVer(1.2); bilgiVer(3); bilgiVer("merhaba"); }
İşlevlerin hepsinin de ismi bilgiVer olduğu halde, derleyici parametrenin türüne uygun olan işlevi seçer ve onun çağrılmasını sağlar. Örneğin 1.2 hazır değerinin türü double olduğu için, onun kullanıldığı durumda o işlevler arasından double parametre alanı çağrılır.
Hangi işlevin çağrılacağı derleme zamanında seçilir. Bu seçim her zaman kolay veya açık olmayabilir. Örneğin şu kodda kullanılan int değer hem real hem de double türüne uyduğu için derleyici hangisini seçeceğine karar veremez:
real yediKatı(real değer) { return 7 * değer; } double yediKatı(double değer) { return 7 * değer; } void main() { int sayı = 5; auto sonuç = yediKatı(sayı); // ← derleme HATASI }
Not: Normalde aynı işi yapan böyle iki işlevin yazılması gereksizdir. Tek işlev tanımının nasıl birden fazla tür için kullanılabileceğini daha sonra Şablonlar bölümünde göreceğiz.
Öte yandan, bu işlevlerin long türünde parametre alan bir üçüncüsü tanımlansa derleme hatası ortadan kalkar çünkü yüklenen işlev seçimi konusunda int değerler long türüne kesirli türlerden daha uyumludurlar:
long yediKatı(long değer) { return 7 * değer; } // ... auto sonuç = yediKatı(sayı); // şimdi derlenir
Parametre uyum kuralları
Aynı isimde birden fazla işlev bulunması, derleyicinin bir seçim yapmasını gerektirir. Yüklenen işlevler arasından, kullanılan parametrelere daha çok uyan işlev seçilir.
Bu seçim çoğu durumda kolay ve beklendiği gibi olur; ama hangi işlevin daha çok uyduğu konusu bazen çok karışıktır. Bu yüzden uyum kuralları geliştirilmiştir.
Parametreler için uyum konusunda dört durum vardır:
- uyumsuzluk
- otomatik tür dönüşümü yoluyla uyum
const'a dönüştürerek uyum- tam uyum
Derleyici, yüklenmiş olan işlevlerden hangisini çağıracağına karar vermek için işlevleri gözden geçirir. Her işlevin parametrelerine teker teker bakar ve her parametrenin yukarıdaki dört uyum durumundan hangisinde olduğunu belirler. Bütün parametreler içindeki en az uyum, bu işlevin de uyumu olarak kabul edilir.
Bu şekilde bütün işlevlerin uyum durumları belirlendikten sonra; eğer varsa, en çok uyan işlev seçilir.
Eğer birden fazla işlev aynı derecede uymuşsa, onlardan hangisinin seçileceğine daha da karışık başka kurallar yoluyla karar verilir.
Ben burada bu kuralların daha derinine inmeyeceğim; çünkü eğer bu kadar karışık kurallarla yüz yüze kalmışsanız, aslında programınızın tasarımında değişiklik yapma zamanı gelmiş demektir. Belki de işlev şablonlarını kullanmak daha doğru olacaktır. Hatta belki de aynı isimde işlev tanımlamak yerine, daha açıklayıcı isimler kullanarak hangisini çağırmak istediğinizi açıkça belirtmek bütün karışıklığı ortadan kaldıracaktır: yediKatı_real ve yediKatı_double gibi...
Yapılar için işlev yükleme
İşlev yükleme, yapılarda ve sınıflarda çok yararlıdır; üstelik o türlerde işlev seçimi konusunda uyum sorunları da çok daha azdır. Yukarıdaki bilgiVer işlevini Yapılar bölümünde kullandığımız bazı türler için yükleyelim:
struct GününSaati { int saat; int dakika; } void bilgiVer(GününSaati zaman) { writef("%02s:%02s", zaman.saat, zaman.dakika); }
O tanım sayesinde artık GününSaati nesnelerini de bilgiVer işlevine gönderebiliriz. Böylece programımızda her tür nesneyi aynı isimle yazdırabileceğimiz alışılmış bir yöntemimiz olur:
auto kahvaltıZamanı = GününSaati(7, 0);
bilgiVer(kahvaltıZamanı);
Temel türlerde olduğu gibi, artık GününSaati nesneleri de kendilerine özgü çıktı düzenleri ile yazdırılmış olurlar:
07:00
bilgiVer işlevini yapılar bölümünde değinilen Toplantı yapısı için de yükleyelim:
struct Toplantı { string konu; int katılımcıSayısı; GününSaati başlangıç; GününSaati bitiş; } void bilgiVer(Toplantı toplantı) { bilgiVer(toplantı.başlangıç); write('-'); bilgiVer(toplantı.bitiş); writef(" \"%s\" toplantısı (%s katılımcı)", toplantı.konu, toplantı.katılımcıSayısı); }
Gördüğünüz gibi; bilgiVer'in GününSaati için yüklenmiş olanı, Toplantı'yı yazdıran işlev tarafından kullanılmaktadır. Artık Toplantı nesnelerini de alıştığımız isimdeki işlevle yazdırabiliriz:
auto geziToplantısı = Toplantı("Bisikletle gezilecek yerler", 3, GününSaati(9, 0), GününSaati(9, 10)); bilgiVer(geziToplantısı);
Çıktısı:
09:00-09:10 "Bisikletle gezilecek yerler" toplantısı (3 katılımcı)
Eksiklikler
Yukarıdaki bilgiVer işlevi her ne kadar kullanım kolaylığı getirse de, bu yöntemin bazı eksiklikleri vardır:
bilgiVerişlevi yalnızcastdout'a yazdığı için fazla kullanışlı değildir. Oysa örneğinFiletüründen bir dosyaya da yazabiliyor olsa, kullanışlılığı artardı. Bunu sağlamanın yolu, çıktının yazdırılacağı akımı da işleve bir parametre olarak vermektir:void bilgiVer(File akım, GününSaati zaman) { akım.writef("%02s:%02s", zaman.saat, zaman.dakika); }
O sayede
GününSaatinesnelerini istersekstdout'a, istersek de bir dosyaya yazdırabiliriz:bilgiVer(stdout, kahvaltıZamanı); auto dosya = File("bir_dosya", "w"); bilgiVer(dosya, kahvaltıZamanı);
Not:
stdin,stdout, vestderrnesnelerinin türleri de aslındaFile'dır.- Daha önemlisi,
bilgiVergibi bir işlev, yapı nesnelerini temel türler kadar rahatça kullanabilmemiz için yeterli değildir. Temel türlerden alışık olduğumuz rahatlık yoktur:writeln(kahvaltıZamanı); // Kullanışsız: Genel düzende yazarO kod çalıştırıldığında
GününSaatitürünün ismi ve üyelerinin değerleri programa uygun biçimde değil, genel bir düzende yazdırılır:GününSaati(7, 0)
Bunun yerine, yapı nesnesinin değerini örneğin
"12:34"biçiminde birstring'e dönüştürebilen bir işlev olması çok daha yararlı olur. Yapı nesnelerinin de otomatik olarakstring'e dönüştürülebileceklerini bundan sonraki bölümde göstereceğim.
Problem
bilgiVer işlevini şu iki yapı için de yükleyin:
struct Yemek { GününSaati zaman; string adres; } struct GünlükPlan { Toplantı sabahToplantısı; Yemek öğleYemeği; Toplantı akşamToplantısı; }
Yemek yapısı yalnızca başlangıç zamanını barındırdığı için; onun bitiş zamanını başlangıç zamanından bir buçuk saat sonrası olarak belirleyin. Bu işlem için yapılar bölümünde tanımladığımız zamanEkle işlevi yararlı olabilir:
GününSaati zamanEkle(GününSaati başlangıç,
GününSaati eklenecek) {
GününSaati sonuç;
sonuç.dakika = başlangıç.dakika + eklenecek.dakika;
sonuç.saat = başlangıç.saat + eklenecek.saat;
sonuç.saat += sonuç.dakika / 60;
sonuç.dakika %= 60;
sonuç.saat %= 24;
return sonuç;
}
Yemek bitiş zamanları o işlev yardımıyla hesaplanınca GünlükPlan nesneleri çıkışa şuna benzer şekilde yazdırılabilirler:
10:30-11:45 "Bisiklet gezisi" toplantısı (4 katılımcı) 12:30-14:00 Yemek, Yer: Taksim 15:30-17:30 "Bütçe" toplantısı (8 katılımcı)
Kitaplar
Forum
Tanıtım
İletişim
Hakları