BG MVC Model View Controller eğitim serisi yayında...

Ana sayfa > Programlama > C++ Programlama > Sınıf

Sınıf

Nesneye yönelik programlama (NYP) çalışma sistemi sınıflar içinde yer alan iki temel kavram üzerinde şekillenir:

1. Değişkenler (veriler): NYP ile geliştirilen uygulamalarda ihtiyaç duyulan farklı türden değişkenler sınıf içinde tanımlanır.

2. Değişkenlere işlem yapan fonksiyonlar (metodlar): NYP ile geliştirilen uygulamalarda sınıf içinde tanımlanan değişkenlere işlem yapmak üzere fonksiyonlar içinde kodlar tanımlanır.

NYP ile geliştirilen uygulamalarda program içinde, veriler ile verilere işlem yapacak olan fonksiyonlar (metodlar) sınıf (class) adı verilen yapıların içinde tanımlanır. Sınıf tanımlandıktan sonra bu sınıf türünden bir değişken (nesne) oluşturularak, bu nesne yoluyla sınıf içinde yer alan tüm değişken ve fonksiyonlara erişim sağlanır.

Sınıf (Class) bildirimi

Bir nesne oluşturmadan önce, class anahtar kelimesini kullanarak nesnenin tüm özellik (değişkenler) ve davranışlarını (fonksiyonlar) gösteren genel yapısını tanımlamamız gerekir.

Sınıflar class anahtar kelimesi ile oluşturulur. Bir sınıf oluşturulduğunda, sınıf içindeki değişkenlere yine sınıf içindeki fonksiyonların işlem yapmasını sağlayan yeni bir veri türü tanımlanmış olur.

Sınıf adı verilen bu yeni veri türü oluşturulduktan sonra, bu sınıfa ait nesne bildirimi yapılır. Bu işlem, int bir veri türünden değişken tanımlamaya benzer.

Bir sınıf bildiriminin genel yapısı aşağıda gösterilmektedir:

class sınıf-adı {
   // private veri ve fonksiyonlar
   private:   
      veri-türü değişken_adı;
      .
	  .
      veri-türü değişken_adı;
   
      veri-türü fonksiyon_adı (veri-türü);
      .
	  .
      veri-türü fonksiyon_adı (veri-türü);
      
   // protected veri ve fonksiyonlar
   protected:
      veri-türü değişken_adı;
      .
      .
      veri-türü değişken_adı;
   
      veri-türü fonksiyon_adı (veri-türü);
      . 
      .
      veri-türü fonksiyon_adı (veri-türü);

   // public veri ve fonksiyonlar
   public:
      veri-türü değişken_adı;
      .
      .
      veri-türü değişken_adı;
   
      veri-türü fonksiyon_adı (veri-türü);
      . 
      .
      veri-türü fonksiyon_adı (veri-türü);	 

} nesne/nesne-listesi; // İsteğe bağlı olarak tanımlanır.

Bir sınıf içinde bildirimi yapılan tüm fonksiyon ve veriler, ön tanımlı olarak, private özelliği taşır ve sadece sınıfın diğer üyeleri tarafından erişilebilir.

Bir sınıf içinde yer alan protected fonksiyon ve verilere ise; bu sınıftan türetilen alt sınıflar public olarak türetildiğinde, alt sınıfların içindeki fonksiyonlar direk erişim sağlayabilir.

Bir sınıf içinde yer alan public fonksiyon ve verilere ise, programın içinde yer alan kodların tamamı tarafından erişim sağlanabilir.

Sınıf içindeki üye fonksiyonların kod bloğu sınıf içinde veya sınıf dışında ayrıca yapılabilir.

private, public ve protected erişim tanımlayıcılarından birisi kullanıldığında, başka bir erişim tanımlayıcısı ile karşılaşana veya sınıf bildiriminin sonu gelene kadar, erişim tanımlayıcının geçerliliği devam eder.

Erişim tanımlayıcılarını istediğimiz sayıda ve farklı sıralamalarla kullanabiliriz.

Aşağıdaki kod satırları sinif isimli bir sınıf tanımlar:


class sinif {
  // Sadece sınıf fonksiyonlarının erişebileceği private değişkenler
  private:  
    int priid;
  // Sadece sınıf fonksiyonları ile türetilmiş sınıf fonksiyonlarının erişebileceği private değişkenler
  protected:  
    int proid;	
  // Sınıf fonksiyonları, türetilmiş sınıf fonksiyonları ve sınıf nesneleri erşim sağlayabilir.
  public: 
    int pubid;
	void deger_ata(int pid1, int pid2, int pid3);
    void deger_goster();
};

Yukarıdaki sınıf tanımlamasında, priid değişkeni private olduğundan, bu sınıf elemanına sınıf üyesi olmayan herhangi bir fonksiyon tarafından erişilemez. Bu özellik nesneye yönelik programlama dilinin kapsülleme özelliğinin bir sonucudur.

public olarak tanımlanan deger_ata() ve deger_goster() fonksiyonları sinif sınıfında yer aldıklarından üye fonksiyonlardır. Aynı zamanda, priid, proid, pubid değişkenleri de sinif sınıfında yer aldıklarından üye değişkenlerdir.

Bir sınıf içinde bildirimi yapılan fonksiyonlara üye fonksiyon adı verilir. Üye fonksiyonlar içinde tanımlandıkları sınıftaki private, public ve protected olmak üzere tüm elemanlara erişim sağlayabilirler.

Bir sınıf içinde bildirimi yapılan değişkenlere üye değişken adı verilir.

Bir sınıf içindeki private fonksiyon ve değişkenlere, sadece sınıf içindeki fonksiyonlar yoluyla, protected değişkenlere, sınıf içindeki fonksiyonlar ile bu sınıftan public olarak türetilen alt sınıfların fonksiyonları yoluyla, public değişkenlere ise, sınıf içindeki fonksiyonlar, bu sınıftan türetilen alt sınıfların fonksiyonları ve programın herhangi bir yerinde tanımlanan sınıf nesneleri yoluyla erişim sağlanabilir.

Eğer bir değişken veya fonksiyon için herhangi bir erişim türü tanımı yapılmamışsa, ön tanımlı olarak, private kabul edilir.

Bir sınıf içindeki değişken ve fonksiyonlara, program içinde bildirimi yapılan sınıf türünden nesneler yoluyla erişmek için, bu değişken ve fonksiyonları public: ifadesinden sonra tanımlamamız gerekir. Bu durumda, programımızın sınıf dışındaki fonksiyonları tanımlanan sınıftan oluşturulan nesneye public fonksiyonlar yoluyla erişim sağlar. Tüm değişkenleri private ve bu verilere dışarıdan erişimi public tanımlı sınıf fonksiyonlarıyla yapmak veri güvenliği açısından en tercih edilen yöntem olarak kabul görür.

Nesne (Object) bildirimi

Nesne bildirimi 2 farklı yöntemle yapılabilir:

1. Nesne bildirimi sınıf bildirimi ile birlikte yapılır.

2. Sınıf bildirimi yapıldıktan sonra, sınıf adını kullanarak nesne bildirimi yapılır.

1. Sınıf bildirimi ile birlikte nesne bildirimi


class sinif {
  // Sadece sınıf fonksiyonlarının erişebileceği private değişkenler
  private:  
    int priid;
  // Sadece sınıf fonksiyonları ile türetilmiş sınıf fonksiyonlarının erişebileceği private değişkenler
  protected:  
    int proid;	
  // Sınıf fonksiyonları, türetilmiş sınıf fonksiyonları ve sınıf nesneleri erşim sağlayabilir.
  public: 
    int pubid;
	void deger_ata(int pid1, int pid2, int pid3);
    void deger_goster();
} nes;

Yukarıda, sinif adlı bir sınıfı oluşturulurken aynı zamanda sinif türünden nes adlı bir nesne tanımlanır.

2. Sınıf bildirimi yapıldıktan sonra nesne bildirimi

Bir sınıf tanımlayarak, yeni bir veri tipi oluşturduktan sonra, bu veri tipinden değişkenler oluşturmak için nesne bildirimi yapılır. Nesne bildirimi yapmak için sınıf adı ile birlikte oluşturulacak nesne adı kullanılır:

sınıf-adı nesne-adı

Aşağıdaki kod satırı sinif sınıfından nes adlı bir nesne oluşturur:


sinif nes;

Tanımlaması yaptığımız sınıf içindeki fonksiyonların sadece bildirimini yaptık. Bu fonksiyonların içinde yer alacak kodları yazabilmek için aşağıdaki genel yapıyı kullanmamız gerekir:

