C++ Constructor

Not: C++ konularını soru/cevap şeklinde kısaca bilgi vererek ve kod parçaları üzerinde inceleyerek anlatıyorum. Daha fazlası için lütfen araştırma yapınız.

Construct -> inşa etmek, kurmak
Constructor -> inşa eden, kurucu

  • Constructor hakkında bilmemiz gereken bazı bilgiler: (Yazının devamında bunları inceleyeceğiz)

    1. Constructor nesneyi hayata hazırlar.
    2. Her sınıfın constructorı mutlaka var.
    3. Ben constructor yazmassam benim yerime derleyici yazıyor.
    4. Constructor sınıf ismi ile aynı olmalıdır.
    5. Constructor geri dönüş kavramı yoktur. (void değil karıştırılmamalı)
    6. Constructor NON-STATIC üye fonksiyonudur.
    7. Constructor const OLAMAZ.
    8. Constructor OVERLOAD edilebilir.
  • Bir sınıf nesnesi için bir bellek alanı elde etmemiz onu kullanılabilir bir sınıf yapmıyor. Kullanılabilir olması için ona bir ilk değer vermek gerekiyor.

  • Bir nesne hayata geldiğinde o nesnenin kullanılabilmesi için bazı kaynaklar edinmesi gerekiyor. Yani sadece ilk değer vermek olarak düşünmeyin, bazıları kaynak edinecekler.

  • C++ dilinde sınıflar için bazı fonksiyonlar var ki mutlaka olmak zorunda. Bu fonksiyonlara özel fonksiyonlar diyoruz. Yani bu fonksiyonlar biz yazarsak bizimkiler kullanılacak, biz yazmassak derleyeci bizim yerimize yazıp kullanacak. Constructor da özel fonksiyonlardan birisi.

  • Constructor için keyfi isim veremiyoruz. Yani bu isim class ismi ile aynı olmak zorunda. Derleyici constructor olup olmadığını sınıf ismi ile aynı olup olmadığına bakarak anlıyor.

  • Constructor’ın geri dönüş değeri gibi bir kavram yok. Yani void değil. Karıştırılmasın geri dönüş değeri diye bir kavram yok.


Constructor static üye fonksiyon mu yoksa non-static üye fonksiyon mu?

  • Constructor non-static üye fonksiyonudur. Çünkü nesnemize ilk değer vereceğine göre büyük olasıklıkla veri elemanlarına değerler yerleştirecek. Veri elemanlarına değer yerleştirmesi için nesnemizin adresini alması lazım. Nesnemizin adresini alması demek this göstericisinin olması demek.

Constructor sınıfın public bölümünde olmak zorunda mı?

  • Hayır böyle bir zorunluluk yok.
  • Eğer sınıfın private bölümüne koyarsak, derleyici tarafından çağırıldığı zaman yine sentaks hatası olur. Hatırlarsak clientlar sınıfın sadece public bölümüne erişebiliyorlardı. Hata olan constructor’ın private olması değil, private bir fonksiyonun çağırılması.
  • Constructor’ı private bölümüne yazacağımız özel bazı senaryolar var. (Örnek: Singleton Pattern)

#include <iostream>

// Private Constructor
class Myclass {
Myclass();
public:
void func();
};


int main()
{
Myclass m; //gecersiz cunku constructor private
// error: calling a private constructor of class 'Myclass'

return 0;
}

Default Constructor (Varsayılan Kurucu İşlev)

  • Parametre değişkeni yoksa yada tüm parametre değişkenleri varsayılan argüman alıyorsa bu default constructor

  • Argüman göndermeden çağırılabilen constructorlara default constructor diyoruz


Argüman Nedir?

  • Fonksiyon çağrılarıyla fonksiyonlara gönderilen ifadelere arguman yada actual parameter denir.

  • Biz hiçbir constructor yazmassak derleyici bize non-static inline public bir constructor yazar.


