Menu

C++ Kurucular: Nesneleri Doğru Şekilde Başlatın

Kurucu (constructor), bir nesne oluşturulduğunda çalışan özel üye fonksiyondur. Varsayılan, parametreli ve kopya kurucularını, üye başlatma listelerini ve nesneleri yarım başlatılmış halde bırakmaktan nasıl kaçınılacağını öğrenin.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

Kurucu Nedir

Önceki sayfada sınıflar tanımlayıp onlara üye değişkenler verdiniz. Ancak yeni oluşturulmuş bir nesnenin üyeleri, siz onları ayarlamadıkça o bellekte ne çöp varsa onu tutar. Bir kurucu bunu düzeltir: nesne oluşturulduğu anda otomatik olarak çalışan özel bir üye fonksiyondur ve tek görevi nesneyi geçerli, tamamen başlatılmış bir durumda bırakmaktır.

Bir kurucu, sınıfla aynı ada sahiptir ve dönüş tipi yoktur, hatta void bile değil. Onu asla doğrudan çağırmazsınız; bir nesne var olduğunda derleyici sizin için onu çağırır.

Parametresi olmayan Counter() varsayılan kurucu olarak adlandırılır; hiç argüman geçmeden bir nesne oluşturduğunuzda kullanılan kurucudur.

Parametreli Kurucular

Hiç argüman almayan bir kurucu iş görür, ama genellikle bir nesneyi belirli değerlerle oluşturmak istersiniz. Bir parametreli kurucu argümanları kabul eder ve üyeleri başlatmak için onları kullanır.

Bir sınıf, parametre listeleri farklı olduğu sürece birden fazla kurucuya sahip olabilir; bu, kuruculara uygulanan sıradan fonksiyon aşırı yükleme işlemidir. Burada Point, koordinatlarla veya koordinatsız oluşturulabilir:

Sık karşılaşılan bir tuzak: Point p(); bir nesne oluşturmaz; derleyici bunu, Point döndüren p adlı bir fonksiyonun bildirimi olarak okur. Varsayılan kurucuyu çağırmak için Point p; (parantezsiz) ya da süslü parantezlerle Point p{}; yazın.

Üye Başlatma Listeleri

Şimdiye kadar örnekler üyelere kurucunun gövdesi içinde atama yaptı. Bu, basit tipler için çalışır ama yanlış araçtır. Gövde çalıştığında her üye zaten varsayılan olarak kurulmuştur; gövde sonra bunu çöpe atıp üzerine atama yapar. Bir üye başlatma listesi her üyeyi doğrudan, gövdeden önce, tek adımda başlatır.

Söz dizimi, parametre listesinden sonra iki nokta üst üste, ardından member(value) çiftleridir:

Bir string üye için bu, ayrıca boş bir string oluşturup sonra atama yapmaktan da kaçınır; başlatma listesi onu daha ilk denemede doğru şekilde oluşturur.

Başlatma listesi yalnızca bir optimizasyon değildir; üç durumda zorunludur, çünkü gövde yalnızca atama yapabilir, başlatamaz:

  • const üyeler: bir const, var olduktan sonra ona atama yapamazsınız.
  • Referans üyeler: bir referans, doğduğu anda bağlanmalıdır.
  • Tipinin varsayılan kurucusu olmayan üyeler.
class Sensor {
    const int id;        // const üye
    int& slot;           // referans üye

public:
    Sensor(int sensorId, int& s) : id(sensorId), slot(s) {}
    // id veya slot'u gövdede ayarlamaya çalışmak derlenmezdi.
};

Bilinmesi gereken bir incelik: üyeler, başlatma listesinde sizin sıraladığınız sıraya göre değil, sınıfta bildirildikleri sıraya göre başlatılır. Bir üyenin başlatıcısı başka bir üyeyi okuyorsa önemli olan bildirim sırasıdır; bu ikisini karıştırmak, henüz başlatılmamış bir değeri kullanmanın klasik bir kaynağıdır.

Varsayılan Argümanlar ve Yetki Devreden Kurucular

Her zaman ayrı aşırı yüklemelere ihtiyacınız yoktur. Varsayılan argümanlar, tek bir kurucunun birkaç durumu kapsamasını sağlar; bir argümanı atlayın, varsayılan değer onun yerini doldurur:

