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:
- Okumayı güçleştirecek derecede karmaşıktır.
- Onun bir yığın veri yapısı olduğunun ve elemanlarının
Nokta
şablonunundouble
türü ile kullanılmalarından oluştuğunun her noktada görülmesi gereksiz bir bilgi olarak kabul edilebilir. - Programın ihtiyaçlarının değişmesi durumunda örneğin
double
yerine artıkreal
kullanılması gerektiğinde, veya yığın veri yapısı yerine bir ikili ağaç veri yapısı gerektiğinde, türün açıkça yazıldığı her yerde değişiklik yapılması gerekecektir.
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
alias
var olan isimlere takma isimler verir.with
aynı nesnenin veya ismin tekrarlanmasını önler.