non-static ne demek?

  • static keywordu olmayan, yani this göstericisine sahip olan demek

  • Derleyicinin yazdığı constructor ın static olma imkanı yok, private olma imkanı yok


inline, fonksiyonlar için 2 farklı model var.

  1. Biz bir fonksiyona çağrısı yapıyoruz. Derleyici bu fonksiyona giriş çıkış kodlarını üretiyor fakat bu fonksiyonun kendi kodunun bu koda bağlanması işini linker’a havale ediyor. Peki bunu nasıl yapıyor? external reference yazıyor.

  2. inline fonksiyon dediğimiz şöyle, derleyici fonksiyonun tanımını da görücek, fonskiyonun tanımını gördüğü için fonksiyonu derleyecek object kodunu doğrudan çağırdığı yere gömücek. Yani burada artık linker bypass edilmiş oluyor. inline yapmakla derleyiciye şunu söylüyoruz, derleyici sen linker’i felan bırak gel bu işi aramızda halledelim.

  • inline fonksiyon ile inlined fonksiyonu birbirine karıştırmamak gerek. Bir fonksiyona inline demek böyle bir rica da bulunmak demek. Derleyici bu ricayı yerine getirebiliyorsa artık o fonksiyon inlined olucak.

  • Ama semantik olarak şöyle bir avantaj olacak. Derleyici bunu yerine getirse de getirmese de client tarafından kodla bunun bir alakası yok. Nasıl fonksiyon çağırıyorsak öyle çağırıcaz.

  • Constructor const olamaz

  • Constructor overload edilebilir, fazlaca karşılaşılan bir senaryo

// Constructor Overload

#include <string>
#include <cstring>


class Date {
public:
Date();
Date(int d, int m, int y);
Date(const std::string &); // tarih formatini bir yazi seklinde iletiyoruz
};
//Constructor Overload

#include <string>
#include <cstring>


class Triangle {

public:
Triangle(double d1, double d2, double d3);
Triangle(double base, double height);
};

Constructor explicit olarak çağrılamaz. (constructor ismi ile çağırılamaz)

// Constructor Hatalı Çağırma

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass(){}
Myclass(int, int);
};


int main()
{
Myclass m;

m.M(); // gecersiz
// error: cannot refer to type member 'Myclass' in 'Myclass' with '.'

return 0;
}
//Constructor C++11 Default Keyword
#include <iostream>

using namespace std;

class Myclass {

public:
Myclass(int);
};


int main()
{
Myclass m; // hata boyle bir constructor yok
// no matching constructor for initialization of ‘MyClass'

return 0;
}

// Cozum -> C++11 ile gelen default keywordu
// Myclass(){}; yada Myclass = default(); // c++11

Function overload kuralları burada tamamen geçerli

// Function Overload
Myclass(long);
Myclass(float);
//main….
Myclass m(10); //embiguity
// Constructor C++11

#include <string>
#include <cstring>


class Myclass {

public:
Myclass(int);
Myclass();


};


int main()
{
Myclass m1; //default constructor
Myclass m2{}; //default constructor - C++11
Myclass m3(); //function declaration
Myclass m4 = Myclass();
Myclass m5 = Myclass{};

return 0;
}

Nesne ne zaman hayata başlarsa constructor o zaman çağırılıyor.

  • C++ diğer dillerden farklılığı, primitive nesneler gibi, yerel nesneler oluşturabiliyoruz. Yani otomatik ömürlü, static ömürlü ve dinamik ömürlü nesne oluşturabiliyoruz.
// Constructor
#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}

void func()
{
Myclass m; // stack yer alan, default contructor cagirilacak
}

int main()
{
for (int k = 0; k < 10; k++)
func();

return 0;
}
/*
Myclass default constructor
Myclass default constructor
Myclass default constructor
Myclass default constructor
….
*/
// Default Constructor

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}

void func()
{
Myclass m1, m2; // hem m1 hemde m2 icin cagirilacak ayri ayri
cout << "************************" << endl;
}

int main()
{
for (int k = 0; k < 10; k++)
func();

return 0;
}