geri-dönüş-değeri sınıf-adı::fonksiyon-adı(parametre1, parametre2, ...)
{
  işlem satırı;
  .
  .  
}

Fonksiyon kodlarını tanımlayan ana yapı içinde yer alan :: işlemcisi bu fonksiyonun sol tarafta yer alan sınıfa ait olduğunu gösterir.

Şimdi, yukarıda tanımlamasını yaptığımız sınıf içinde yer alan fonksiyonların ana yapılarını da içeren programı oluşturmaya çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    // Bu değişkene sadece sinif içindeki fonksiyonlar erişim sağlayabilir.
    int priid;

  protected:
    // Bu değişkene sinif içindeki fonksiyonlar ile sinifana sınıfından türetilmiş sınıf fonksiyonları erişim sağlayabilir.
    int proid;

  public:
    // Bu değişkene sinif içindeki fonksiyonlar ve sinif sınıfından türetilmiş sınıf fonksiyonları ile
    // sinifana türünden ve türetilmiş sınıf türünden oluşturulmuş nesneler doğrudan erişim sağlayabilir.
    int pubid;
    void deger_ata(int pid1, int pid2, int pid3)
    {
      priid = pid1; proid = pid2; pubid = pid3;
      cout << "sinif değişkenlerine değer atama: " << priid << " " << proid << " " << pubid << endl;
    }
    void deger_goster()
    {
      cout << "sinif değişken değerleri: " << priid << " " << proid << " " << pubid << endl;
    }
};

int main(void)
{
  sinif nes;

  cout << "nes değişkeninin sinif elemanlarına erişimi:" << endl;
  cout << "--------------------------------------------" << endl;
  nes.deger_ata(7, 21, 135);
  nes.deger_goster();

  // nes.priid = 954; // Derleme hatası verir (Nesne sınıf içindeki private değişkene doğrudan erişim sağlayamaz).
  // nes.proid = 954; // Derleme hatası verir (Nesne sınıf içindeki protected değişkene doğrudan erişim sağlayamaz).
  nes.pubid = 954;    // Nesne sınıf içindeki public değişkene doğrudan erişim sağlar.

  nes.deger_goster();

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

nes değişkeninin sinif elemanlarına erişimi:
--------------------------------------------
sinif değişkenlerine değer atama: 7 21 135
sinif değişken değerleri: 7 21 135
sinif değişken değerleri: 7 21 954

Program, sinif adlı bir sınıf oluşturur. Sınıf içinde de private, protected ve public olmak üzere üç adet değişken ve bu değişkenlere değer atayan ve ekranda gösteren iki adet fonksiyon tanımlar.

Program çalışmaya başladığında, sinif cinsinden nes adlı bir nesne tanımlar. Nesne yoluyla, sınıf fonksiyonlarını çağırarak, önce deger_ata() fonksiyonu ile sınıf içindeki değişkenlere birer değer atar, sonra deger_goster() fonksiyonu ile değişken değerlerini ekrana yazar. Nesne yoluyla sınıf içindeki pubid adlı public değişkene 954 değeri atar. Sınıf içinde yer alan private ve protected değişkenlere doğrudan nesne yoluyla erişim sağlamaya çalışırsa, program derleme hatası verir. Sonra, deger_goster() fonksiyonu ile değişken değerlerini tekrar ekrana yazar.

Bir sınıftan oluşturulmuş nesneler ayrı bir sınıf kopyası üzerinde işlem yaptığından, farklı nesnelere ait değişken değerlerinin birbiri ile ilgisi yoktur.

Bir sınıf içinde yer alan üye fonksiyonların, aynı sınıf içinde yer alan diğer fonksiyon ve değişkenlere erişim için nokta (.) işlemcisini kullanmasına gerek yoktur.

Bir sınıftan oluşturulan nesne yoluyla sınıf içinde yer alan private ve protected değişken ve fonksiyonlara direk erişim sağlanmaz, bu erişim aynı sınıf içinde yer alan üye fonksiyonlar yoluyla sağlanır.

Constructor ve destructor kavramı

C++'da nesnelerin bildirimi yapılırken, nesnenin tanımlandığı sınıfta yer alan değişkenlere bir ilk değer verme işlemi gerçekleştirmek için constructor fonksiyonları kullanılır.

Aynı şekilde nesneler yok edilirken de, belirli işlemleri gerçekleştirmek için destructor fonksiyonları kullanılır.

constructor ve destructor fonksiyonları içinde tanımlandığı sınıfın bir elemanı olup, sınıf ile aynı adı taşır. destructor fonksiyonları başına ~ işareti alır.

Bir sınıfın constructor fonksiyonu, bir nesne oluşturulduğunda, destructor fonksiyonu ise, nesne yok edildiğinde, otomatik olarak çağrılır.

C++'da constructor ve destructor fonksiyonları herhangi bir değer geri vermediğinden, fonksiyon bildiriminde dönüş değeri tanımlaması yapılmadığına dikkat ediniz.

Şimdi, constructor ve destructor fonksiyonlarının kullanımını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    // Bu değişkene sadece sinif içindeki fonksiyonlar erişim sağlayabilir.
    int priid;

  protected:
    // Bu değişkene sinif içindeki fonksiyonlar ile sinif sınıfından türetilmiş sınıf fonksiyonları erişim sağlayabilir.
    int proid;

  public:
    // Bu değişkene sinif içindeki fonksiyonlar ve sinif sınıfından türetilmiş sınıf fonksiyonları ile
    // sinif türünden ve türetilmiş sınıf türünden oluşturulmuş nesneler doğrudan erişim sağlayabilir.
    int pubid;
    // Constructor fonksiyon bildirimi
    sinif(int pid1, int pid2, int pid3);
    // Destructor fonksiyon bildirimi
    ~sinif();
    void deger_ata(int pid1, int pid2, int pid3);
    void deger_goster();
};

// Sınıf fonksiyonlarının kod blok tanımlamaları
sinif::sinif(int pid1, int pid2, int pid3)
{
  cout << "constructor fonksiyonu ile sınıf değişkenlerine değer atama" << endl;
  priid = pid1; proid = pid2; pubid = pid3;
}

sinif::~sinif()
{
  cout << "Nesne yok edildi!" << endl;
}

void sinif::deger_ata(int pid1, int pid2, int pid3)
{
  cout << "Üye fonksiyon ile sınıf değişkenlerine değer atama" << endl;
  priid = pid1; proid = pid2; pubid = pid3;
}

void sinif::deger_goster()
{
  cout << "sınıf değişken değerleri: " << priid << " " << proid << " " << pubid << endl;
}

