delege eden kurucu işlev (delegating constructor)

Bir sınıfın birden fazla kurucu işlevinin olması çok doğal ve çoğu zaman da gerekli. Bu durumda çoğunlukla bu kurucu işlevlerin paylaştığı ortak bir kod söz konusu oluyor. Yine tipik olarak bu ortak kodun bir kısmı sınıfın veri öğelerini ilk değer verici liste ile (member initializer list) başlatıyor. Ortak kodun farklı noktalarda yeniden yazılmasının kodun bakımını zorlaştırdığını dahası kodlama hatalarına davetiye çıkarttığını biliyorsunuz. C++11 standartlarından önce bu sorunla başa çıkmak için dil tarafından doğrudan desteklenen bir araç yoktu. C++11 ile dile eklenen delege eden kurucu işlev (delegating constructor) kurucu işlevlerde karşılaşılan bu tipik sorunla başa çıkmak için önemli bir destek sağlıyor.
C++11 öncesinde kurucu işlevlerin ortak bir koda sahip olması durumunda programcıların tipik başvurduğu yol, ortak kodu sınıfın bir private işlevinde toplamak ve kurucu işlevler içinde de bu işlevi çağırmaktı. Standartlara bu konuda verilen SC22/WG21/N1986 No‘lu öneri belgesindeki örneği inceleyelim:

X sınıfının Y ve Z türlerinden veri öğeleri var. Sınıfın üç ayrı kurucu işlevinin olduğunu görüyorsunuz. Bu kurucu işlevlerin ortak olan kodu sınıfın commonInit isimli private işlevinde toplanmış. Kurucu işlevlerin ana bloğunun içinde commonInit işlevi çağrılıyor. Yine kurucu işlevler içinde, öğe ilk değer verme listesi ile sınıfın y_ ve z_ isimli öğelerine ilk değer veriliyor. Peki bu kodlarda bizi rahatsız edecek noktalar var mı?
X sınıfının kurucu işlevlerin ortak kodunun bir parçası da öğelere ilk değer vermek. Ancak bu işi bir başka işleve delege edemiyoruz. Yalnızca kurucu işlevler veri öğelerine ilk değer verebilirler, değil mi? Örnekte yer alan commonInit işlevi çağrıldığında zaten öğeler hayata gelmiş olacak.
Bu yapıda kurucu işlevlerin ana bloğunu boş bırakamıyoruz. Örneğin commonInit işlevinin kodundan bir hata nesnesi (exception) gönderilebilir. commonInit üye işlevi sınıfı kodları tarafından (yanlışlıkla) çağrılabilir. Bu işlevin sınıfı diğer işlevleri tarafından çağrılmasını engelleyen bir mekanizma yok.
C++11 standartlarıyla gelen eklemeyle artık bir kurucu işlev başka bir kurucu işlevin kodunu çalıştırabiliyor. Şimdi kodu yeniden düzenleyerek delege eden kurucu işlevler oluşturuyoruz:

Yukarıdaki kodu inceleyelim: Sınıfa daha önceki kodda yer almayan private bir kurucu işlev ekledik:

Bu private işlev y_ ve z_ veri öğelerini üye ilk değer verme listesiyle hayata başlattığı gibi, kuruluş sürecinde yapılması gerelen diğer işlemleri de ana bloğundaki kod ile gerçekleştiriyor. Yani bir önceki sürümdeki init işlevinin kodunun bu kurucu işlevin ana bloğu içine yerleştirildiğini düşenebilirsiniz. Diğer kurucu işlevler ise tüm işi private kurucu işleve yaptırıyorlar (delege ediyorlar).

SC22/WG21/N1986 No’lu öneride verilen güzel bir örnek de şöyle:

Yukarıdaki kodda FullName sınıfının hem iki parametreli kurucu işlevi hem de kopyalayan kurucu işlevi, sınıfın üç parametreli kurucu işlevine delege ediyorlar.

Delege eden kurucu işlev içinde, ilk değer verme listesi ile bir veri öğesine ilk değer veremiyoruz. Yani ilk değer verme listesinde bulunan tek öğe delege edilen kurucu işleve yapılan çağrı olmalı. Aşağıdaki koda bakalım:

A sınıfının kurucu işlevinin tanımı geçerli olsaydı sınıfın my isimli veri öğesine iki kez ilk değer verilmiş olurdu, değil mi?

Delege edilen kurucu işlev de aynı sentaksı kullanarak bir başka kurucu işleve delege edebilir. Ancak derleyici böyle bir durumda oluşacak sonsuz bir çevrimi kontrol etmekle yükümlü değil, böyle bir çevrim oluşmasından tamamen programcı sorumlu.

Eğer delege edilen bir kurucu işlev bir hata nesnesi gönderirse gönderilen hata nesnesini delege eden kurucu işlevde oluşturulan bir işlev try bloğu ile yakalayabiliyoruz:

Yukarıdaki kodda A sınıfının varsayılan kurucu işlevinin bir hata nesnesi gönderdiğini görüyorsunuz. Sınıfın int parametreli kurucu işlevi varsayılan kurucu işleve delege ediyor. Varsayılan kurucu işlevden gönderilen hata nesnesi int parametreli kurucu işlevin oluşturduğu işlev try bloğu ile yakalanıyor.

Delege eden kurucu işlevler örneklerden de görüldüğü kodu karmaşıklıktan arındırıyor ve kodun bakımını kolaylaştırıyor.

Necati Ergin

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

Bunlar da ilginizi çekebilir

delege eden kurucu işlev (delegating constructor)” için 3 yorum

  1. Elinize sağlık Necati Bey diğer yazılarınız gibi güzel&anlaşılır bir yazı olmuş. plepa.com’a her gün bir yazı yazacağım hedefinize geri dönersiniz umarım. Galiba haftada bir yazı şeklinde hedefinizi değiştirmişsiniz.
    ————————————————————————————————-

    A sınıfının kurucu işlevinin tanımı geçerli olsaydı sınıfın mx isimli veri öğesine iki kez ilk değer verilmiş olurdu, değil mi? sorusunda mx ifadesi doğru mu yoksa my’yi kastettiniz? mx ise iki kez olan yerler neresi acaba?

    1. Merhaba Serkan Bey,
      Teşekkürler, öncelikle yazım hatasını düzelttim. Her gün bir yazı yazma hedefimi değiştirmedim kesinlikle. Ancak araya uzun bir hastalık dönemi girdi ve ardıdan da yoğun bir kurumsal eğitim süreci başladı.Yakın zamanda aradaki boşluğu kapatabileceğimi umuyorum.
      iyi çalışmalar diliyorum.

  2. Necati Bey çok geçmiş olsun. İhtiyacımız olan bir kurs açılmadığından derneğe gelme şansımızda olmuyor. Herkesin talep ettiği gibi sizden de modern cpp kursu açılmayınca sizle iletişimimiz minimuma indi. Umarım çok ciddi bir problem değildir. Sizin gibi kıymetli insanların hem değerinin bilinmesi hem de çoğalması gerekir ki bu topraklar düzelsin. iyi çalışmalar

Bir Cevap Yazın

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

Kod Eklemek İçin Okuyun