/*
Myclass default constructor
Myclass default constructor
"************************"
Myclass default constructor
Myclass default constructor
*/

Referans demek nesne demek değil

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}


int main()
{
Myclass m1; // 1 kere constructor cagirilir
// Myclass default constructor
Myclass &r1 = m1; // referans demek nesne demek degil
Myclass &r2 = m1; // referans bir nesnenin yerine gecen isim

return 0;
}

Pointer — Constructor

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}


int main()
{
Myclass *ptr; // constructor cagrisi yok, bu bir pointer, bir instance
// olusturulmadigi surece constructorin cagrisina neden olmaz

return 0;
}
// Constructor

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}


int main()
{
Myclass a[10]; // 10 kere constructor cagirilacak, her elemani icin

return 0;
}

Static ömürlü nesneler main’den önce çağırılır

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}

Myclass g; // global bir nesne, ömrü static
// static omurlu nesneler main den önce cagirilirlar

int main()
{
cout << "main basliyor" << endl;

return 0;
}

Static Yerel Sınıf Nesneleri

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}

void func()
{
static Myclass m; //statik yerel sinif nesneleri icin
//eger bu fonksiyon cagrilmazsa m hayata gelmez
}

int main()
{

return 0;
}
#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}

void func()
{
static Myclass m;
}

int main()
{
cout << "main basliyor" << endl;
cout << "func cagriliyor " << endl;
func(); // constructor cagirilacak
cout << "main devam ediyor" << endl;
cout << "func bir kez daha cagriliyor " << endl;
func(); // contructor yazisi cikmaz, artik 2. kez cagirdigimizda hayata gelmiyor
// static nesneler hayata 1 kere gelir
cout << "func bir kez daha cagriliyor " << endl;
func();

return 0;
}

New Operatörü

  • Dinamik nesne yaratılması için heap te bir alan yaratılması gerekiyor. Bu alanı tahsis eden operatör new (bu bir fonksiyonun ismi) (new bir operatör, operator new fonksiyon)

  • new operatörü önce operator new çağırıyor. Heap te bir alan elde ediliyor. O fonksiyon malloc un eşdeğeri bir fonksiyon. Yani size of myclass kadar yer ayırıyor. O adresi void olarak döndürüyor. Derleyici void myclass* dönüştürerek o adres için constructor çağırıyor.

  • Constructor çağırılmasını sağlayan pointer değil. new operatörünün kullanılması.

// New Operator
#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}

int main()
{
cout << "main basliyor" << endl;

Myclass *ptr = new Myclass;
// main basliyor
// MyClass default constructor

return 0;
}

2 tane new operatörü var. tekil new operatörü && [] new operatörü

// New Operator
#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
}

int main()
{
cout << "main basliyor" << endl;

Myclass *ptr = new Myclass[5];
// 5 tane MyClass nesnesi hayata gelir
// Constructor 5 kere cagirilir

////

return 0;
}

Non-Static Function

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
cout << "this = " << this << endl;
}

int main()
{
Myclass m1;

cout << "&m1 = " << &m1 << endl;

return 0;
}
// Uniform Initializer

#include <iostream>

using namespace std;

class Myclass {

public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
cout << "this = " << this << endl;
}

int main()
{
Myclass *ptr{new Myclass};

cout << "ptr = " << ptr << endl;

return 0;
}
// Pointer Aritmetiği
#include <iostream>

using namespace std;

class Myclass {
int a[4];
public:
Myclass();
};

/////
Myclass::Myclass()
{
cout << "Myclass default constructor" << endl;
cout << "this = " << this << endl;
}

int main()
{
Myclass x[4];

return 0;
}

C++03 ve C++11 Kullanımları

#include <iostream>

using namespace std;

class Myclass {
public:
Myclass(int);
};

/////
int main()
{
Myclass *p1 = new Myclass(12); //C++03
Myclass *p2 = new Myclass{45}; //C++11



return 0;
}