D Programlama Dili – Programlama dersleri ve D referansı
Ali Çehreli

açıkça elle yapılan: [explicit], programcı tarafından açık olarak yapılan
otomatik: [implicit], derleyici tarafından otomatik olarak yapılan
tür dönüşümü: [type conversion], bir değeri kullanarak başka bir türden değer elde etmek
yükleme: [overloading], aynı isimde birden çok işlev tanımlama
... bütün sözlük



İngilizce Kaynaklar


Diğer




İş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(in double sayı) {
    writeln("Kesirli sayı: ", sayı);
}

void bilgiVer(in int sayı) {
    writeln("Tamsayı     : ", sayı);
}

void bilgiVer(in char[] 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ı(in real değer) {
    return 7 * değer;
}

double yediKatı(in 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ı(in 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:

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(in 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(in 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:

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(in GününSaati başlangıç,
                     in 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ı)