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

arayüz: [interface], yapının, sınıfın, veya modülün sunduğu işlevler
geçici: [temporary], bir işlem için geçici olarak oluşturulan ve yaşamı kısa süren değişken veya nesne
ifade: [expression], programın değer oluşturan veya yan etki üreten bir bölümü
isim gizleme: [name hiding], üst sınıfın aynı isimli üyelerinin alt sınıftakiler tarafından gizlenmeleri
kalıtım: [inheritance], başka bir türün üyelerini türeme yoluyla edinmek
şablon: [template], derleyicinin örneğin 'türden bağımsız programlama' için kod üretme düzeneği
takma isim: [alias], var olan bir olanağın başka bir ismi
... bütün sözlük



İngilizce Kaynaklar


Diğer




alias ve with

alias

alias anahtar sözcüğü programda geçen isimlere takma isim vermek için kullanılır. alias, farklı bir olanak olan alias this ile karıştırılmamalıdır.

Uzun bir ismi kısaltmak

Önceki bölümde gördüğümüz şablonlarda olduğu gibi, programda geçen bazı isimler kullanışsız derecede uzun olabilirler. Daha önce tanımladığımız şu işlevi hatırlayalım:

Yığın!(Nokta!double) rasgeleNoktalar(int adet) {
    auto noktalar = new Yığın!(Nokta!double);

    // ...
}

Programda açıkça Yığın!(Nokta!double) yazmanın bir kaç sakıncası görülebilir:

Bu sakıncalar Yığın!(Nokta!double) ismine tek noktada yeni bir isim vererek giderilebilir:

alias Noktalar = Yığın!(Nokta!double);

// ...

Noktalar rasgeleNoktalar(int adet) {
    auto noktalar = new Noktalar;

    // ...
}

Bir adım daha ileri giderek yukarıdaki alias'ı iki parça halinde de tanımlayabiliriz:

alias HassasNokta = Nokta!double;
alias Noktalar = Yığın!HassasNokta;

alias'ın söz dizimi şöyledir:

    alias takma_isim = var_olan_isim;

O tanımdan sonra takma isim daha önceden var olan ismin eşdeğeri haline gelir ve artık aynı biçimde kullanılır.

Bazı D programlarında bu olanağın eski söz dizimine de rastlayabilirsiniz:

    // Eski söz dizimini kullanmaya gerek yok:
    alias var_olan_isim takma_isim;

Türlerin isimlerini modülleriyle birlikte uzun uzun yazmak yerine de alias'tan yararlanabiliriz. Örneğin okul ve firma isimli iki modülde Müdür isminde iki farklı tür tanımlı olduğunu varsayalım. Bu iki modülün de programa eklendikleri bir durumda yalnızca Müdür yazıldığında program derlenemez:

import okul;
import firma;

// ...

    Müdür kişi;             // ← derleme HATASI

Derleyici hangi Müdür türünü kasdettiğimizi anlayamaz:

Error: okul.Müdür at [...]/okul.d(1) conflicts with
firma.Müdür at [...]/firma.d(1)

Bunun önüne geçmenin bir yolu, programda kullanmak istediğimiz Müdür'e bir takma isim vermektir. Böylece her seferinde modülüyle birlikte örneğin okul.Müdür yazmak zorunda kalmadan birden fazla yerde kullanabiliriz:

import okul;

alias OkulMüdürü = okul.Müdür;

void main() {
    OkulMüdürü kişi;

    // ...

    OkulMüdürü başkaKişi;
}

alias programdaki başka çeşit isimlerle de kullanılabilir. Aşağıdaki kod bir değişkene nasıl takma isim verildiğini gösteriyor:

    int uzunBirDeğişkenİsmi = 42;

    alias değişken = uzunBirDeğişkenİsmi;
    değişken = 43;

    assert(uzunBirDeğişkenİsmi == 43);
Tasarım esnekliği

Her ne kadar ileride değişmeyecek olduğundan emin bile olunsa, tasarımın esnek olması için int gibi temel türlere bile anlamlı yeni isimler verilebilir:

alias MüşteriNumarası = int;
alias Şirketİsmi = string;
// ...

