D Diline Genel Bakış
Bu bölümde D'nin ilginç ve önemli olanaklarından bazılarını bulabilirsiniz. Bu listenin kaynağı Digital Mars'ın sitesindeki D tanıtım sayfasıdır.
Sınıflar
D'de nesne yönelimli programlamanın temelini sınıflar oluşturuyor. Bu konuda C++'dan oldukça köklü farkları var: Arayüz [interface] türetirken çoklu kalıtım, ama gerçekleştirme [implementation] türetirken tekli kalıtım... Bütün sıradüzenin en tepesinde Object
temel arayüzü bulunuyor. Nesneler C ve C++'nın tersine referans olarak oluşturuluyorlar.
İşleç yükleme [operator overloading]
İşleç yükleme konusunda aslında bir fark yok: Başka nesne yönelimli dillerde olduğu gibi D'de de işleçler her tür için özel olarak tanımlanabiliyor.
Modüller
Her kaynak dosya, içerisinde bulunduğu klasöre göre bir modülün parçası olarak kabul ediliyor. Bildirimleri derleyiciye tanıtmak için #include
etmek yerine, modüller import
ediliyorlar. Böylelikle ne başlık dosyalarına gerek kalıyor ne de programcılığın özüyle ilgisi olmayan #ifndef
ve #pragma once
gibi çözümlere...
Bildirimler gereksiz
C'de ve C++'da anahtar kelimeler dışındaki bütün isimlerin önceden bildirilmiş olmaları gerekir. printf
bile <stdio.h>
başlığı eklenmeden kullanılamaz. (Aslında bazı derleyiciler buna izin verirler ama standart değildir.) Bu yüzden programcılar sürekli olarak .h
başlık dosyaları ile .c
kod dosyalarını uyumlu tutmaya çabalarlar. Bu hem hataya açık hem de gereksiz bir külfettir. D'de ise bu iş derleyicinin görevidir:
import std.stdio; void main() { ismi_henüz_görülmemiş_bir_fonksiyon(); } void ismi_henüz_görülmemiş_bir_fonksiyon() { auto nesne = new İsmiHenüzGörülmemişBirSınıf; nesne.selam_ver(); } class İsmiHenüzGörülmemişBirSınıf { void selam_ver() { writeln("Merhaba!"); } }
Şablonlar [templates]
Ne kadar güçlü olsalar da; C++'nın şablonları hem büyük uzmanlık gerektirir, hem de kullanımları bazı durumlarda çok sorunludur. C++ şablonlarının en büyük uzmanlarından birisi Andrei Alexandrescu'dur. Kendisi şablonlarla tasarımlarının ötesinde işler becermiş ve bunları Modern C++ Design kitabında tanıttığı Loki kütüphanesinde göstermiştir. C++'da gördüğü eksiklikleri D'ye taşımış ve şablonları D'de çok daha kullanışlı bir hale getirmiştir.
Çağrışımlı diziler [associative arrays]
Başka dillerde de bulunan ve C++'da std::map
ile sunulan çağrışımlı diziler D'de dilin iç olanaklarına dahil ve hızlı eşleme tablosu [hash table] olarak gerçekleştirilmişler.
Gerçek anlamda typedef
'ler
C'de ve C++'da typedef
yeni tür oluşturmaz, olan bir türe daha kullanışlı bir isim verir. D'de ise yeni bir tür oluşur. Sonuçta,
typedef int ÖğrenciNumarası;
ile oluşturulan ÖğrenciNumarası
türü ile onun temel aldığı int
farklıdırlar. Örneğin fonksiyonlar bu türler için ayrı ayrı yüklenebilirler:
int yazdır(int sayı); int yazdır(ÖğrenciNumarası öğrenci);
Belgeleme
Kaynak kodlar geleneksel olarak; JavaDoc, Doxygen, vs. gibi araçlarla açıklama satırlarında belgelenir. Örneğin fonksiyon parametrelerinin ve dönüş değerlerinin ne anlama geldikleri ve nasıl kullanıldıkları, fonksiyonların başlarındaki /**
ve */
ile belirlenen açıklama bölümlerine yazılırlar. D'de belgeleme işi de derleyicinin görevidir; ayrıca program kullanmaya gerek yoktur. Dahası, belgeleme salt kod ile sınırlı değildir; okumakta olduğunuz bu sayfalar bile Ddoc ile oluşturulmuşlardır.
Kapsam fonksiyonları
Fonksiyonlar başka fonksiyonların içinde tanımlanabilirler.
Fonksiyon sabitleri [function literals]
Fonksiyonlar kullanıldıkları ifadeler içinde isimsiz olarak tanımlanabilirler. [Not: Fonksiyonlar zaten sabit oldukları halde burada literal'ı string literal'a benzeterek sabit olarak çeviriyorum.]
// Burada üçüncü parametre bir fonksiyon sabiti foo(3, 10, (int sayı){ return sayı * sayı; } ); // Parametre almayan bir fonksiyon sabiti bar(3, 10, { return 5; } );
Kapamalar [closures]
Kapsam fonksiyonlarına ve sınıf üye fonksiyonlarına kapama ([delegate] de denir) olarak referans verebiliriz. Bu özellik türden bağımsız programlamayı kolaylaştırır ve daha güvenli hale getirir.
Parametre türleri
Parametreler kullanımlarına göre açıkça giriş, çıkış, veya giriş-çıkış olarak belirtilebilirler. C++'da parametrelerin kullanımları biraz anlaşmaya bağlıdır: giriş parametreleri değer olarak veya const
referans olarak geçirilirler; çıkış ve giriş-çıkış parametreleri ise işaretçi veya referans olarak geçirilirler. Bu konu D'de açıktır.
Diziler
C dizilerinin C++'ya da taşınmış olan bir çok sorunu vardır:
- C dizileri kendi uzunluklarından habersizdirler, çünkü dizi kavramı bir işaretçi ve onun kullanımıyla ilgili anlaşmalarla belirlenmiştir.
- C dizileri ikinci sınıf vatandaştırlar. Örneğin bir fonksiyona geçirilirken ilk elemanlarını gösteren işaretçiye dönüşürler. Bu dönüşüm sonucunda da, olası boyut bilgisi kaybedilmiş olur.
- C dizilerinin boyutları değiştirilemez.
- Uzunluk bilgisi taşımadıkları için dizi dışına taşma gibi yasal olan kullanımlar denetlenemez.
- Söz dizimi gereği dizi uzunluğu en sonda belirlendiği için, örneğin dizi işaretçilerinin tanımı çok gariptir:
int (*dizi_işaretçisi)[10];
D'de ise söz dizimi doğaldır:int[10]* dizi_işaretçisi; // 10 uzunlukta int dizisi // işaretçisi long[] fonksiyon(int x); // long dizisi döndüren // fonksiyon
D, dizilerin bu sorunlarını çözer.
Dizgiler [strings]
Dizgiler programlama dilinden tam destek almayı gerektirecek kadar yaygın ve yararlı veri yapılarıdır. Modern dillerde olduğu gibi D'de de dizgiler C ve C++'dan farklı olarak doğrudan destek görürler.
Otomatik bellek yönetimi
D'de bellek yönetimi çöp toplamalıdır [garbage collected]. Bellek yönetimi konusundaki gözlemler, çöp toplama olanağının program geliştirmeyi çok çabuklaştırdığını ve çok daha kolaylaştırdığını gösteriyor. Ortaya çıkan program da çoğu durumda daha hızlı çalışıyor.
Elle bellek yönetimi
D çöp toplamalı olsa da, new
ve delete
işleçleri her sınıf için ayrı olarak yüklenebilir.
RAII
Resource Acquisition Is Initialization'ın kısaltması olan RAII, kaynakların bozucu fonksiyonlarda geri verilmesi yöntemidir. Hata atılan durumlarda bile kaynak sızıntılarını önler. D RAII'yi çöp toplamadan ayrı olarak ve ona ek olarak destekler.
Etkin yapılar
C'deki struct
gibi alt düzey yapılar D'de de desteklenir. Bunlar hem C fonksiyonlarıyla etkileşmek için, hem de etkin veri yapıları oluşturmak için yararlıdırlar.
Makine dili [assembly]
C'de ve C++'da olduğu gibi, D'de de gereken özel durumlarda doğrudan makine dilinde kod yazılabilir.
Sözleşmeli programlama [contract programming]
Eiffel dilinin yaratıcısı Bertrand Meyer tarafından icat edilen sözleşmeli programlama, kodun doğruluğunu sağlama konusunda çok güçlü bir yöntemdir. Sözleşmeli programlama D'de fonksiyon giriş ve çıkış koşulları [precondition ve postcondition], sınıf değişmezleri, ve assert
'lerden oluşur.
Örneğin alan_
üyesinin değerinin her zaman için sıfırdan büyük olması beklenen bir Üçgen
sınıfında bu değişmezlik kavramı invariant
sözcüğü ile şöyle ifade edilebilir:
class Üçgen { double alan_; /* ... */ invariant() { /* Üçgen'in her üye fonksiyonunun her işletilişinden * sonra bu koşul denetlenecektir. */ assert(alan_ > 0.0); } }
Birim testleri [unit tests]
Birim testleri başka dillerde dilin dışında düzeneklerle halledilirler. Örneğin C++ için CppUnit, unittest++, vs. gibi bir birim test düzeneği genellikle make
veya benzeri oluşturma sisteminin parçası olacak şekilde ayarlanırlar ve asıl programın dışında olarak çalıştırılırlar.
D birim testleri biraz daha ileri götürmüş ve dil olanağı haline getirmiştir. Testler örneğin sınıflara eklenirler, ve istenirse program başlamadan önce otomatik olarak çalıştırılırlar. Böylece sınıfların değişmezlik koşullarındaki tutarsızlıklar birim testleri tarafından en kısa sürede farkedilmiş olurlar.
Hata ayıklama [debug] olanakları
D'de hata ayıklama olanakları dilin parçasıdır. Hata ayıklayıcı kodlar makrolara gerek bırakmadan, derleyici düzeyinde etkin hale getirilebilirler. Böylece programın hem hata ayıklama sürümü [debug], hem de asıl sürümü [release] aynı kod tarafından oluşturulabilir.
Hata atma [exceptions]
D, try-catch'den daha yararlı olduğu görülen try-catch-finally modelini kullanır. Böylece küçük kaynak temizlikleri için bile C++'da olduğu gibi ayrıca sınıf yazmak gerekmemiş olur.
Çoklu iş parçacıkları [multi-threads]
D bu konuya dil düzeyinde çözüm getirir. Sınıf ve fonksiyon düzeyinde koruma sağlar. Örneğin synchronized
olarak belirtilen fonksiyonların tek bir anda yalnızca tek bir iş parçacığı tarafından işletilmeleri dil tarafından sağlanır.
synchronized int fonksiyon() { /* Bu kod belirli bir anda tek bir iş parçacığı * tarafından işletilir */ }
Sağlam kodlama yöntemleri
D, kod doğruluğunu ve güvenliğini arttıran bazı olanaklar getirir:
- İşaretçilere duyulan ihtiyaç büyük ölçüde giderilmiştir: dinamik diziler, referans değişkenler, referans nesneler, vs.
- Çöp toplamalı bellek yönetimi
- Çoklu iş parçacıklarına dil desteği
- Kodda beklenmeyen değişiklikler yapan makrolar yok
- Makroların bazı görevlerinin yerine iç fonksiyonlar [inline functions].
- C ve C++'dakinin tersine, temel türlerin büyüklükleri açıkça belirtilebilir (o dillerde örneğin
int
'in kaç bit olduğu bile belli değildir) - C ve C++'dakinin tersine,
char
türünün işaretiyle ilgili belirsizlik yoktur (o dillerdechar
üçüncü bir tür olarak yasigned char
'ın ya daunsigned char
'ın eşdeğeridir ama hangisinin eşdeğeri olduğu belirsizdir) - Bildirimlerin başlıklarda ve kod dosyalarında ayrı ayrı ve tutarlı olarak yazılmaları gerekmez
- Hata ayıklayıcı kodu ekleyebilmek için ayrıştırma [parsing] desteği
Derleme zamanı denetimleri
- Kuvvetli tür denetimi
- Tek bir
;
karakteri içeren boşfor
döngülerine izin verilmez - Atamalar Bool değere dönüşmezler
- Eski ve geçersiz arayüz fonksiyonlarına izin verilmez
Çalışma zamanı denetimleri
assert()
ifadeleri- Dizi dışına taşmaya karşı denetim
switch
ifadelerinde yanlışcase
denetimi- Bellek yetersizliği durumlarında atılan hata
- Fonksiyon giriş ve çıkış koşulları ve sınıf değişmezleri yoluyla sözleşmeli programlama desteği
İşleç öncelikleri
Bu konudaki sürprizleri ve hataları engellemek için D C'deki işleç önceliklerini kullanır.
C fonksiyonlarına doğrudan destek
D'ni C'ninkilere bire bir karşılık gelen veri yapıları da destekleridiği için, C kütüphane fonksiyonları hiçbir dönüşüm gerekmeden doğrudan çağrılabilirler.
Bütün C türlerine destek
C'deki struct
'lar, union
'lar, enum
'lar, işaretçiler, C99'un getirdiği bütün türler desteklenir. Hatta struct
üyelerinin bellek sıralanma düzenleri [alignment] bile belirlenebilir.
İşletim sistemi hataları
D'nin hata yakalama düzeneği işletim sistemi tarafından atılan hatalarla da uyumludur.
Araç programlarla uyumluluk
D standart program parçası [object file] formatında kod üretir. Bu sayede; hata ayıklayıcı, bağlayıcı, vs. mevcut araç programların D programları ile kullanılmaları da sağlanmış olur.
Kod sürümleri
D, bir program kodundan birden fazla sürüm üretilmesine destek verir. Böylelikle #if
/#endif
gibi yöntemlere gerek kalmamış olur.
Emekliye ayrılan kod
Çeşitli nedenlerle artık kullanılmaması gereken kodlar eski oldukları şeklinde işaretlenebilirler ve böylece yeni kodlarda kullanılmaları önlenebilir ama gerekirse eski programlarda kullanılmalarına izin verilebilir.