bileşik türler (aggregates)

Bu yazı dizisinde C++ dili açısından önem taşıyan bazı terimleri ele alacağım. Bu terimlerden ilki “aggregate”. Bu terimi türkçeye, daha iyi bir karşılık buluncaya kadar “bileşik tür” ya da yalnızca “bileşik” olarak çevireceğim.
C++11 standartları aggregate terimini şöyle tanımlıyor:
Bir “bileşik tür”, bir dizi (array) türü ya da  programcı tarafından sağlanmış (user defined) bir kurucu işlevi olmayan, tüm statik olmayan veri öğeleri public olan, sınıf içinde ilk değer alan statik olmayan bir veri elemanına sahip olmayan, bir taban sınıfı olmayan ve bir sanal işleve sahip olmayan bir sınıf türüdür. Başlangıçta karmaşık gelebilecek bu tanımı tam olarak anlayabilmek için daha küçük parçalara bölelim ve tanımdan bazı sonuçlar çıkartalım:
Her şeyden önce bir dizi bir “bileşik”‘tir. Bir sınıf türü de bir “bileşik” olabilir. Ancak bir sınıf türünün bir bileşik tür olabilmesi için bazı koşulları sağlaması gerekir. Tanımda, yapılardan (structures) ya da birliklerden (unions) söz edilmediğini görüyorsunuz. Yapı türleri ya da birlik türleri de bileşik türler olabilir mi? Kesinlikle. C++ dilinde sınıf (class) terimi yapıları ve birlikleri de kapsıyor.

Tanım, bir sınıfın bileşik tür kabul edilebilmesi için kurucu işlevin olmaması gerektiğini değil, programcı tarafından sağlanmamış bir kurucu işlevinin olmaması gerektiğini söylüyor. Bu, derleyici tarafından içsel olarak bildirilen sınıfın kurucu işlevlerinin sınıfın bileşik tür olarak ele alınmasına engel olmadığı anlamına geliyor.

Sınıfın private ya da protected, statik olmayan (non-static) veri öğesi olmamalı. Başka bir deyişle statik olmayan tüm veri öğeleri sınıfın public bölümünde yer almalı. Diğer taraftan sınıfın private ya da protected, static veri öğelerine sahip olması sınıfın bir bileşik tür olarak ele alınmasına engel bir durum değil.

Bir sınıfın programcı tarafından bildirilmiş kopyalayan atama işlevine (copy assignment) ya da sonlandırıcı işleve (destructor) sahip olması, onun bileşik tür olmasını engelleyici bir durum değil.
“Bileşik” olmayan bir türden öğelere sahip bir dizi yine “bileşik” kabul ediliyor.
Bileşik olan sınıfların sanal olmayan (non virtual) işlevleri olabilir. Sanal bir işleve sahip bir sınıf bir “bileşik” değildir. Bileşik sınıflar public, protected ya da private sanal olmayan işlevlere sahip olabilirler.

Şimdi birkaç örnek verelim:

A sınıfı bir bileşik tür değil. Çünkü sınıfın private bir veri öğesi var.

B sınıfı bileşik bir tür değil. Çünkü sınıfın programcı tarafından bildirilen bir kurucu işlevi var.

C sınıfı bileşik bir tür değil. Çünkü sınıfın bir sanal işlevi var.

D sınıfı bir bileşik tür. Sınıfın tüm statik olmayan veri öğeleri sınıfın public bölümünde yer alıyor. Sınıfın public bir veri öğesinin bileşik olmayan bir türden olması, sınıfın kopyalayan atama işlevinin programcı tarafından bildirilmiş olması ya da sınıfın private bir üye işleve sahip olması sınıfın bileşik bir tür olmasını engellemiyor.

Der sınıfı Base sınıfından kalıtım yoluyla elde edildiği için “bileşik” değil.
Şimdi de aşağıdaki koda bakalım:

A sınıfının mx ve my isimli öğelerine sınıf içinde ilk değer verilmiş. C++11 kurallarına göre A sınıfı bir “bileşik” değil. Ancak C++14 standartları ile bu konuda önemli bir değişiklik yapıldı. Sınıfın statik olmayan veri öğelerine sınıf içinde ilk değer verilmesi artık sınıfın bir “bileşik” olmasına engel değil.

Bir sınıfın bir kurucu işlevinin default edilmesi sınıfın “bileşik” olma özelliğini engellemiyor. Aşağıda tanımlanan Data sınıfı bir “bileşik”;

Bir türün bileşik olmasının önemli bir sonucu var: Bileşik türlerden nesnelere küme parantezi içinde virgüllerle ayrılmış ifadelerle ilk değer verebiliyoruz. Bu şekilde ilk değer vermenin yalnızca dizilere uygulanan bir sentaks olduğunu sanıyor olabilirsiniz, ama değil.