struct Müşteri {
    MüşteriNumarası numara;
    Şirketİsmi şirket;
    // ...
}

Sırasıyla int'in ve string'in aynıları olsalar da, eğer o yapıyı kullanan kodlar her zaman için MüşteriNumarası ve Şirketİsmi yazarlarsa, yapı tanımında int veya string yerine başka bir tür kullanıldığında daha az satırda değişiklik gerekmiş olur.

Bu yöntem kodun anlaşılır olmasına da yardım eder. Bir değerin türünün int yerine MüşteriNumarası olması, kod okunurken o değerin anlamı konusunda hiçbir şüphe bırakmaz.

Bazı durumlarda böyle tür isimleri bir yapı veya sınıfın içinde de tanımlanabilir. Böylece o yapının veya sınıfın arayüzünde bu takma isimleriyle kullanılırlar. Örnek olarak ağırlık niteliğine sahip bir sınıfa bakalım:

class Kutu {
private:

    double ağırlık_;

public:

    double ağırlık() const {
        return ağırlık_;
    }

    // ...
}

Bu sınıfın üyesinin ve niteliğinin açıkça double yazılarak tanımlanmış olması kullanıcıların da ağırlığı double olarak kullanmalarına neden olacaktır:

    double toplamAğırlık = 0;

    foreach (kutu; kutular) {
        toplamAğırlık += kutu.ağırlık;
    }

Bunun karşıtı olarak, ağırlığın türünün sınıf içindeki bir alias ile tanımlandığı duruma bakalım:

class Kutu {
private:

    Ağırlık ağırlık_;

public:

    alias Ağırlık = double;

    Ağırlık ağırlık() const {
        return ağırlık_;
    }

    // ...
}

Kullanıcı kodu da sınıfın arayüzüne bağlı kalarak artık Ağırlık yazacaktır:

    Kutu.Ağırlık toplamAğırlık = 0;

    foreach (kutu; kutular) {
        toplamAğırlık += kutu.ağırlık;
    }

Kutu sınıfının tasarımcısı Ağırlık'ı daha sonradan başka şekilde tanımlarsa kodda değiştirilmesi gereken yerlerin sayısı bu sayede azalmış olur.

Üst sınıfın gizlenen isimlerini alt sınıfta görünür yapmak

Aynı ismin hem üst sınıfta hem de alt sınıfta bulunması isim çakışmasına neden olur. Alt sınıfta aynı isimde tek bir işlev bile bulunsa, üst sınıfın işlevlerinin isimleri gizlenirler ve alt sınıf arayüzünde görünmezler:

class GenelHesap {
    void hesapla(int x) {
        // ...
    }
}

class ÖzelHesap : GenelHesap {
    void hesapla() {
        // ...
    }
}

void main() {
    auto hesap = new ÖzelHesap;
    hesap.hesapla(42);            // ← derleme HATASI
}

O çağrıda 42 değeri kullanıldığından, ÖzelHesap nesnesinin kalıtım yoluyla edindiği ve int türünde parametre alan GenelHesap.hesapla işlevinin çağrılacağını bekleyebiliriz. Oysa, her ne kadar parametre listeleri farklı olsa da ÖzelHesap.hesapla işlevi, aynı isme sahip olduğu için GenelHesap.hesapla işlevini gizler ve program derlenmez.

Not: Üst sınıf işlevinin alt sınıfta değişik olarak yeniden tanımlanmasından bahsetmediğimize dikkat edin. Öyle olsaydı, Türeme bölümünde anlatıldığı gibi, parametre listesini üst sınıftakiyle aynı yapar ve override anahtar sözcüğünü kullanırdık. Burada, alt sınıfa eklenen yeni bir işlev isminin üst sınıftaki bir isimle aynı olduğu durumdan bahsediyoruz.

Derleyici, GenelHesap.hesapla'yı bu gizleme nedeniyle dikkate bile almaz ve ÖzelHesap.hesapla'nın bir int ile çağrılamayacağını belirten bir hata verir:

Error: function deneme.ÖzelHesap.hesapla () is not callable
using argument types (int)