int main(void)
{
  // Nesne oluştururken sınıf değişkenlerine değer atama
  sinif nes(9, 25, 174);
  nes.deger_goster();

  // Üye fonksiyon ile sınıf değişkenlerine değer atama
  nes.deger_ata(7, 21, 135);
  nes.deger_goster();

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

constructor fonksiyonu ile sınıf değişkenlerine değer atama 
sınıf değişken değerleri: 9 25 174
Üye fonksiyon ile sınıf değişkenlerine değer atama 
sınıf değişken değerleri: 7 21 135
Nesne yok edildi!

Program, sinif adlı bir sınıf oluşturur. Sınıf içinde de private, protected ve public olmak üzere üç adet değişken ve bu değişkenlere nesne oluştururken değer atayan bir constructor fonksiyonu, bir destructor fonksiyonu, nesne ile değişkenlere değer atayan deger_ata() adlı bir fonksiyon ve değişkenleri ekrana yazan deger_goster() adlı bir fonksiyon tanımlar.

Program çalışmaya başladığında, sinif cinsinden nes adlı bir nesne bildirimi yaparken sınıf içindeki değişkenlere constructor fonksiyonu yoluyla birer değer atar. Nesne yoluyla, deger_goster() fonksiyonu ile değişken değerlerini ekrana yazar. Yine, nesne yoluyla deger_ata() fonksiyonu ile sınıf içindeki değişkenlere birer değer atar, sonra deger_goster() fonksiyonu ile değişken değerlerini yeniden ekrana yazar.

Burada destructor fonksiyonu içeriği sadece yaptığı işlemi göstermek için oluşturulmuştur.

Tek parametre değeri alan constructor fonksiyonları

Bir constructor fonksiyonu ile sadece tek bir ilk değer verme işlemi gerçekleştireceksek, bu işlemi aşağıdaki işlem satırı ile de yapabiliriz.

sınıf-adı nesne-adı = ilk-değer;

Aşağıdaki program 2 farklı yöntem kullanarak personel sınıfı içindeki int değişkene nesne oluşturma anında bir ilk değer atar:


#include <iostream>

using namespace std;

class sinif {
  private:
    int priid;
  public:
    sinif(int pid);
    ~sinif();
    void deger_ata(int pid);
    void deger_goster();
};

sinif::sinif(int pid)
{
  cout << "constructor fonksiyonu ile sınıf değişkenine değer atama" << endl;
  priid = pid;
}

sinif::~sinif()
{
  cout << "Nesne yok edildi!" << endl;
}

void sinif::deger_ata(int pid)
{
  cout << "Üye fonksiyon ile sınıf değişkenine değer atama" << endl;
  priid = pid;
}

void sinif::deger_goster()
{
  cout << "sınıf değişken değeri: " << priid << endl;
}

int main(void)
{
  // Nesne oluştururken sınıf değişkenlerine değer atama
  sinif nes1(25);
  // Sadece tek bir değer verme işlemi için nesne bildirimi
  sinif nes2 = 36; // Otomatik olarak sinif nes2(36) şekline çevrilir.

  // Üye fonksiyon ile sınıf değişkenlerini ekrana yazma
  nes1.deger_goster();
  nes2.deger_goster();

  // Üye fonksiyon ile sınıf değişkenlerine değer atama
  nes1.deger_ata(121);
  nes2.deger_ata(246);

  // Üye fonksiyon ile sınıf değişkenlerini ekrana yazma
  nes1.deger_goster();
  nes2.deger_goster();

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

constructor fonksiyonu ile sınıf değişkenine değer atama
constructor fonksiyonu ile sınıf değişkenine değer atama
sınıf değişken değeri: 25
sınıf değişken değeri: 36
Üye fonksiyon ile sınıf değişkenine değer atama
Üye fonksiyon ile sınıf değişkenine değer atama
sınıf değişken değeri: 121
sınıf değişken değeri: 246
Nesne yok edildi!
Nesne yok edildi!

Program, sinif adlı bir sınıf oluşturur. Sınıf içinde de private bir int değişken ve bu değişkene nesne oluştururken değer atayan bir constructor fonksiyonu, bir destructor fonksiyonu, nesne ile değişkene değer atayan deger_ata() adlı bir fonksiyon ve değişken değerini ekrana yazan deger_goster() adlı bir fonksiyon tanımlar.

Program çalışmaya başladığında, sinif cinsinden nes1 ve nes2 adlı iki nesne bildirimi yaparken sınıf içindeki değişkene constructor fonksiyonu yoluyla bir değer atar. Nesneler yoluyla, deger_goster() fonksiyonu ile değişken değerlerini ekrana yazar. Yine, nesne yoluyla deger_ata() fonksiyonu ile sınıf içindeki değişkene bir değer atar, sonra deger_goster() fonksiyonu ile değişken değerlerini yeniden ekrana yazar.

Explicit constructor fonksiyonlar

Bir sınıf içindeki constructor fonksiyonu sadece tek bir parametre aldığında, aşağıda gösterilen her iki nesne bildirimi de aynı işlemi yapmaktadır:

sınıf-adı nesne-adı(değer);

sınıf-adı nesne-adı = değer;

Nesne ile temsil edilen sınıf kopyasındaki değişkene değer atamak için atama işlemcisi kullanıldığında, bu satır otomatik olarak üstteki işlem satırındaki yapıya dönüştürülür. Bu otomatik dönüştürme işleminin yapılmasını istemediğimizde, explicit constructor fonksiyonlarını kullanabiliriz.

Şimdi, explicit constructor fonksiyonlarının kullanımını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    int priid;
  public:
    sinif(int pid);
    void deger_goster();
};

sinif::sinif(int pid)
{
  cout << "constructor fonksiyonu ile sınıf değişkenine değer atama" << endl;
  priid = pid;
}

void sinif::deger_goster()
{
  cout << "Sınıf değişken değeri: " << priid << endl;
}

int main(void)
{
  // Nesne oluştururken sınıf değişkenlerine değer atama
  sinif nes1(21);
  nes1.deger_goster();

  // Atama işlemcisi ile değer vererek nesne bildirimi
  // Aşağıdaki işlem satırları derleme hatası verir.
  // sinif nes2 = 36; // Otomatik olarak sinif nes2(36) şekline çevrilir.
  // nes2.deger_goster();

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

constructor fonksiyonu ile sınıf değişkenine değer atama
Sınıf değişken değeri: 21

Program, sinif adlı bir sınıf oluşturur. Sınıf içinde de private bir int değişken ve bu değişkene nesne oluştururken değer atayan bir constructor fonksiyonu ve değişken değerini ekrana yazan deger_goster() adlı bir fonksiyon tanımlar.

Program çalışmaya başladığında, sinif cinsinden nes1 adlı bir nesne bildirimi yaparken sınıf içindeki değişkene constructor fonksiyonu yoluyla bir değer atar. Nesne yoluyla, deger_goster() fonksiyonu ile değişken değerini ekrana yazar. Atama işlemcisi ile değer verilerek yapılan nes2 adlı nesne oluşturma işlemi ise, constructor fonksiyonu implicit olarak tanımlandığından, derleme hatası verir.

Constructor fonksiyonlarda ilk değer verme listesi

Bir sınıf içinde bildirimi yapılan değişkenlere, bu sınıftan oluşturulan nesne bildirimi esnasında, constructor fonksiyonlara geçirilen değerler yoluyla bir ilk değer verilebilir. Ancak, sınıf içinde const olarak bildirimi yapılan değişkenlere bir ilk değer vermek için ilk değer verme listesi içeren constructor fonksiyonlarını kullanabiliriz. İlk değer verme listesi içeren constructor fonksiyonlarının genel yapısı aşağıda gösterilmektedir:

constructor-adı(veri-türü1 değer1, veri-türü2 değer2, ... ) : değişken-adı1(değer1), değişken-adı2(değer2), ...
{ 
  // Kod
}

İlk değer verilecek olan değişkenler constructor fonksiyonunun adından sonra yer alır ve fonksiyon kodlarından : işareti ile ayrılır.

Şimdi, ilk değer verme listesi içeren explicit constructor fonksiyonlarının kullanımını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    const int priid1, priid2;
  public:
    sinif(int pid1, int pid2):priid1(pid1), priid2(pid2) { }
    void deger_goster();
};

void sinif::deger_goster()
{
  cout << "Sınıf değişken değerleri: " << priid1 << " " << priid2 << endl;
}

int main(void)
{
  sinif nes(21, 35);
  nes.deger_goster();

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

Sınıf değişken değerleri: 21 35

Program, sinif adlı bir sınıf oluşturur. Sınıf içinde, iki adet const private int değişken ve bu değişkenlere nesne oluştururken değer atayan ve bir ilk değer verme listesi içeren constructor fonksiyonu ve değişken değerlerini ekrana yazan deger_goster() adlı bir fonksiyon tanımlar.

Program çalışmaya başladığında, sinif cinsinden nes adlı bir nesne bildirimi yaparken sınıf içindeki değişkenlere, constructor fonksiyonu yoluyla değer atar. Nesne yoluyla, deger_goster() fonksiyonu ile değişken değerlerini ekrana yazar.

Constructor ve destructor fonksiyonlarının çalışma sırası

Bir sınıftan bir nesne oluşturulduğunda constructor fonksiyonu, yok edildiğinde ise destructor fonksiyonu çağrılır.

Ancak global ve lokal nesne bildirimlerinde bir farklılık meydana gelir. Nesne bildirimi global olarak yapıldığında, constructor fonksiyonları main() fonksiyonunun çalışmasından önce bildirim sırasına göre, destructor fonksiyonları ise main() fonksiyonunun çalışması sona erdikten sonra bildirim sırasının tersine olacak şekilde devreye girer.

Nesne bildirimi lokal olarak yapıldığında, bildirim yapılır yapılmaz constructor fonksiyonu çağrılır. Bildirimi ilk yapılan nesneden başlamak üzere constructor fonksiyonları devreye girer. Destructor fonksiyonları ise bildirimi en son yapılan nesneden başlamak üzere devreye girer.

Constructor fonksiyonları, önce global sonra yerel nesneler olmak üzere, bildirim sırasına göre çalışır. Destructor fonksiyonları, önce yerel sonra global nesneler olmak üzere, bildirim sırasının tersine çalışır.

Şimdi bu sistemin çalışmasını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    int priid;
  public:
    sinif (int pid);
    ~sinif();
} gnes1(7), gnes2(8); // global nesne bildirimleri

sinif::sinif(int pid)
{
  cout << "Nesne oluşturuluyor: " << pid << endl;
  priid = pid;
}

sinif::~sinif()
{
  cout << "Nesne yok ediliyor: " << priid << endl;
}

int main(void)
{
  sinif lnes01(21); // lokal nesne bildirimi

  sinif lnes02(35); // lokal nesne bildirimi

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

Nesne oluşturuluyor: 7
Nesne oluşturuluyor: 8
Nesne oluşturuluyor: 21
Nesne oluşturuluyor: 35
Nesne yok ediliyor: 35
Nesne yok ediliyor: 21
Nesne yok ediliyor: 8
Nesne yok ediliyor: 7

Program 2 adet global ve 2 adet lokal nesne bildirimi yapar. Nesnelerin oluşturulma ve yok edilme anındaki değişken değerlerini ekrana yazar.

Friend fonksiyonlar

Bir sınıf içinde yer alan private ve protected değişken ve fonksiyonlara sadece sınıf içinde yer alan üye fonksiyonlar erişim sağlayabilirler. Ancak, sınıf içinde üye olarak yer almayan bir fonksiyonun bildirimini, sınıfın public elemanlarının tanımlandığı bölümde başında friend anahtar kelimesini kullanarak yaparsak, bu fonksiyona private ve protected değişken ve fonksiyonlara erişim hakkını verebiliriz.

class sınıf-adı {
  // private veri ve fonksiyonlar
  private:
     .
     .
     .
      
  // public veri ve fonksiyonlar
  public:
     friend void fonksiyon-adı(); 
     .
     . 
     .  
};

Şimdi, friend fonksiyonların kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    int priid;

  protected:
    int proid;

  public:
    int pubid;
    sinif(int pid1, int pid2, int pid3);
    void deger_ata(int pid1, int pid2, int pid3);
    void deger_goster();
    friend void deger_al(sinif sin);
};

// Sınıf fonksiyonlarının kod blok tanımlamaları
sinif::sinif(int pid1, int pid2, int pid3)
{
  cout << "constructor fonksiyonu ile sınıf değişkenlerine değer atama" << endl;
  priid = pid1; proid = pid2; pubid = pid3;
}

void sinif::deger_ata(int pid1, int pid2, int pid3)
{
  cout << "Üye fonksiyon ile sınıf değişkenlerine değer atama" << endl;
  priid = pid1; proid = pid2; pubid = pid3;
}

void sinif::deger_goster()
{
  cout << "sınıf değişken değerleri: " << priid << " " << proid << " " << pubid << endl;
}

// Friend fonksiyon kod bloğu tanımlama
void deger_al(sinif sin)
{
  // Sınıf içindeki tüm değişkenlere, private dahil, doğrudan erişim
  cout << "Friend fonksiyondan sınıf değişken değerlerini okuma: ";
  cout << sin.priid << " " << sin.proid << " " << sin.pubid << endl;
}

int main(void)
{
  // Nesne oluştururken sınıf değişkenlerine değer atama
  sinif nes(9, 25, 174);
  nes.deger_goster();

  // Sınıf değişken değerlerini friend fonksiyon ile okuma
  // Oluşturulan nesne friend fonksiyona parametre olarak geçiriliyor.
  deger_al(nes);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

constructor fonksiyonu ile sınıf değişkenlerine değer atama
sınıf değişken değerleri: 9 25 174
Friend fonksiyondan sınıf değişken değerlerini okuma: 9 25 174

Program, sinif adlı bir sınıf oluşturur. Sınıf içinde de private, protected ve public olmak üzere üç adet değişken ve bu değişkenlere nesne oluştururken değer atayan bir constructor fonksiyonu, nesne ile değişkenlere değer atayan deger_ata() adlı bir fonksiyon ve değişkenleri ekrana yazan deger_goster() adlı bir fonksiyon tanımlar. Ayrıca, sınıf içinde, kendisine parametre olarak geçirilen sinif veri türünden bir nesne ile sınıf içindeki değişken değerlerini ekrana yazan bir friend fonksiyon bildirimi yapar.

Program çalışmaya başladığında, sinif cinsinden nes adlı bir nesne bildirimi yaparken sınıf içindeki değişkenlere constructor fonksiyonu yoluyla birer değer atar. Nesne yoluyla, deger_goster() fonksiyonu ile değişken değerlerini ekrana yazar. Sonra, deger_al() friend fonksiyonuna nes nesnesini parametre olarak geçirerek sınıf içindeki değişken değerlerini ekrana yazar.

Yukarıdaki örnekte, deger_al() fonksiyonu friend özelliği taşıdığından, nesne yoluyla çağrılmaz, sadece nesne parametre olarak geçirilir.

Friend fonksiyonları çağırmak için nesne yoluyla . (nokta) işlemcisi kullanılmaz. Nesne fonksiyona argüman olarak geçirilerek private değişkenler de dahil olmak üzere tüm sınıf değişkenlerine erişim sağlanır.

Friend fonksiyonlar daha çok birden fazla sınıfta yer alan private değişkenleri tek bir fonksiyonla kontrol etmek ve işlem yapmak için kullanılabilir. Bu durumda, yapılacak işlem her iki sınıfta yer alan değişken değerleri tarafından da etkilenmektedir.

Aşağıdaki örnekte, iki farklı sınıfta yer alan private değişken değerlerinden büyük olanının değerini ekrana yazmak için, iki sınıf içinde, her iki sınıf tarafından ortaklaşa kullanılmak üzere, friend olarak tanımlanmış bir fonksiyon yer almaktadır:


#include <iostream>

using namespace std;

// sinif2 sınıfı ön bildirimi
class sinif2;

class sinif1 {
  private:
    int priid;
  public:    
    sinif1(int pid); // Constructor fonksiyon bildirimi
	friend void buyuk_deger(sinif1 sin1, sinif2 sin2);
};

class sinif2 {
  // Sadece sınıf fonksiyonlarının erişebileceği private değişkenler
    int priid;
  public:    
    sinif2(int pid); // Constructor fonksiyon bildirimi
	friend void buyuk_deger(sinif1 sin1, sinif2 sin2);
};

// Fonksiyonların kodlarının yer aldığı ana yapı bildirimleri
sinif1::sinif1(int pid)
{
  priid = pid;
}

sinif2::sinif2(int pid)
{
  priid = pid;
}

// Her iki sınıf için friend olarak tanımlanan fonksiyon ana yapı bildirimi
void buyuk_deger(sinif1 sin1, sinif2 sin2)
{
  // Sınıflar içindeki private değişkenlere direk erişim
  if (sin1.priid > sin2.priid) cout << "Büyük olan değer sinif1 nesnesi: " << sin1.priid << endl;
  else if (sin1.priid < sin2.priid) cout << "Büyük olan değer sinif2 nesnesi: " << sin2.priid << endl;
  else cout << "Değerler eşit" << endl;
}

int main(void)
{
  // Nesne oluştururken sınıf değişkenlerine değer atama
  sinif1 nes1(121);
  sinif2 nes2(35);

  // Sınıf değişken değerlerini friend fonksiyon ile okuma
  // Oluşturulan nesne friend fonksiyona parametre olarak geçiriliyor.
  buyuk_deger(nes1, nes2);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, zaman aşağıdaki ifadeleri ekrana yazar:

Büyük olan değer sinif1 nesnesi: 121

sinif2 sınıfı, sinif1 içindeki friend fonksiyon bildirimi içinde kullanıldığından, sinif1 bildiriminden önce sinif2 için bir ön bildirim yapılması gerekmektedir.

Bir sınıf için friend olarak tanımlanan bir fonksiyon diğer bir sınıf için üye olarak tanımlanabilir. Bu durumda friend fonksiyonun üyesi olduğu sınıftan oluşturulan nesnenin friend fonksiyona argüman olarak geçirilmesine gerek kalmaz.

Aşağıdaki programın bir önceki programdan tek farkı, friend fonksiyonun sadece tek bir sınıf için tanımlanmış olmasıdır:


#include <iostream>

using namespace std;

// sinif2 sınıfı ön bildirimi
class sinif2;

class sinif1 {
  private:
    int priid;
  public:
    sinif1(int pid); // Constructor fonksiyon bildirimi
	void buyuk_deger(sinif2 sin2);
};

class sinif2 {
  // Sadece sınıf fonksiyonlarının erişebileceği private değişkenler
    int priid;
  public:
    sinif2(int pid); // Constructor fonksiyon bildirimi
	friend void sinif1::buyuk_deger(sinif2 sin2);
};

// Fonksiyonların kodlarının yer aldığı ana yapı bildirimleri
sinif1::sinif1(int pid)
{
  priid = pid;
}

sinif2::sinif2(int pid)
{
  priid = pid;
}

// Friend olarak tanımlanan fonksiyon ana yapı bildirimi
void sinif1::buyuk_deger(sinif2 sin)
{
  // Sınıflar içindeki private değişkenlere direk erişim
  if (priid > sin.priid) cout << "Büyük olan değer sinif1 nesnesi: " << priid << endl;
  else if (priid < sin.priid) cout << "Büyük olan değer sinif2 nesnesi: " << sin.priid << endl;
  else cout << "Değerler eşit" << endl;
}

int main(void)
{
  // Nesne oluştururken sınıf değişkenlerine değer atama
  sinif1 nes1(121);
  sinif2 nes2(35);

  // Fonksiyon sinif1 sınıfının üyesi olduğundan, sin1 nesnesi ile çağrılıyor.
  // Sadece fonksiyonun friend olarak tanımlandığı sinif2 nesnesi parametre olarak geçiriliyor.
  nes1.buyuk_deger(nes2);

  return 0;
}

Yukarıdaki programda, buyuk_deger() fonksiyonu sinif1 sınıfı için üye, sinif2 sınıfı için friend olarak tanımlanmıştır. buyuk_deger() fonksiyonu üyesi olduğu sinif1 değişkenlerine direk olarak erişebileceğinden, sinif1 sınıfından üretilen nesne bu fonksiyona parametre olarak geçirilmemektedir. Fonksiyon kod ana bloğu içinde private değişkenlere direk olarak erişmektedir. Ayrıca, fonksiyon sinif1 sınıfının üyesi olduğundan, sinif1 nesnesi yoluyla çağrılmaktadır.

Friend fonksiyonlar ile ilgili aşağıdaki sınırlamaları dikkate alınız:

1. Türetilen sınıflar friend fonksiyonları devralamaz.

2. Friend fonksiyonlarla birlikte storage-class tanımlayıcıları (extern, static, register, auto) kullanılamazlar.

Friend sınıflar

C++'da bir sınıfı diğer bir sınıfın içinde friend olarak tanımlayabiliriz. Bu durumda, friend sınıf olarak tanımlanan bir sınıf ve bu sınıfa ait tüm üye fonksiyonlar, içinde tanımlandığı sınıfın tüm private elemenlarına erişim sağlayabilir.

Şimdi, friend sınıf kavramını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif1 {
  private:
    int priid;
  public:
    void deger_ata(int pid)
    {
      priid = pid;
    }
    void deger_goster()
    {
      cout << "sinif1 değişken değeri: " << priid << endl;
    }
    friend class sinif2;
};

class sinif2 {
  public:
    void deger_oku(sinif1 sin)
    {
      cout << "Friend sınıf içinden sinif1 değişken değeri okuma: " << sin.priid;
    }
};

int main(void)
{
  sinif1 nes1;
  sinif2 nes2;

  nes1.deger_ata(7);
  nes1.deger_goster();

  nes2.deger_oku(nes1);

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

sinif1 değişken değeri: 7
Friend sınıf içinden sinif1 değişken değeri okuma: 7

Program, içinde bir adet priid adlı private int bir değişken, bu değişkene bir değer atayan deger_ata() adlı bir fonksiyon ve bu değişken değerini ekrana yazan deger_goster() adlı bir fonksiyon ile sinif2 adlı fonksiyonu friend olarak tanımlayan bir ifade içeren, sinif1 adlı bir sınıf bildirimi yapar. Sonra, parametre olarak sinif1 türünden bir nesne alan ve bu nesne yoluyla sinif1 içindeki priid değişken değerini okuyan deger_oku() adlı bir fonksiyon içeren sinif2 adlı bir sınıf bildirimi yapar.

Önce, sinif1 ve sinif2 sınıf türlerinden birer adet nes1 ve nes2 adlı nesne bildirimi yapar. nes1 nesnesi yoluyla deger_ata() fonksiyonunu kullanarak, priid değişkenine 7 değerini atar ve deger_goster() fonksiyonu ile değişken değerini ekrana yazar. nes2 nesnesi yoluyla deger_oku() fonksiyonunu kullanarak, sinif1 içindeki priid değişken değerini ekrana yazar.

Bir sınıf diğer bir sınıfın içinde friend olarak tanımlandığında, friend sınıf sadece diğer sınıfın tüm elemanlarına erişim sağlar. Diğer sınıfın elemanlarına kendi elemanları gibi işlem yapamaz.

const üye fonksiyonlar

Bir sınıf içindeki üye fonksiyonları const olarak tanımladığımızda, bu sınıftan oluşturulan bir nesne ile çağırdığımız const fonksiyon nesne içinde yer alan değişken değerlerini değiştiremez.

Bir üye fonksiyonu const olarak tanımlamada kullanılan genel yapı aşağıda gösterilmektedir:

veri-türü fonksiyon-adı(param1, param2, ...) const;

Şimdi, const üye fonksiyonların kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    int priid;

  public:
    void deger_ata(int pid) { priid = pid; };
    // const fonksiyon bildirimi
    void deger_goster(void) const
    {
      cout << "priid değişken değeri: " << priid;
    };
};

int main(void)
{
  sinif nes;

  nes.deger_ata(21);
  nes.deger_goster(); // const fonksiyonu çağırma

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

priid değişken değeri: 21

Program, priid adlı private int bir değer, bu int değere bir değer atayan deger_ata() adlı bir fonksiyon ve priid değişken değerini ekrana yazan deger_goster() adlı const bir fonksiyon içeren bir sınıf oluşturur. Bu sınıftan nes adlı bir nesne oluşturur. Sonra, deger_ata() fonksiyonu ile priid değişkenine 21 değeri atar. Daha sonra, deger_goster() fonksiyonunu kullanarak, priid değişken değerini ekrana yazar.

Burada, deger_goster() fonksiyonu const olarak tanımlandığından, sınıf içindeki değişkene sadece okuma işlemi yapabilmektedir. Aksi takdirde, program derleme hatası verir.

Bir sınıf içinde const olarak tanımlanan bir fonksiyonun, sınıf içindeki bir değişken değerini değiştirebilmesi için, değişkeninin mutable olarak tanımlanmış olması gerekir.

Şimdi, mutable olarak tanımlanmış değişkenler ile const üye fonksiyonların kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    mutable int priid;

  public:
    // const fonksiyon bildirimleri
	void deger_ata(int pid) const { priid = pid; };    
    void deger_goster(void) const
    {
      cout << "priid değişken değeri: " << priid;
    };
};

int main(void)
{
  sinif nes;

  // const fonksiyonları çağırma
  nes.deger_ata(21);
  nes.deger_goster();

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

priid değişken değeri: 21

Program, priid adlı private int bir değer, bu int değere bir değer atayan deger_ata() adlı ve priid değişken değerini ekrana yazan deger_goster() adlı iki adet const fonksiyon içeren bir sınıf oluşturur. Bu sınıftan nes adlı bir nesne oluşturur. Sonra, deger_ata() fonksiyonu ile priid değişkenine 21 değeri atar. Daha sonra, deger_goster() fonksiyonunu kullanarak, priid değişken değerini ekrana yazar.

Burada, deger_ata() fonksiyonu const olarak tanımlandığı halde, sınıf içindeki değişken değerini değiştirebilmektedir. Bunun nedeni, değişkenin mutable olarak tanımlanmasıdır.

Sınıf ve yapı (struct)

C++ içinde C programlama dilinin bir parçası olan yapıları sınıf tanımlaması yapmak için kullanabilirsiniz. Çünkü, yapılar sınıflarla çok büyük benzerlikler göstermektedir.

Sınıf ve yapı arasındaki en önemli fark, sınıflarda tüm elemanların ön tanımlı olarak private, yapılarda ise public olmasıdır.

Şimdi, yapıların kullanılmasını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

struct yapi {
  // struct içinde elemanlar ön tanımlı olarak public olduğundan, public erişim tanımlayıcı kullanılmaz.
    int pubid;
    void deger_ata(int pid1, int pid2, int pid3);
    void deger_goster();

  private:
    int priid;

  protected:
    int proid;
};

void yapi::deger_ata(int pid1, int pid2, int pid3)
{
  cout << "Yapı değişkenlerine değer atama" << endl;
  priid = pid1; proid = pid2; pubid = pid3;
}

void yapi::deger_goster()
{
  cout << "Yapı değişken değerleri: " << priid << " " << proid << " " << pubid << endl;
}

int main(void)
{
  // Nesne oluşturma
  yapi nes;

  // Üye fonksiyon ile yapı değişkenlerine değer atama
  nes.deger_ata(7, 21, 135);
  nes.deger_goster();

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

Yapı değişkenlerine değer atama
Yapı değişken değerleri: 7 21 135

Program, yapi adlı bir struct oluşturur. Yapı içinde de private, protected ve public olmak üzere üç adet değişken, nesne ile değişkenlere değer atayan deger_ata() adlı bir fonksiyon ve değişkenleri ekrana yazan deger_goster() adlı bir fonksiyon tanımlar.

Program çalışmaya başladığında, yapi türünden nes adlı bir nesne bildirimi yapar. Nesne yoluyla, deger_ata() fonksiyonu ile yapı içindeki değişkenlere birer değer atar, sonra deger_goster() fonksiyonu ile değişken değerlerini ekrana yazar.

Bir yapı içinde elemanlar ön tanımlı olarak public olduğundan, programda public erişim tanımlayıcısı kullanılmamaktadır.

Sınıf ve bileşim (union)

C++'da bileşimler de bir sınıf tanımlamak için kullanılabilir. Bileşimler içinde, tıpkı sınıflarda olduğu gibi, üye fonksiyon ve değişkenler ile constructor ve destructor fonksiyonları yer alabilir. Ön tanımlı olarak, tüm elemanlar public özelliğe sahiptir.

Bileşimlerin en büyük özelliği tüm değişken elemanlarının aynı bellek bölgesini paylaşmasıdır.

C++'da bileşimler ile ilgili olarak aşağıdaki sınırlamaları dikkate almamız gerekmektedir:

1. Bileşimler bir ana sınıf olarak kullanılamaz ve herhangi bir sınıftan türetilemezler.

2. Bileşimler içinde virtual üye fonksiyonlar ve statik değişkenler yer alamaz.

Şimdi bir örnek ile bileşim yoluyla tanımlanan sınıf kullanımını inceleyelim:


#include <iostream>
#include <cstdlib>

using namespace std;

// Bileşim yoluyla sınıf tanımlaması
union bil {
  char cd;
  int id;

  void deg_boyut_deger_al();
  bil (int pid); // Constructor fonksiyon bildirimi
};

// Fonksiyon kodları
bil::bil(int pid)
{
  id = pid;
}

void bil::deg_boyut_deger_al()
{
  cout << "Bileşim boyutu: " << sizeof(bil) << endl;
  cout << "id değişken boyutu: " << sizeof(id) << endl;
  cout << "cd değişken boyutu: " << sizeof(cd) << endl;

  cout << "id değişken değeri: " << id << endl;
  cout << "cd değişken değeri: " << cd << endl;
}

void IntToBin(int val);

int main(void)
{
  // Nesne oluşturma
  bil bd (46401);

  // Nesne yoluyla değer okuma
  bd.deg_boyut_deger_al();

  IntToBin(bd.id);

  return 0;
}

void IntToBin(int val)
{
  int bitsayi = sizeof(val) * 8;   // Integer değerin bit adet değeri
  char *cdizi = (char*) malloc(bitsayi+1); // Dizi sonu '\0' karakteri için
  cdizi[bitsayi] = '\0';
  // En soldaki bit'in negatif değerler sorununu önlemek için unsigned değişken olarak işlem yapma
  unsigned int u = *(unsigned int*)&val;
  // 1 sayısını bit genişliğinden bir düşük değer kadar sola kaydırarak 32 bit'in en soldaki bit'ini 1 diğer bitleri 0 yapar.
  unsigned int mask = 1 << (bitsayi-1);
  int id;

  for (id=0; id<bitsayi; id++, mask >>= 1) {
      // Döngü değişkeninin her artışında mask değerinin en solundaki 1 değeri bir sağa kayar
      cdizi[id] = (u & mask) ? '1' : '0';
  }

  cout << val << " " << cdizi << endl;
  free(cdizi);
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

Bileşim boyutu: 4
id değişken boyutu: 4
cd değişken boyutu: 1
id değişken değeri: 46401
cd değişken değeri: A
46401 00000000000000001011010101000001

Sınıf tanımlamasının bileşim yoluyla yapıldığı programda, sınıf içinde tanımlanan bir adet char ve bir adet int değişken aynı bellek bölgesini paylaştıklarından, int değişkene atanan değer char değişkenin de değerini belirlemektedir.

İsimsiz bileşimler

C++'da İsimsiz bileşim adı verilen özel bir bileşim türü vardır. Bu bileşim türünde, bildirimi yapılan tür için bir ad tanımlanmaz. Ayrıca, bu bileşimden herhangi bir nesne bildirimi yapılmaz.

İsimsiz bileşimlerde de, üye değişkenler bellekte aynı bölgeyi paylaşırlar, ancak bu değişkenlere erişim için . (nokta) işlemcisi yerine direk erişim yöntemi kullanılır.

İsimsiz bileşimlerdeki değişkenlere lokal değişkenler gibi erişim sağlanır. Ancak, bu durumda da, bileşim içinde yer alan değişken adları program içindeki diğer lokal değişken adları ile farklı olmalıdır. Aksi takdirde, program hata verir.

C++'da isimsiz bileşimler ile ilgili olarak aşağıdaki sınırlamaları dikkate almamız gerekmektedir:

1. İçinde yer alan elemanlar sadece veri (değişken) olabilir, üye fonksiyon tanımlanmaz.

2. Değişkenler sadece public olarak tanımlanabilir.

3. Global olarak tanımlandıklarında bildirim static yapılmalıdır.


#include <iostream>
#include <cstdlib>

using namespace std;

void IntToBin(int val);

int main(void)
{
  // İsimsiz bileşim tanımlaması
  union {
    char cd;
    int id;
  };

  id = 46401;

  cout << "id değişken değeri: " << id << endl;
  cout << "cd değişken değeri: " << cd << endl;

  IntToBin(id);

  return 0;
}

void IntToBin(int val)
{
  int bitsayi = sizeof(val) * 8;   // Integer değerin bit adet değeri
  char *cdizi = (char*) malloc(bitsayi+1); // Dizi sonu '\0' karakteri için
  cdizi[bitsayi] = '\0';
  // En soldaki bit'in negatif değerler sorununu önlemek için unsigned değişken olarak işlem yapma
  unsigned int u = *(unsigned int*)&val;
  // 1 sayısını bit genişliğinden bir düşük değer kadar sola kaydırarak 32 bit'in en soldaki bit'ini 1 diğer bitleri 0 yapar.
  unsigned int mask = 1 << (bitsayi-1);
  int id;

  for (id=0; id<bitsayi; id++, mask >>= 1) {
      // Döngü değişkeninin her artışında mask değerinin en solundaki 1 değeri bir sağa kayar
      cdizi[id] = (u & mask) ? '1' : '0';
  }

  cout << val << " " << cdizi << endl;
  free(cdizi);
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

id değişken değeri: 46401
cd değişken değeri: A
46401 00000000000000001011010101000001

Program main() fonksiyonu içinde tanımlanan isimsiz bileşim içindeki değişkenlere direk erişim sağlayarak değer atar ve değerleri ekrana yazar.

Inline fonksiyonlar

C++'da, genellikle sınıflarla birlikte kullanılan inline fonksiyonlar oluşturulduğunda, derleyici derleme zamanında bu fonksiyonun çağrıldığı her yere fonksiyonun bir kopyasını yani tüm kod bloğunu yerleştirir. Böylece, fonksiyonun her çağrılışında, gerçek anlamda fonksiyon çağrılmadığından, program daha hızlı bir şekilde çalışır.

Inline fonksiyonunun genel yapısı aşağıdadır:

inline geri-dönüş-değeri fonksiyon-adı(parametre1, parametre2, ...)
{
  işlem satırı;
  .
  .  
}

Bir fonksiyonu inline olarak tanımlamak için fonksiyon ana yapısında yer alan ilk satırın en başına inline ifadesi getirilmelidir. Eğer bir sınıf içinde tanımlanan fonksiyon kodları yine sınıf içinde yapılmışsa, bu fonksiyon da inline ifadesi kullanılmadan inline fonksiyon olarak tanımlanmış olur.

Inline fonksiyon kullanma nedenleri

Normal fonksiyon kullanımında, Program fonksiyon çağrısına işlem yaptığında, CPU fonksiyon çağrısından sonraki komutun bellek adresini depolar, fonksiyonun parametrelerini yığına kopyalar ve kontrolü fonksiyona aktarır. CPU daha sonra fonksiyon kodlarını çalıştırır, fonksiyonun geri döndürdüğü değeri önceden tanımlanmış bir bellek konumuna veya yazmaca aktarır ve kontrolü çağıran program satırına geri döndürür.

Bu durumda, eğer fonksiyonun çalışma süresi, fonksiyon çağırma işlemi için geçen süreden daha az ise, sistem için gereksiz bir yük oluşturabilir. Eğer fonksiyon büyük çaplı ve karmaşık işlemler gerçekleştiriyorsa, fonksiyon çağrısı için geçen zaman, fonksiyonun çalışma süresine kıyasla genellikle önemsizdir. Bununla birlikte, küçük ve yaygın olarak kullanılan fonksiyonlar için, fonksiyon çağrısı yapmak için gereken süre genellikle fonksiyon kodunu çalıştırmak için gereken zamandan çok daha fazla olduğundan, sistem için ek bir yük oluşturur.

Inline fonksiyon kullanırken, tercihimizi küçük ve yaygın olarak kullanılan fonksiyonlardan yana kullanmamız daha doğru bir tercih olacaktır.

C++'da, inline fonksiyonların kullanılmasının temel nedeni, fonksiyon çağrılarının ek yükünü azaltmaktır.

Şimdi, inline fonksiyon uygulamasını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

inline void deger_goster(int deger) {
  cout << deger << endl;
}

int main(void) {
  deger_goster(21);
  deger_goster(145);
	
  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

21
145

Aslında, derleyici yukarıdaki programı aşağıdaki şekilde yazılmış gibi işlem yapar:


#include <iostream>

using namespace std;

int main(void) {
  cout << 21 << endl;
  cout << 145 << endl;
	
  return 0;
}

Şimdi, inline fonksiyonunu sınıf ve sınıf dışında tanımlama işlemini bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  private:
    int priid;
  public:
    // Sınıf içinde inline fonksiyon bildirimi (inline kelimesini kullanmaya ihtiyaç yoktur)
    void deger_ata(int pid)
    {
      priid = pid;
    }
    void deger_goster(void);
};

// Sınıf üye fonksiyonunun sınıf dışında inline fonksiyon bildirimi
inline void sinif::deger_goster()
{
  cout << "priid değişken değeri: " << priid << endl;
}

// Sınıfta yer almayan inline fonksiyon bildirimi
inline int kare_al(int deger)
{
  return deger*deger;
}

int main(void)
{
  sinif nes;

  nes.deger_ata(457);
  nes.deger_goster();

  cout << kare_al(17) << endl;

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

priid değişken değeri: 457
289

Program, herhangi bir sınıfa ait olmayan kare_al() adlı bir fonksiyon ve sinif sınıfına ait iki adet inline fonksiyon bildirimi yapar. Sınıfa ait olan inline fonksiyonlardan kod bloğu sınıf dışında tanımlanan fonksiyon için inline ifadesi kullanır, ancak kod bloğu sınıf içinde tanımlanan fonksiyon için inline ifadesi kullanmaz.

Normal fonksiyon çağrılarında, fonksiyon argümanları stack'e atılır ve bazı register değerleri değiştirilir. Fonksiyonun çalışması sona erdiğinde ise, stack ve register değerleri eski haline döner. Normal fonksiyon çağrılarında gerçekleşen ve zaman alan bu işlemleri yapmadan fonksiyonları kullanmak için inline fonksiyonlar kullanılır.

Inline fonksiyonlar daha hızlı çalışır, ancak program boyutlarını büyütür. Bu nedenle, inline fonksiyonların küçük boyutlu olması tercih edilir ve önerilir.

Static sınıf elemanları

Bir sınıf içinde yer alan fonksiyon ve veriler static olarak tanımlanabilir.

Static veri elemanları (değişkenler)

Sınıf içinde normal değişken tanımladığımızda, bu sınıftan türeteceğimiz her bir nesne içinde bu değişken için farklı bir değer oluşturulmuş olur. Ancak, bir sınıf içinde tanımladığımız değişkeni static olarak tanımlarsak, bu sınıftan üreteceğimiz tüm nesneler bu değişken için verilen sadece tek bir değeri paylaşabilir ve kullanabilirler. Sınıftan ilk nesne oluşturulmadan önce, tüm static değişkenlere ilk değer olarak 0 (sıfır) değeri verilir.

Sınıf içinde static değişken tanımladığımızda, tüm nesneler aynı değeri kullanacağından, sınıf dışında programın herhangi bir yerinde, sınıf adı ile birlikte :: işlemcisi kullanarak, global olarak static değişkenler için bir tanımlama yapmamız gerekir.

Şimdi bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
    static int id1;
    int id2;
  public:
    void deger_ata (int pid1, int pid2)
    {
      id1 = pid1;
      id2 = pid2;
    }
    void deger_goster(void);
};

int sinif::id1; // Static sınıf değişkeni için global bildirim

int main(void)
{
  sinif nes1, nes2;

  nes1.deger_ata(10, 25);
  nes1.deger_goster();

  // static id1 değişkenine atanan değer sin1 nesnesi için de geçerlidir.
  nes2.deger_ata(34, 42);
  nes2.deger_goster();

  nes1.deger_goster();

  return 0;
}

void sinif::deger_goster(void)
{
  cout << id1 << " " << id2 <<  endl;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

10 25
34 42
34 25

Program sinif sınıfı içinde bir adet normal ve bir adet static olmak üzere 2 adet int değişken bildirimi yapar. Sınıftan türetilen iki farklı nesne yoluyla her iki değişkene farklı değerler atar ve değerleri ekrana yazar. İkinci nesne yoluyla static id1 değişkenine atadığı 34 değeri, ilk nesnenin kullandığı değeri de otomatik olarak değiştirir.

Static sınıf değişkenleri için programın sınıf dışındaki bir yerinde ayrıca bir bildirim yapıldığı için, sınıftan herhangi bir nesne oluşturulmasa bile static değişkenler için bellekte yer ayrılmış olduğundan, bu değişkenlere değer atanabilir ve kullanılabilir. Ancak, direk erişim için static değişkenin sınıf içinde public olarak tanımlanması gerekir.

Şimdi bu özelliği bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinif {
  public:
    static int id;

    void deger_ata (int pid) { id = pid; }
    void deger_goster();
};

int sinif::id; // Static sınıf değişkeni için global bildirim

int main(void)
{
  sinif::id = 24;

  sinif nes;

  nes.deger_goster();

  nes.deger_ata(36);

  nes.deger_goster();

  return 0;
}

void sinif::deger_goster()
{
  cout << id << endl;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

24
36

Program sinif sınıfı içinde tanımladığı static değişkene bir nesne oluşturmadan atadığı değeri, nesne oluşturduktan sonra nesne yoluyla ekrana yazar. Daha sonra, aynı değişkene nesne yoluyla atadığı değeri ekrana yazar.

Static sınıf fonksiyonları

Bir önceki örnekte sınıf içindeki static bir değişkene nesne oluşturmadan erişim sağlamak public olarak tanımlamıştık. Aynı değişkeni private olarak tanımlayıp erişim sağlamak için sınıf içinde static olarak tanımladığımız fonksiyonları kullanabiliriz. Static fonksiyonları kullanmak için de bir nesne oluşturmamız gerekmez.

Bir sınıf içinde static olarak tanımlanan fonksiyonların bazı sınırlamaları vardır:

  • Static fonksiyonlar sadece sınıf içindeki static değişkenlere erişim sağlayabilir.
  • Bir nesne ile ilgili olmadığından, this işlemcisi ile kullanılamaz.
  • Virtual olarak tanımlanamazlar.
  • const veya volatile olarak tanımlanamazlar.

#include <iostream>

using namespace std;

class sinif {
    static int id;

  public:
    void deger_ata (int pid) { id = pid; }
    static void deger_goster();
};

int sinif::id = 17;      // Static sınıf değişkeni için global bildirim ve değer atama

int main(void)
{
  sinif::deger_goster(); // Static fonksiyonun sınıf adı ile kullanımı

  sinif nes;

  nes.deger_ata(29);
  nes.deger_goster();   // Static fonksiyonun nesne yoluyla kullanımı

  return 0;
}

void sinif::deger_goster()
{
  cout << id << endl;
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

17
29

Program sinif sınıfı içinde private tanımladığı static değişkene, bir nesne oluşturmadan global bildirim yoluyla, atadığı değeri static bir fonksiyon yoluyla ekrana yazar. Daha sonra, aynı değişkene nesne yoluyla atadığı değeri ekrana yazar.

Lokal sınıflar

C++'da, sınıf bildirimlerini bir fonksiyon içinde yapabilirsiniz. Ancak bu durumda, sınıflar tıpkı lokal değişkenler gibi sadece fonksiyon içinde geçerli olur ve erişilebilirler.

Lokal sınıfların pratik olarak kullanımını etkileyen bazı sınırlamalar vardır:

  • Bütün üye fonksiyonlara ait kodlar sınıf bildirimi içinde yer almalıdır.
  • İçinde tanımlandıkları fonksiyonlardaki static olmayan lokal değişkenlere erişemezler.
  • İçinde static değişken tanımlamaz.

Şimdi, lokal sınıf kullanımını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

void lokal_sinif_fonk();

int main(void)
{
  lokal_sinif_fonk();
  
  return 0;
}

void lokal_sinif_fonk()
{
  class sinif {
      int id; 

    public:
      void deger_ata(int pid) { id = pid; }
      void deger_goster() { cout << id; }
  };

  sinif nes;
  
  nes.deger_ata(17);  
  nes.deger_goster();
}

Yukarıdaki programı derleyip çalıştırdığımızda, aşağıdaki ifadeleri ekrana yazar:

17

Program lokal_sinif_fonk() fonksiyonu içinde bildirimini yaptığı sinif lokal sınıfı içinde tanımladığı değişkene atadığı değeri ekrana yazar.

Soyut (Abstract) sınıflar

Bir sınıfın içinde en az bir adet saf sanal fonksiyon tanımı yapılarak soyut (abstract) sınıflar oluşturulur.

Bir ana sınıf içinde kod bloğu içermeyecek ve bildirim satırında 0 değerine eşit olacak şekilde bildirimi yapılan sanal fonksiyonlara saf sanal fonksiyon adı verilir. Saf sanal fonksiyonların bildirimi aşağıdaki şekilde yapılır:

class ana-sınıf {
  public:
    virtual veri-türü fonk-adı(parametreler) = 0;
}

class türetilen-sınıf public: ana-sınıf {
  public:
    veri-türü fonk-adı(parametreler)
    {
      // kod bloğu   
    }
}
  • Bir sınıf içinde en az bir adet saf sanal fonksiyon tanımlanırsa, bu sınıf soyut (abstract) sınıf haline gelir. Soyut sınıflardan nesne oluşturulamaz.
  • Saf sanal fonksiyon bulunan ana sınıflardan türetilen sınıflarda sanal fonksiyon yeniden tanımlanmadığında, türetilen sınıf soyut sınıf olarak kabul edildiğinden, bu sınıflardan nesne oluşturulamaz.
  • Soyut sınıflardan nesne oluşturulmaz, ancak işaretçi ve referans oluşturulabilir.
  • Bir sınıf içinde saf sanal fonksiyon tanımlandığında, bu sınıftan türetilen tüm sınıflarda sanal fonksiyon bildirimi yeniden yapılmalıdır. Eğer yapılmazsa, türetilen sınıflarda soyut sınıf olarak kabul edilir.
  • Soyut sınıflar saf sanal fonksiyon dışında normal fonksiyonlar ve değişkenler içerebilir.

Şimdi, soyut sınıf ve saf sanal fonksiyon kullanımını bir örnek üzerinde incelemeye çalışalım:


#include <iostream>

using namespace std;

class sinifana {

  private:
    int priidana;

  protected:
    // Bu değişkenin sinifturtur sınıfında tek kopyası oluşur.
    int proidana;

  public:
    // Buradaki değişken ve fonksiyonların sinifturtur sınıfında tek kopyası oluşur.
    int pubidana;
    // Saf sanal fonksiyon bildirimi
    virtual void deger_topla(void) = 0;
    void deger_ata(int pid1, int pid2, int pid3)
    {
      priidana = pid1; proidana = pid2; pubidana = pid3;
      cout << "sinifana değişkenlerine değer atama: " << priidana << " " << proidana << " " << pubidana << endl;
    }
    void deger_goster()
    {
      cout << "sinifana değişken değerleri: " << priidana << " " << proidana << " " << pubidana << endl;
    }
};

class siniftur1 : public sinifana {
  private:
    int priidtur1;

  protected:
    int proidtur1;

  public:
    int pubidtur1;
    // Sanal fonksiyonun siniftur1 için yeniden tanımlanması
    virtual void deger_topla(void) {
      cout << "siniftur1 değişken toplamları: "<< priidtur1 + proidtur1 + pubidtur1 << endl;
    }
    void deger_ata_tur1(int pid1, int pid2, int pid3)
    {
      priidtur1 = pid1; proidtur1 = pid2; pubidtur1 = pid3;
      cout << "siniftur1 değişkenlerine değer atama: " << priidtur1 << " " << proidtur1 << " " <<  pubidtur1 << endl;
    }
    void deger_goster_tur1() { cout << "siniftur1 değişken değerleri: " << priidtur1 << " " << proidtur1 << " " << pubidtur1 << endl; }
};

class siniftur2 : public sinifana {
  private:
    int priidtur2;

  protected:
    int proidtur2;

  public:
    int pubidtur2;
    // Sanal fonksiyonun siniftur2 için yeniden tanımlanması
    virtual void deger_topla(void) {
      cout << "siniftur2 değişken toplamları: "<< priidtur2 + proidtur2 + pubidtur2 << endl;
    }
    void deger_ata_tur2(int pid1, int pid2, int pid3)
    {
      priidtur2 = pid1; proidtur2 = pid2; pubidtur2 = pid3;
      cout << "siniftur2 değişkenlerine değer atama: " << priidtur2 << " " << proidtur2 << " " <<  pubidtur2 << endl;
    }
    void deger_goster_tur2() { cout << "siniftur2 değişken değerleri: " << priidtur2 << " " << proidtur2 << " " << pubidtur2 << endl; }
};

int main(void)
{
  sinifana *pnes_ana;
  siniftur1 nes_tur1;
  siniftur2 nes_tur2;

  cout << "nes_tur1 değişkeninin siniftur1 elemanlarına erişimi:" << endl;
  cout << "-----------------------------------------------------" << endl;
  nes_tur1.deger_ata_tur1(249, 587, 1087);
  nes_tur1.deger_goster_tur1();
  pnes_ana = &nes_tur1;    // sinifana türünden işaretçi siniftur1 nesnesinin adresini gösteriyor.
  pnes_ana->deger_topla(); // siniftur1 sanal fonksiyonu çağrılır.

  cout << endl << "nes_tur2 değişkeninin siniftur2 elemanlarına erişimi:" << endl;
  cout << "-----------------------------------------------------" << endl;
  nes_tur2.deger_ata_tur2(725, 1945, 3154);
  nes_tur2.deger_goster_tur2();
  pnes_ana = &nes_tur2;    // sinifana türünden işaretçi siniftur2 nesnesinin adresini gösteriyor.
  pnes_ana->deger_topla(); // siniftur2 sanal fonksiyonu çağrılır.

  return 0;
}

Yukarıdaki programı derleyip çalıştırdığınız zaman aşağıdaki ifadeleri ekrana yazar:

nes_tur1 değişkeninin siniftur1 elemanlarına erişimi:
-----------------------------------------------------
siniftur1 değişkenlerine değer atama: 249 587 1087
siniftur1 değişken değerleri: 249 587 1087
siniftur1 değişken toplamları: 1923

nes_tur2 değişkeninin siniftur2 elemanlarına erişimi:
-----------------------------------------------------
siniftur2 değişkenlerine değer atama: 725 1945 3154
siniftur2 değişken değerleri: 725 1945 3154
siniftur2 değişken toplamları: 5824

Program, sinifana türünden pnes_ana adlı bir işaretçi ile siniftur1 türünden nes_tur1 adlı ve siniftur2 türünden nes_tur2 adlı bir nesne oluşturur. Her üç sınıf içinde de private, protected ve public olmak üzere üç adet değişken ve bu değişkenlere değer atayan, ekranda gösteren ve değerlerini toplayan biri sanal olmak üzere üç adet fonksiyon tanımlar.

İlk olarak, nes_tur1 nesnesi yoluyla siniftur1 içindeki deger_ata_tur1() ve deger_goster_tur1() fonksiyonları ile siniftur1 içindeki değişkenlere birer değer atar ve ekrana yazar. Sonra, nes_tur1 nesnesinin bellek adresini pnes_ana işaretçisine atar. İşaretçi ile siniftur1 içindeki deger_topla() sanal fonksiyonunu kullanarak, siniftur1 içindeki değişken değerlerini toplar.

İkinci safhada, nes_tur2 nesnesi yoluyla siniftur1 içindeki deger_ata_tur2() ve deger_goster_tur2() fonksiyonları ile siniftur2 içindeki değişkenlere birer değer atar ve ekrana yazar. Sonra, nes_tur2 nesnesinin bellek adresini pnes_ana işaretçisine atar. İşaretçi ile siniftur2 içindeki deger_topla() sanal fonksiyonunu kullanarak, siniftur2 içindeki değişken değerlerini toplar.

Programda, sinifana sınıfı saf sanal fonksiyon tanımlaması ile soyut hale geldiğinden, sinifana türünden nesne oluşturulamaz, ancak işaretçi oluşturulabilir.

Programın çalışmasını gösteren şema aşağıdadır: