D.ershane D Programlama Dili Dersleri

arayüz: [interface], yapının, sınıfın, veya modülün sunduğu işlevler
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], türün başka bir ismi
... bütün sözlük

Bölümler
İngilizce Kaynaklar
Diğer



alias ve alias this

Not: alias ve alias this ayrı bölümlerde anlatılacak kadar bağımsız olanaklardır. Yine de ileride daha uygun bir ders olmadığını bildiğimden, isim benzerliği nedeniyle ve çok kısa olduğu için alias this'i de burada anlatmaya karar verdim.

alias

alias anahtar sözcüğü, programda geçen bir isme yeni bir takma isim vermek için kullanılır. Takma isimler vermenin çeşitli yararları vardır.

Uzun bir ismi kısaltmak

Önceki derste 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) rastgeleNoktalar(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 bir yerde yeni bir isim vererek giderilebilir:

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

// ...

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

    // ...
}

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

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

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

alias var_olan_isim takma_isim;

Takma isim ve daha önceden var olan isim birbirinin eşdeğeridir ve aynı şekilde kullanılır.

Daha önceki derslerde de bir kaç kere karşılaştığımız gibi, türlerin isimlerini modülleriyle birlikte uzun uzun yazmamız gerekebilir. O durumlarda da alias'tan yararlanabiliriz:

import std.stdio;
import std.stream;

void main()
{
    File dosya;                   // ← derleme HATASI
}

Hem std.stdio modülünde, hem de std.stream modülünde File isminde bir tanım bulunduğu için derleyici hangisini kasdettiğimizi anlayamaz:

Error: std.stdio.File at [...]/std/stdio.d(237) conflicts with
std.stream.File at [...]/std/stream.d(1787)

Bunun önüne geçmek için, programda kullanmak istediğimiz File'a bir takma isim verebiliriz. Böylece her seferinde uzun uzun örneğin std.stream.File yazmak zorunda kalmadan birden fazla yerde kullanabiliriz:

import std.stdio;
import std.stream;

alias std.stream.File Dosya;

void main()
{
    Dosya dosya;

    // ...

    Dosya başkaDosya;
}
Tasarım esnekliği

Bazı durumlarda, 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 int    MüşteriNumarası;
alias string Şirketİsmi;
// ...

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

Her ne kadar sırasıyla int'in ve string'in aynısı olsalar da, eğer o yapının kullanıcıları 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:

    @property 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ı, onun kullanıcılarının da ağırlığı double olarak kullanmalarını 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ığı düşünürsek:

class Kutu
{
private:

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

public:

    alias double Ağırlık;

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

    // ...
}

Kullanıcı kodu da doğrudan double yazmak yerine, sınıfın arayüzüne bağlı kalarak Ağırlık yazacaktır:

// ...

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

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

Bu sayede, 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ı da az olur.

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

Üst sınıfta da aynı isimde bulunan isimler, alt sınıfta görünmezler. 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ığı için, ÖzelHesap nesnesinin kalıtım yoluyla edindiği 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: Burada üst sınıfın işlevini alt sınıfta değişik olarak yeniden tanımlamaktan bahsetmediğimize dikkat edin. Öyle olsaydı, Türeme dersinde 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 durumla ilgileniyoruz.

Derleyici, GenelHesap.hesapla'yı bu gizleme nedeniyle hiç dikkate bile almadığı için Ö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, nesneye yönelik 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 görünmeleri de alias ile sağlanır:

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

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

    alias GenelHesap.hesapla 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 beklendiği 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 GenelHesap.hesapla genelHesapla;
}

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, 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. Yeni bir isimle getiren bir örnek:

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

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

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

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

Başka bağlamlarda kendi özel anlamları bulunan alias ve this anahtar sözcükleri, bir arada kullanıldıklarında tamamen farklı bir anlamdadırlar. Bu yüzden, ikisi bir arada kullanıldığında tek bir anahtar sözcük olarak kabul edilmelidir.

alias this, bir yapının veya sınıfın otomatik tür dönüşümü yoluyla başka bir tür yerine kullanılabilmesini sağlar. Bu iki sözcüğün arasına yapının veya sınıfın bir üyesi yazılır:

struct Para
{
    long lira;
    long kuruş;

    alias lira this;
}

O tanım, Para nesnelerinin otomatik olarak lira'nın türü olan long yerine kullanılabilmelerini sağlar:

void main()
{
    auto miktar = Para(10, 25);
    long kuruşsuz = miktar; // ← derlenir, ve otomatik olarak
                            //   long'a dönüşür
    assert(kuruşsuz == 10);
}

Yukarıdaki koddaki Para nesnesi; lira üyesinin türü olan long yerine, ve onun değeri ile kullanılabilmiştir.

Bir yapı veya sınıf tanımında birden fazla alias this bulunamaz.

Tür dönüşümü için başka bir seçenek de opCast işlecidir.