Bunun geçerli bir nedeni vardır: İsim gizleme olmasa, ileride bu sınıflara eklenen veya onlardan çıkartılan hesapla işlevleri hiçbir uyarı verilmeden kodun istenenden farklı bir işlevi çağırmasına neden olabilirler. İsim gizleme, nesne yönelimli programlamayı destekleyen başka dillerde de bulunan ve bu tür hataları önleyen bir olanaktır.

Gizlenen isimlerin alt sınıf arayüzünde de görünmeleri istendiğinde yine alias'tan yararlanılır:

class GenelHesap {
    void hesapla(int x) {
        // ...
    }
}

class ÖzelHesap : GenelHesap {
    void hesapla() {
        // ...
    }

    alias hesapla = GenelHesap.hesapla;
}

Yukarıdaki alias, üst sınıftaki hesapla ismini alt sınıf arayüzüne getirir ve böylece gizlenmesini önlemiş olur.

O eklemeden sonra kod artık derlenir ve istenmiş olduğu gibi üst sınıfın hesapla işlevi çağrılır.

Eğer daha uygun olduğu düşünülürse, üst sınıfın işlevi farklı bir isimle bile görünür hale getirilebilir:

class GenelHesap {
    void hesapla(int x) {
        // ...
    }
}

class ÖzelHesap : GenelHesap {
    void hesapla() {
        // ...
    }

    alias genelHesapla = GenelHesap.hesapla;
}

void main() {
    auto hesap = new ÖzelHesap;
    hesap.genelHesapla(42);
}

İsim gizleme üye değişkenler için de geçerlidir. İstendiğinde onların alt sınıf arayüzünde görünmeleri de alias ile sağlanır.

class ÜstSınıf {
    int şehir;
}

class AltSınıf : ÜstSınıf {
    string şehir() const {
        return "Kayseri";
    }
}

Her ne kadar birisi üye değişken ve diğeri üye işlev olsa da, alt sınıftaki şehir, üst sınıfın aynı isimdeki üyesini gizler ve bu yüzden aşağıdaki kod derlenemez:

void main() {
    auto nesne = new AltSınıf;
    nesne.şehir = 42;             // ← derleme HATASI
}

Üst sınıfın üye değişkeni alias ile alt sınıf arayüzüne getirildiğinde kod artık derlenir. Aşağıdaki kod değişkenlerin de yeni isimle kullanılabileceklerini gösteriyor:

class ÜstSınıf {
    int şehir;
}

class AltSınıf : ÜstSınıf {
    string şehir() const {
        return "Kayseri";
    }

    alias şehirKodu = ÜstSınıf.şehir;
}

void main() {
    auto nesne = new AltSınıf;
    nesne.şehirKodu = 42;
}
with

with, bir nesnenin veya başka bir ismin tekrarlanmasını önler. Parantez içinde bir ifade veya isim alır ve kendi kapsamı içinde geçen isimleri belirlerken o ifade veya ismi de göz önünde bulundurur:

struct S {
    int i;
    int j;
}

void main() {
    auto s = S();

    with (s) {
        i = 1;    // s.i ile aynı anlamda
        j = 2;    // s.j ile aynı anlamda
    }
}

Parantez içinde geçici bir nesne de oluşturulabilir. Oluşturulan nesne bir sol değer haline gelir. Doğal olarak, bu geçici nesnenin yaşam süreci with kapsamı ile sınırlıdır:

    with (S()) {
        i = 1;    // geçici nesnenin i üyesi
        j = 2;    // geçici nesnenin j üyesi
    }

Daha sonra Göstergeler bölümünde göreceğimiz gibi, bu geçici nesnenin yaşamı new anahtar sözcüğü ile uzatılabilir.

with özellikle enum gibi tür isimlerinin case bloklarında tekrarlanmalarını önlemek için yararlıdır:

enum Renk { kırmızı, turuncu }

// ...

    final switch (r) with (Renk) {

    case kırmızı:    // Renk.kırmızı anlamında
        // ...

    case turuncu:    // Renk.turuncu anlamında
        // ...
    }
Özet