Biz de önce dizilerle başlayalım. a, öğeleri T türünden olan n öğeye sahip bir dizi olsun. n değerinin bir sabit ifadesi (constant expression) olması gerektiğini biliyorsunuz.

Kuralları hatırlayalım. Eğer ilk değer verici ifadelerin sayısı dizinin boyutuna eşit ise dizinin öğeleri sırasıyla ilk değer verme listesinde bulunan ifadelerin değerleriyle hayata gelir.
Eğer ilk değer veren ifade sayısı dizinin boyutundan daha küçük ise, yani yukarıdaki örnekte m değeri n değerinden daha küçük ise, dizinin ilk m öğesi değerlerini ilk değer veren ifadelerden alırken dizinin kalan öğeleri üzerinde “değerle başlatım” (value initialization) uygulanır. Değerle başlatımın ne demek olduğunu hatırlayalım: Eğer dizi öğeleri skalar türlerden (bool, char  int, double vs) ise 0 değeriyle, sınıf türlerinden ise ilgili sınıfın varsayılan kurucu işleviyle hayata başlatılırlar. Eğer sınıfın varsayılan kurucu işlevi derleyici tarafından yazılıyor ise sınıfın statik olmayan her veri öğesi özyinelemeli olarak “değerle” başlatılır.
Bir referans değerle başlatılamaz. Bir sınıfın varsayılan kurucu işlevi çağrılamayacak durumda ise (yok ise, delete edilmiş ise ya da private ise) bu durumda “değerle başlatma” derleme zamanı hatası ile sonuçlanır. Aşağıdaki koda bakalım:

main işlevi içinde ar1 isimli, öğeleri A sınıfı türünden olan bir dizinin tanımlandığını görüyorsunuz. Bu dizinin boyutu 4 olarak belirtilmiş ve diziye ilk değer verilmiş. Küme parantezi içinde 4 tane ilk değer verici ifade kullanılmış. Tanımlamada bir sentaks hatası yok.
Şimdi de ar2 isimli dizinin tanımına bakalım. Yine dizinin boyutu 4 olarak belirlenmiş, ancak diziye ilk değer verilirken küme parantezi içinde yalnızca bir ifade kullanılmış. Bu durumda dizinin  diğer öğeleri, değerle başlatım söz konusu olduğundan, A sınıfının varsayılan kurucu işlevi ile hayata başlatılacak. A sınıfının varsayılan kurucu işlevi olmadığı için ar2 dizisinin tanımı sentaks hatası olarak kabul edilecek.

Bileşik sınıf nesnelerine de küme parantezi ile ilk değer verebiliyoruz:

Yukarıdaki kodda tanımlanan hem A hem de B “bileşik” sınıf türleri.
B sınıfının protected bir static veri öğesine sahip olması, private bir üye işlevinin olması ya da A sınıfı türünden bir öğeye sahip olması, B snıfının “bileşik” bir tür olmasını engellemiyor. Global kod alanında tanımlanan B sınıfı türünden b isimli nesneye küme parantezi içinde ilk değer verildiğini görüyorsunuz. Bu tanımlama ile b.c öğesi ‘a’ değeriyle, b.a.mx öğesi 1 değeriyle b.a.my öğesi 2 değeriyle, b.ar dizisi {20, 30, 0} değerleriyle ve  b.dval öğesi 0. değeriyle başlatılıyor. Şimdi de aşağıdaki koda bakalım:

main işlevi içinde tanımlanan Data türünden x nesnesine yine küme parantezi içinde ilk değer verildiğini görüyorsunuz. “necati” yazısı std:.string sınıfının ilgili kurucu işlevine ve 5632u sabiti std::bitset sınıfının ilgili kurucu işlevine gönderiliyor.

Standart kütüphanenin array sınıfı da “bileşik” olmanın tüm gereklerini sağlıyor:

C++14 standartlarına göre sınıfın statik olmayan veri öğelerinin sınıf içinde ilk değer alması sınıfın “bileşik” olmasını engellemiyor. Aşağıdaki kod C++14 standartlarına göre geçerli:

Peki neden bileşik sınıflara ve bileşik sınıf nesnelerine ihtiyaç duyuyoruz? Bileşik sınıflar ile, mantıksal bir ilişki içindeki farklı türden birden fazla varlığı, bir gizlemeye gerek duymadan, yani kullanıcı kodların bu varlıklara doğrudan isimleriyle ulaşabileceği biçimde, bir arada tutabiliyoruz.

 

Necati Ergin

C ve Sistem Programcıları Derneğinde eğitmen olarak çalışıyor.

Bunlar da ilginizi çekebilir

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Kod Eklemek İçin Okuyun