Varsayılan değerli parametreli bir kurucuyu, ayrı bir Point() varsayılan kurucusuyla birleştirirken dikkatli olun: derleyici Point p; için hangisini çağıracağını bilemez ve bir belirsizlik bildirir. Tek bir yaklaşım seçin.

Ortak bir kurulum paylaşan birden fazla kurucunuz olduğunda, bir yetki devreden kurucu (delegating constructor, C++11) mantığı tekrarlamak yerine bir kurucunun diğerini çağırmasını sağlar. Diğer kurucuyu başlatma listesine koyarak "yetki devredersiniz":

Kopya Kurucu

Bir nesneyi başka bir nesnenin kopyası olarak oluşturduğunuzda (değer ile geçirerek, geri döndürerek veya Foo b = a; yazarak) kopya kurucu çalışır. İmzası, aynı tipe bir const referans alır:

ClassName(const ClassName& other);

Eğer siz yazmazsanız, derleyici her üyeyi kopyalayan bir varsayılan kopya kurucu üretir. Yalnızca değerler (int'ler, string'ler, vector'ler) tutan sınıflar için bu tam olarak doğrudur ve kendinizinkini yazmamalısınız.

Asıl büyük tuzak, bellek üzerine olan bir sonraki bölümde yaşıyor: eğer sınıfınız yığın (heap) belleğine işaret eden ham bir işaretçi sahipliyorsa, varsayılan kopya kurucu veriyi değil işaretçiyi kopyalar; böylece iki nesne aynı belleğe işaret eder ve ikisi de onu serbest bırakmaya çalışır. İşte bu, double-free (çifte serbest bırakma) hatasıdır. Pratik kural Üç/Beş Kuralı'dır: özel bir yıkıcı (destructor) yazıyorsanız, neredeyse kesinlikle özel bir kopya kurucuya (ve kopya atamasına) da ihtiyacınız vardır. Modern C++'ta daha temiz çözüm, bir std::vector ya da akıllı işaretçi tutmaktır; böylece derleyicinin ürettiği kopya kendiliğinden doğru çalışır.

Ayrıca parametreyi referans ile almanın zorunlu olduğunu, isteğe bağlı olmadığını unutmayın: argümanını değer ile alan bir kopya kurucu, kendisini çağırmak için argümanı kopyalamak zorunda kalırdı; bu da derlenemeyecek sonsuz bir özyinelemedir.

Sıradaki: Yıkıcılar

Bir kurucu nesneyi kurar; bir yıkıcı (destructor) onu söker. Bir nesne kapsam dışına çıktığında ya da silindiğinde, yıkıcısı otomatik olarak çalışır; dosyaları, ağ bağlantılarını veya nesnenin tuttuğu yığın belleğini serbest bırakmak için mükemmel yerdir. Sonraki sayfa, yıkıcıların nasıl çalıştığını, tam olarak ne zaman tetiklendiklerini ve C++'a güçlü RAII desenini kazandırmak üzere kurucularla nasıl eşleştiklerini ele alıyor.

Sıkça Sorulan Sorular

C++ içinde kurucu (constructor) nedir?

Kurucu, sınıfla aynı ada sahip olan ve dönüş tipi bulunmayan özel bir üye fonksiyondur. Bir nesne oluşturulduğunda otomatik olarak çalışır ve görevi, başka herhangi bir kod nesneyi kullanmadan önce onu geçerli ve tamamen başlatılmış bir duruma getirmektir.

Varsayılan kurucu ile parametreli kurucu arasındaki fark nedir?

Varsayılan kurucu hiç argüman almaz ve değer vermeden bir nesne oluşturduğunuzda kullanılır (Point p;). Parametreli kurucu ise argüman alır, böylece çağıran kişi nesneyi belirli değerlerle başlatabilir (Point p(3, 4);). Bir sınıf her ikisine de sahip olabilir, çünkü kurucular parametre listelerine göre aşırı yüklenir.

C++ içinde neden üye başlatma listesi kullanmalıyım?

Üye başlatma listesi (: name(n), age(a)), üyeleri doğrudan, kurucunun gövdesi çalışmadan önce başlatır. const üyeler, referanslar ve varsayılan kurucusu olmayan üyeler için zorunludur ve gövde içinde atama yaptığınızda oluşan israflı önce-varsayılanla-kur-sonra-ata davranışını ortadan kaldırır.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA