Haftalık C++ 57 – Üç/Beş Kuralı, Nesne Oluşturma

Merhaba sevgili yazılımperver dostlarım. Bugün C++ geliştiricilerin vakıf olduğu “rule of three” ve C++ 11 ile birlikte artık “rule of five” mevzusuna bakıyor olacağız. Bunu yaparken de, C++ constructor, copy constructor, assignment operator gibi temel kavramlara da eğiliyor olacağız. Kurallara geçmeden önce, bu temel kavramları ve nasıl kullanıldıklarını hatırlayalım isterseniz.

Temel Sınıf Oluşturma/Atama Operasyonları

Yapıcılar aslında, sınıf ile aynı ismi tanıyan (derleyici diğer fonksiyonlardan bu sayede onları ayırır), dönüş değeri olmayan özel üye fonksiyonlardır ve sınıf içerisindeki verileri geçerli değerler ile doldurmak (ya da benzeri) amacı ile kullanılırlar. Üç tip yapıcı bulunur. Bunlar: Varsayılan, parametrik ve kopya yapıcılardır. Varsayılan yapıcı, herhangi bir parametre almayan ve eğer programcı tanımlamaz ise derleyici tarafından otomatik olarak üretilen bir yapıcıdır. Temel değerler 0/false ile doldurulur.

Parametrik yapıcılar ise varsayılanlardan farklı olarak çeşitli girdiler alabilmektedir. Bunlar da ilgili sınıfın oluşturulmasında ve verilerin ilklendirilmesinde kullanılırlar.

Son olarak, kopya yapıcı ise isminden de anlaşılacağı gibi, daha önce aynı tipte oluşturulmuş bir nesne ile yeni nesnenin ilklendirilmesi için kullanılan yapıcıdır. Bu yapıcı için nesneye referans tipinde argüman geçirilir.

Aşağıda bütün bunları gösteren basit bir örnek bulabilirsiniz:

Bu örnekte gördüğünüz üzere bir sınıf için birden fazla yapıcı tanımlayabilirsiniz. Yapılacar mevzusunu kapatmadan, onlar ile ilgili bir kaç hususa daha değinelim:

  • Yapıcıları tanımlamazsanız, derleyiciler varsayılan bir taneyi sizin için tanımlar (tabi özellikle tanımlamamasını demezseniz),
  • Yapıcılar statik ya da virtual ön eki almaz,
  • Yapıcılar için olabildiğince ilklendirme işlemleri yapıp, “exception” üretebilecek durumlardan kaçınmalısınız.

Şimdi kurallara bakabiliriz.

Üç Kuralı

Üç kuralı aslında C++ 11 öncesi kullanım için geçerli, sonrası için beş kuralına da bakıyor olacağız. Üç kural temelde şunu der:

Eğer bir C++ sınıfı, yıkıcı (“destructor”) ya da kopya yapıcı da kopyalama operatöründen  (operator =()) birini tanımlarsa, diğerlerini de tanımlamalıdır.

Yapıcılara baktık, yıkıcı da temelde ilgili sınıf kapsam dışına çıktığında ya da özellikle silindiğinde çağrılan yine özel bir fonksiyondur. Atama operatörü ise, zaten oluşturulmuş bir nesnenin içeriğinin diğerine kopyalanması amacı ile kullanılır.

Bunun arkasında yatan temel motivasyon da, eğer programcı bunlardan birini tanımlama ihtiyacı duymuşsa, muhtemelen (özel durumlar hariç) otomatik olarak tanımlanacak diğer yapıcılar ve yıkıcı ihtiyacını karşılamayacaktır. Çünkü, derleyici tarafından tanımlanan yapıcılar ve kopyalama operatörü, basitçe verileri kopyalar (“shallow copy”), işaretçilerin veya benzeri yapıların ihtiyaç duyacağı kopyalama işlemlerini yapmaz.

Şimdi bunun sıkıntı yarattığı bir duruma göz atalım:

Yukarıdaki örnekte, her ne kadar parametrik yapıcı ile dinamik bellekler kopyalansa da, kopya yapıcı tanımlanmadığı için kopyalama işlemi sadece adreslerin kopyalanması ile kalıyor. Bu da ilgili nesneler kapsam dışına çıktığında yıkıcının zaten silinmiş bir belleği iki kere silmeye kalkmasına yol açıyor.  Yukarıdaki kodu çalıştırdığınızda aşağıdaki gibi bir çıktı görmeniz normal:

Benzer şekilde kopya yapıcının olup, atama operatörünün olmadığını düşünün. Bu durumda da, yine atama ile kopyalanan nesnenin değerleri basitçe kopyalanacak ve işaretçiler yanı yeri gösteriyor olacaklar.

Şimdi gelelim beş kuralına.

Beş Kuralı

Beş kuralı temelde, üç kuralı ile aynı olması yanında, taşıma yaklaşımı ile gelen yeni taşıma yapıcısı ve atama operatörünü de göz önüne alır ve kuralı şu şekilde günceller:

Eğer bir C++ sınıfı, yıkıcı (“destructor”) ya da kopya yapıcı da kopyalama operatöründen  (operator =()) ya da taşıma yapıcısı ya da taşıma atama operatöründen birini tanımlarsa, diğerlerini de tanımlamalıdır.

taşıma operatör ya da yapıcılarının tanımlanmaması her ne kadar hata olmasa da özellikle bellek yönetimi gerektiren sınıflarda bunların tanımlanması daha verimli bir çözüm sağlar. Bu bağlamda yukarıdaki sınıf için aşağıdaki iki operatörü tanımlamalıyız:

Bu arada taşıma yapıcısı ve atama operatörü ve diğer taşıma semantiğine ilişkin detaylar için aşağıdaki yazıma göz atabilirsiniz.

Modern C++ (5) : Taşıma Semantikleri

Bir sonraki yazımda görüşmek dileğiyle, kendinize iyi bakın.

Kaynaklar

Bir cevap yazın

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

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.