Sınıflar
Sınıflar, kullanıcı türleri tanımlamaya yarayan bir başka olanaktır.
D'nin nesneye yönelik programlama olanakları sınıflar yoluyla gerçekleştirilir. Nesneye yönelik programlamayı üç temel kavram üzerinde düşünebiliriz:
- sarma: üyelere erişimin kısıtlanması (aslında yapılarda da bulunan bu olanağı genelde yapıların kullanım amaçlarının dışında kaldığı için göstermedim)
- kalıtım: başka bir türün üyelerini ve üye işlevlerini kendisininmiş gibi edinmek
- çok şekillilik: birbirlerine yakın türlerin daha genel ortak bir tür gibi kullanılabilmeleri
Sarma, daha sonra göreceğimiz erişim hakları ile sağlanır. Kalıtım, gerçekleştirme türemesidir. Çok şekillilik ise arayüz türemesi yoluyla gerçekleştirilir.
Bu derste sınıfları genel olarak tanıtacağım ve özellikle referans türü olduklarına dikkat çekeceğim. Sınıfların diğer olanaklarını daha sonraki derslere bırakacağım.
Yapılarla karşılaştırılması
Sınıflar yapılara temelde çok benzerler. Bu yüzden daha önce şu derslerde yapılar üzerinde gördüğümüz hemen hemen her konu sınıflar için de geçerlidir:
- Yapılar
- Üye İşlevler
const refParametreler veconstÜye İşlevler- Kurucu ve Diğer Özel İşlevler
- İşleç Yükleme
Sınıfları yapılardan ayıran önemli farklar da vardır. Bu farkları aşağıdaki bölümlerde anlatıyorum.
Referans türleridir
Sınıfların yapılardan farklı olmalarının en büyük nedeni, yapıların değer türü olmalarına karşın sınıfların referans türü olmalarıdır. Aşağıdaki farklılıkların büyük bir çoğunluğu, sınıfların bu özelliğinden kaynaklanır.
new ile kurulurlar
Sınıf değişkenlerinin kendileri değer taşımadıkları için, asıl nesne new anahtar sözcüğü ile oluşturulur. Aynı nedenden, null ve is dersinde de gösterildiği gibi, sınıf değişkenleri null da olabilirler. Yani, "hiçbir nesneye erişim sağlamıyor" olabilirler.
Hatırlayacağınız gibi; bir değişkenin null olup olmadığını == işleciyle değil, is işleciyle denetlememiz gerekir:
BirSınıf erişimSağlayan = new BirSınıf; BirSınıf değişken; // erişim sağlamayan assert(değişken is null);
Bunun nedeni == işlecinin nesnenin üyelerini de kullanmasının gerekebileceğidir. O üye erişimi, değişkenin null olduğu durumda programın bir bellek hatası ile sonlanmasına neden olur. O yüzden sınıf değişkenlerinin is ile karşılaştırılmaları gerekir.
Sınıf nesneleri ve değişkenleri
Sınıf nesnesi ile sınıf değişkeni farklı kavramlardır.
Sınıf nesnesi, new anahtar sözcüğü ile oluşturulan ve kendi ismi olmayan bir program yapısıdır. Temsil ettiği kavramı gerçekleştiren, onun işlemlerini yapan, ve o türün davranışını belirleyen hep bu sınıf nesnesidir. Sınıf nesnelerine doğrudan erişemeyiz.
Sınıf değişkeni ise sınıf nesnesine erişim sağlayan bir program yapısıdır. Kendisi iş yapmasa da eriştirdiği nesnenin aracısı gibi işlem görür.
Daha önce Değerler ve Referanslar dersinde gördüğümüz şu koda bakalım:
auto değişken1 = new BirSınıf; auto değişken2 = değişken1;
İlk satırda sağ taraftaki new, isimsiz bir BirSınıf nesnesi oluşturur. değişken1 ve değişken2 ise yalnızca bu isimsiz nesneye erişim sağlayan değişkenlerdir:
(isimsiz BirSınıf nesnesi) değişken1 değişken2
---+-------------------+--- ---+---+--- ---+---+---
| ... | | o | | o |
---+-------------------+--- ---+-|-+--- ---+-|-+---
▲ | |
| | |
+--------------------+------------+
Kopyalama
Değişkenleri etkiler.
Referans türü oldukları için; sınıf değişkenlerinin kopyalanarak oluşturulmaları, onların hangi nesneye erişim sağlayacaklarını belirler. Bu işlem sırasında asıl nesne kopyalanmaz.
Bir nesne kopyalanmadığı için de, yapılarda kopya sonrası işlevi olarak öğrendiğimiz this(this) üye işlevi sınıflarda bulunmaz.
auto değişken2 = değişken1;
Yukarıdaki kodda değişken2, değişken1'in kopyası olarak oluşturulmaktadır. O işlem her ikisinin de aynı nesneye erişim sağlamalarına neden olur.
Sınıf nesnelerinin kopyalanmaları gerektiğinde bunu sağlayan bir üye işlev tanımlanmalıdır. Bu işleve kopyala() gibi bir isim verebileceğiniz gibi, dizilere benzemesi açısından dup() isminin daha uygun olduğunu düşünebilirsiniz. Bu işlev yeni bir nesne oluşturmalı ve ona erişim sağlayan bir değişken döndürmelidir:
class Sınıf { Yapı yapıNesnesi; dchar[] dizgi; int tamsayı; // ... this(Yapı yapıNesnesi, dchar[] dizgi, int tamsayı) { this.yapıNesnesi = yapıNesnesi; this.dizgi = dizgi; this.tamsayı = tamsayı; } Sınıf dup() { return new Sınıf(yapıNesnesi, dizgi.dup, tamsayı); } }
dup() içinde oluşturulan yeni nesne için yalnızca dizgi üyesinin dup() ile açıkça kopyalandığına dikkat edin. yapıNesnesi ve tamsayı üyeleri ise değer türleri olduklarından onlar zaten otomatik olarak kopyalanırlar.
O işlevden örneğin şöyle yararlanılabilir:
auto nesne1 = new Sınıf(Yapı(1.5), "merhaba"d.dup, 42); auto nesne2 = nesne1.dup();
Sonuçta, nesne2 nesne1'in hiçbir üyesini paylaşmayan ayrı bir nesnedir.
Atama
Değişkenleri etkiler.
Referans türü oldukları için; sınıf değişkenlerinin atanmaları, daha önce erişim sağladıkları nesneyi bırakmalarına ve yeni bir nesneye erişim sağlamalarına neden olur.
Eğer bırakılan nesneye erişim sağlayan başka değişken yoksa, asıl nesne ilerideki belirsiz bir zamanda çöp toplayıcı tarafından sonlandırılacak demektir.
auto değişken1 = new BirSınıf; auto değişken2 = new BirSınıf; değişken1 = değişken2;
Yukarıdaki son atama işlemi, değişken1'in kendi nesnesini bırakmasına ve değişken2'nin nesnesine erişim sağlamaya başlamasına neden olur. Bırakılan nesne daha sonra çöp toplayıcı tarafından sonlandırılacaktır.
Atama işleminin davranışı sınıflar için değiştirilemez; yani opAssign sınıflarda yüklenemez.
Tanımlama
struct yerine class anahtar sözcüğü kullanılır:
class SatrançTaşı { // ... }
Kurma
Kurucu işlevin ismi, yapılarda olduğu gibi this'tir. Yapılardan farklı olarak sınıf nesneleri {} karakterleri ile kurulamaz.
class SatrançTaşı { dchar şekil; this(dchar şekil) { this.şekil = şekil; } }
Eğer sınıf başka bir sınıftan türetilmişse, türetildiği üst sınıfın kurucusunu çağırmak bu sınıfın görevidir. Bunu bir sonraki derste super anahtar sözcüğünü tanıtırken göstereceğim.
Sonlandırma
Sonlandırıcı işlevin ismi yapılarda olduğu gibi ~this'tir:
~this() { // ... nesne sonlanırken gereken işlemler ... }
Üye erişimi
Yapılarda olduğu gibi, üyelere nokta karakteri ile erişilir.
auto şah = new SatrançTaşı('♔'); writeln(şah.şekil);
Her ne kadar değişkenin bir üyesine erişiliyor gibi yazılsa da; erişilen, asıl nesnenin üyesidir.
Not: Üye değişkenlere böyle doğrudan erişilmesi çoğu durumda doğru kabul edilmez. Onun yerine daha sonra anlatacağım sınıf niteliklerinden yararlanmak daha uygundur.
İşleç yükleme
Yapılardaki gibidir.
Bir fark, opAssign'ın sınıflar için özel olarak tanımlanamamasıdır. Yukarıda atama başlığında anlatıldığı gibi, sınıflarda atama işleminin anlamı yeni nesneye erişim sağlamaktır; bu anlam değiştirilemez.
Üye işlevler
Yapılardaki gibidir.
Bir fark, bazı işlevlerin Object sınıfından kalıtım yoluyla hazır olarak edinilmiş olmalarıdır. Örneğin toString işlevini kendimiz tanımlamamış olsak bile kullanabiliriz:
writefln("%s", şah);
Hatırlarsanız, bir nesnenin "%s" düzeniyle yazdırılması, onun toString üye işlevini çağırıyordu. Yukarıdaki SatrançTaşı sınıfında böyle bir işlev tanımlı olmadığı halde o satır derlenir ve bir çıktı üretir:
deneme.SatrançTaşı
Hemen hemen bütün durumlarda kullanışsız olan bu çıktının override sözcüğü ile nasıl değiştirildiğini bir sonraki derste göstereceğim.
is ve !is işleçleri
Sınıf değişkenleri üzerinde işler.
is işleci, sınıf değişkenlerinin aynı nesneye erişim sağlayıp sağlamadıklarını bildirir. İki değişken de aynı nesneye erişim sağlıyorlarsa true, değilse false değerini üretir. !is işleci de bunun tersi olarak işler: Aynı nesneye erişim sağlıyorlarsa false, değilse true değerini üretir.
auto benimŞah = new SatrançTaşı('♔'); auto seninŞah = new SatrançTaşı('♔'); assert(benimŞah !is seninŞah);
Yukarıdaki koddaki benimŞah ve seninŞah değişkenleri new ile oluşturulmuş olan iki farklı nesneye erişim sağladıkları için !is'in sonucu true'dur. Bu iki nesnenin aynı şekilde kurulmuş olmaları, yani ikisinin şekil üyelerinin de '♔' olması bunu değiştirmez; nesneler birbirlerinden ayrı iki nesnedir.
İki değişkenin aynı nesneye erişim sağladıkları durumda ise is işleci true üretir:
auto benimŞah2 = benimŞah; assert(benimŞah2 is benimŞah);
Yukarıdaki atama işlemi sonucunda iki değişken de aynı nesneye erişim sağlamaya başlarlar. is işleci bu durumda true üretir.
Özet
- Sınıfların yapılarla çok sayıda ortak yanları olduğu kadar büyük farkları da vardır.
- Sınıflar referans türleridir;
newile isimsiz bir sınıf nesnesi kurulur; döndürülen, o nesneye erişim sağlayan bir sınıf değişkenidir. - Hiçbir nesneye erişim sağlamayan sınıf değişkenlerinin değeri
null'dır; bu durumisveya!isile denetlenir (==veya!=ile değil). - Kopyalama normalde değişkeni kopyalar; nesnenin kopyalanabilmesi için
dup()gibi bir üye işlev yazılması gerekir. - Atama, değişkenin başka bir nesneyi göstermesini sağlar; bu davranış değiştirilemez.
D.ershane
Forum
Wiki
Projeler
Tanıtım
İletişim
Hakları