top of page

C++ Dilinde class Devrimi

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • May 27, 2023
  • 7 min read

Yazılım dillerinin gelişim sürecinde gelinen önemli aşamalardan biri nesne yönelimli programlama(object-oriented programming) olarak söylenebilir. Bu programlama yöntemi gelişiyle beraber birçok yazılım dilinde temel birkaç başlık altında gelişim göstermektedir. Bu başlıklar ilerleyen yazılarda detaylandırılarak anlatılacaktır ve tüm maddelerin ortak noktası olan sınıf(class) kavramı ele alınacaktır.

Sınıf kavramı nesnenin oluşturduğu veri(data) alanları üzerinde işlemler gerçekleştirilen bir kavramdır. Bu kavramı ele alırken iyi kavrayabilmek için birkaç yapıya değinilecektir. Bunlardan biri veri soyutlama(data abstraction) sınıf ile ilgili ayrıntıları gerekli görülen şekilde gizlemek ve bu doğrultuda dışarıya sunmak olarak tanımlanabilir. Bir tasarım veya problem için oluşturulan bir yapıdaki varlıkların yazılımsal olarak temsil edilmesi gerekmektedir. Bunlar için yazılım dillerinde variable oluşturulmaktadır.(int, double, float... gibi değişkenler olabilir) Daha karmaşık yapılar bu tasarım ya da problem çözümünde gerekli görülürse farklı şekillerde buna yazılım dilleri destek vermektedir. Bu seride en çok karşılaştırılacak olan C programlama dilindeki ve C++ dilinde de sınıf olarak ele alınacak olan struct kavramı bunu sağlayarak bu değişkenleri temsil ederek çözüm sağlayabilmektedir. C++ dilinde de bunu sağlamanın en temel aracı class(sınıf) kavramıdır. Aynı zamanda nesne yönelimli programlama(OOP) temel noktalarından biridir. Sınıflar bir user define type yani kullanıcının tanımladığı türlerden biridir. Kullanımı da aşağıda temel seviyede gösterilecektir.


#include <iostream>

using namespace std;

class OutClass {
private:
    int pr_data;
    
public:
    int pb_data;
    
    class InClass {             // İç içe geçmiş bir tür (member type)
    private:
        int in_pr_data;
        
    public:
        int in_pb_data;
        
        void in_Func()          // İç içe geçmiş bir işlev (member function)
        {
            cout << "InClass::in_Func() çağrısı" << endl;
        }
    };
    
    struct InStruct {           // İç içe geçmiş bir tür (member type)
        int i_data;
    };
    
    enum InEnum                // İç içe geçmiş bir tür (member type)
    {
        INVAL1,
        INVAL2
    };
    
    int get_Pr_Data()          // Veri elemanlarına erişim için işlevler
    {
        return pr_data;
    }
    
    void set_Pr_Data(int val) 
    {
        pr_data = val;
    }
    
    void out_func()           // İç içe geçmiş bir işlev (member function)
    {
        cout << "OutClass::out_func() çağrısı" << endl;
    }
};

int main() 
{
    OutClass oc;
    
    oc.set_Pr_Data(1);
    oc.pb_data = 2;
    
    int pr_val = oc.get_Pr_Data();
    int pb_val = oc.pb_data;
    
    cout << "pr_val : " << pr_val << endl;
    cout << "pb_val : " << pb_val << endl;
    
    OutClass::InClass in_oc;
    in_oc.in_pb_data = 5;
    in_oc.in_Func();
    
    OutClass::InStruct in_str;
    in_str.i_data = 6;
    
    OutClass::InEnum in_enum = OutClass::INVAL1;
    
    oc.out_func();
    
    return 0;
}
*****************************
AÇIKLAMA: Bu kod içerisindeki tüm üye elemanlar aşağıda listelenmiştir.
*****************************
class SmartCode {
// Burada bazı isimlerin bildirimlerini yapacaz. Burada bildirilen isimlere sınıfın memberları denir.
// Sınıfın memberları 3 ayrı varlık kategori de olabilir. 

	*** CLASS MEMBERS ***
	   1- data members, 
	   2- member functions, 
	   3- nested types(member types, type members)

	1) data members
	   a-non-static data members, 
	   b-static data members

	2) member functions
	   a-non-static member functions, 
	    1) non-const member functions
	    2) const member functions
	   b-static member functions

	3) member types
        static ya da non-static diye ayrım yoktur.
};
*****************************
AÇIKLAMA: 
*****************************	
  • Yukarıda bir SmarCode isimli(tag) bir sınıf oluşturulmuştur. Ön eki olarakta class keyword eklenmiş, devamında süslü parantezler içerisinde maddelerle ileride detaylandırılacak olan üyeler sıralanmış ve noktalı virgül(;) atomu ile kapatılmıştır. Burada sıralanan maddeler sınıfları oluştururken ihtiyaç duyacağımız elemanlar olacaktır. Ve buradan devam ederken süslü parantezlerin içerisinde erişim belirteçleri(access specifier) kullanılacak olup sınıfın kapsamını etkilemeyecektir. Bu konu önemli olduğu için scope yani kavram olarak bilinmesi gereken konuya sınıftaki gibi parantez açarak hatırlatma babında değinmekte fayda olacaktır.

Scope(kapsam) konusunu anlatırken C ve C++ dillerindeki kullanımları ile ayrımlarına değinerek örneklendirilecektir.


Öncelikle dört madde olarak ele alacağımızı belirterek sıralayalım ve tek tek değerlendirelim:

  1. file scope

  2. block scope

  3. function prototype scope

  4. function scope

int val = 5;
void smart_code(int);
*****************************
AÇIKLAMA: C programlama dilinde global isim alanında tanımlanan                          isimler "file scope". Yani tanımlandıkları yerden dosya(file) sonuna kadar her yerde bilinir olurlar.
*****************************
int main()
{
    int scp = 1;
    
    if (scp > 0)
    {
	    int val = 5; 
    }
}
*****************************
AÇIKLAMA: blok içerisinde tanımlanan isimler "block scope". Süslü parantezlerle belirtilen kod bloğunda tanımlanan değişkenlerin geçerliliklerini ifade eder.
*****************************
void smart_code(int scp);  
*****************************
AÇIKLAMA: fonksiyonların bildirimlerinde parametrelerinin kapsamları "function scope".
*****************************
void smart_code(int scp)
{
    goto edit; 
           
edit:                  
}
*****************************
AÇIKLAMA: goto etiketi function scope olarak değerlendirilir. edit ismi smart_code(...) fonksiyonunun her yerinde bilinir olacaktır.
*****************************
  • C++ programlama dilinde ise OOP ile biraz değiştirilerek şu şekilde listelenebilir :

  1. file scope

  2. class scope

  3. block scope

  4. function prototype scope

  5. function scope


Listedeki class scope kavramı sınıf içerisinde tanımlanan herşeyin yer aldığı kavramdır. Bildirilen ismin kullanıldığı yeri belirlemek ve sınırlandırmak için kullanılmaktadır.


class scope incelenirken birkaç kavramı daha iyice kavramak ve sınıflarla yapacağımız tasarımları yapabilmek için faydalı olacaktır. Bunlardan ilki name-lookup kavramıdır.

Derleyicilerin bir tanımlama içinde bir ya da birden fazla ismin kullanıldığını gördüklerinde compile işleminin belirli bir aşamasında bu kodu anlamlandırabilmek, dolayısıyla bu kod karşılığı assembly kod üretebilmek için; tanımlanan değişkenin bir isim olduğunu anlayıp neyin ismi olduğunu anlamlandırdıkları sürece name-lookup(isim arama) denir. Bu süreç iki şekilde sonuçlanabilir :

  1. Syntax error ile sonuçlanabilir. Derleyici aranan isme ilişkin bildirim bulamayabilir.

  2. İsim arama başarılı sonuçlanır ve değerlendireceğimiz diğer süreçlere geçilir.

İsim arama kurallarına da değinerek biraz pekiştirmeye çalışalım.

  • Dilin kuralları gereği ismin aranması çoğu zaman bir sıraya bağlanmaktadır.

int main() 
{ 
    if (1) 
    { 
        if (1) 
        { 
	        val; 
	    } 
    } 
}
*****************************
AÇIKLAMA: val değişkeni sırayla scope tarayarak sırayla aranacaktır.
*****************************
  • İsim arama(name-lookup) bulunduktan sonra sona erer ve tekrar başlamaz.

void smart_code(int);

int main() 
{ 
    int smart_code = 0; 
    smart_code(5); 
}
*****************************
AÇIKLAMA: isim arama önce ilk süslü parantezler içerisinde yapılacaktır ve değişken olarak tanımlandıktan sonra () operandı olarak kullanılmaya çalıştığımız için syntax error alacağız. İsim arama başladı bulundu ve bitti.
*****************************

  • Bir ismin hangi durumlarda class scope içerisinde aranacağına birkaç madde ile değinecek olursak :

a) Nokta operatorunun (member selection- dot operator) sağında kullanılmış ise,

class SmartCode {
    ...
};

int main()
{
    SmartCode scp_val;
    
    scp_val.c_val;
}
*****************************
AÇIKLAMA: scp_val SmartCode sınıfı türünden bir değişken ve c_val değişkeni de bu sınıf kapsamında aranacaktır.
*****************************

b) Ok operatorunun (member selection- arrow operator) sağında kullanılmış ise,

class SmartCode {
    ...
};

int main()
{
    SmartCode* ptr_val;
    
    ptr_val->c_val;
}
*****************************
AÇIKLAMA:  ptr_val SmartCode sınıfı türünden bir değişken ve c_val değişkeni de bu sınıf kapsamında aranacaktır.
*****************************

c) :: operatorunun (scope resolution operator(çözünürlük operatoru)) sağında kullanılmış ise,

class SmartCode {
    ...
};

int main()
{
    SmartCode::c_val;
}
*****************************
AÇIKLAMA: c_val çözünürlük operatoru ile nitelenen SmartCode sınıfı kapsamında aranacaktır. :: operatorunun sol operandı bir namespace ya da sınıf ismi olabilir
*****************************
  • Derleyicilerin kodu analiz etme sürecinde geçtikleri aşamaları aşağıdaki tablodaki gibi düşünürsek :

name-lookup ->

context control ->

access control


class SmartCode { 
public : 
    int sc_val; 
};

int main() 
{ 
    SmartCode val; 
    val.sc_val();           
}
*****************************
AÇIKLAMA: Buradaki hata nedeni sc_val bir fonksiyon çağrısı olamadığı için "context control" hatasıdır. 
*****************************
CEVAP   : syntax error 
*****************************
class SmartCode { 
private : 
    int sc_val; 
};

int main() 
{ 
    SmartCode val; 
    val.sc_val = 5;           
}
*****************************
AÇIKLAMA: Buradaki hata nedeni sc_val private alanda olduğu için "access control" hatasıdır.
*****************************
CEVAP   : syntax error 
*****************************

Access control derleme zamanında nesne yönelimli programlama desteğiyle sağlanan bir süreçtir. Bunu kontrol eden üç adet belirteç vardır. Bunlar scope değil erişim belirtecidirler.!!!

  1. public

  2. private

  3. protected

C++ dilinde şu an incelediğimiz class ile C programlama dilinde de yer alan struct farkı(C++ dilinde sınıftır) default erişim belirteçlerinin farklı olmasıdır. Sınıfın default belirteci private, struct ise default public erişim belirtecine sahiptir.

data members

Sınıfın içinde tanımlanan değişkenlerdir. Bunlar, sınıfın durumunu temsil eder ve sınıf nesnelerinin özelliklerini saklar.

member functions

Sınıfın davranışını tanımlayan kod parçacıklarıdır. Bir sınıfın işlevleri, sınıfın içinde tanımlanır ve bu işlevler sınıf nesneleri üzerinde çalışır.

type members

Sınıfın içinde tanımlanan ve sınıfın türünü temsil eden öğelerdir. Bu tür üyeleri, sınıfın kendisi üzerinden erişilebilen türlerdir. nested types ve type aliases olarak iki kısımda incelenirler.


class SmartCode {
public:
  class InnerCode {
  public:
    void sc_func() 
    {
      // ...codes
    }
  };
};
*****************************
AÇIKLAMA: Nested class
*****************************
class SmartCode {
public:
  using ivec = std::vector<int>; 

  ivec val;  
};
*****************************
AÇIKLAMA: Type alias
*****************************
  • Data members non-static, static data members olarak iki kısımda incelenir demiştik. Sınıfların non-static üye fonksiyonları gizli bir parametreye sahiptir. Bu parametre sınıf türünden bir pointer tutar.

class SmartCode { 
public : 
    void sc_func(SmartCode *ptr, int val); 
};

int main() 
{ 
    SmartCode sc; 
    sc.sc_func(5);
}
*****************************
AÇIKLAMA: Derleyiciler görünmeyen sınıf türünden pointer nesnesine bu ilgili nesnenin adresini geçirecek şekilde kod üretecektir.
*****************************
class SmartCode { 
public : 
    void sc_func(); 
};

int main() 
{ 
    sc_func();           // bu durum isim arama hatasıdır. 
    SmartCode::sc_func(); 
}
*****************************
AÇIKLAMA: :: operatoru ile nitelenen fonksiyon isim arama ile bulundu ve non-static üye fonksiyon olduğu anlaşıldı.
*****************************
class SmartCode {
public :
    void sc_func();
};

void sc_func(int);

int main() 
{
    return 0;
}
*****************************
AÇIKLAMA: burada function overloading yoktur. Aynı scope içerisinde olmalıdır.
*****************************
class SmartCode { 
public : 
    int sc_func(int); 
    int sc_func(int);                         
}; 

int main() 
{ 
    return 0;
}
*****************************
AÇIKLAMA: redeclaration durumu yoktur. Sınıfların üye fonksiyonları buna tabi değildir. Ve syntax error söz konusudur. Bunlar global olsaydı hata olmazdı.
*****************************
class SmartCode { 
private :
    int sc1, sc2; 
    
public : 
    int sc_func(int); 
};

int main()  
{ 
	cout << "sizeof(SmartCode) : " << sizeof(SmartCode) << endl;      
}
*****************************
AÇIKLAMA: Sınıfların üye fonksiyonları sınıf nesnesinin size'ını etkilemezler. 
*****************************
CEVAP   : sizeof(SmartCode) : 8
*****************************
smartcode.h 
*********
class SmartCode { 
public : 
    void sc_func(int val); 
    
private : 
    void sc_foo(int val, double dval); 
};

smartcode.cpp 
*****
#include "smartcode.h"

#define PUBLIC 
#define PRIVATE                  

PUBLIC void SmartCode::sc_func(int val)    
{ 
}

PRIVATE void SmartCode::sc_foo(int val, double dval)   
{ 
}
*****************************
AÇIKLAMA: replacement ya da substitution yazmazsak derleyici silecektir.
*****************************
int gval = 5;

int main() 
{ 
    int gval = 6; 
    cout << ::gval << endl;       
}
*****************************
AÇIKLAMA: global isim çağırılır
*****************************
CEVAP   : 5
*****************************
class SmartCode { 
private :
     int sc; 
     
public : 
	void sc_func(); 
};

int sc = 1;

void SmartCode::sc_func() 
{ 
    int sc = 2; 
    ++::sc; 
    SmartCode::sc = 3; 
}
class SmartCode { 
private :
    int sc ; 

public : 
    void sc_func(); 
}; 

void g_func(); 

void SmartCode::sc_func() 
{ 
    int g_func = 5; 
    g_func();               
}
*****************************
AÇIKLAMA: g_func() çağrısı syntax error olur. değişken olduğu için () operatorunun operandı olamaz. İsim arama yapıldı ve bitti.
*****************************
class SmartCode { 
private :
    int sc;
 
public : 
    void sc_func();        // sc_func(SmartCode* p) gibi gizli nesnesi vardır
}; 

void SmartCode::sc_func() 
{ 
// bu üye fonksiyon hangi nesne için çağırılmışssa o nesnenin sc nesnesidir.
    sc = 12;                
} 

int main() 
{ 
     SmartCode sc1, sc2; 
     
// sc1 değişkeninin adresini gizli parametre değişkenine kopyalandı. 
     sc1.sc_func();                                        
      
// sınıfın non-static data member'ı olduğunu gördüğünde p->sc'ye dönüştürür.                    
    sc2.sc_func();   
}
*****************************
AÇIKLAMA: 
*****************************
class SmartCode { 
private :
    int sc;

public : 
    void sc_func(); 
    void b_func(int val); 
}; 

void b_func(); 

void SmartCode::sc_func() 
{ 
    b_func();       
} 
*****************************
AÇIKLAMA: b_func() çağrısı önce blok içerisinde, sonra class scope ve son olarak globalde aranır. SmartCode class scope'ta bulundu ve parametreli olduğu için syntax error aldık.
*****************************
class SmartCode { 
private :
    int sc; 

public : 
    void sc_func(); 
}; 

void b_func(); 

void SmartCode::sc_func() 
{ 
    b_func();       
} 
*****************************
AÇIKLAMA: burada isim arama sonucu globaldeki fonksiyon bulundu ve legal bir çağrı oldu.
*****************************
class SmartCode { 
private :
    int sc; 

public : 
    void sc_func(); 
}; 

SmartCode g_val; 

void SmartCode::sc_func() 
{ 
    g_val.sc = 5;         
}
*****************************
AÇIKLAMA: geçerli bir çağrıdır.
*****************************

 
 
 

Recent Posts

See All
C++ Dilinde [[nodiscard]] attribute

C++’ta [[attribute]] (öznitelik) mekanizması, kod hakkında ek bilgi sağlayarak derleyicinin uyarılar vermesine, belirli optimizasyonlar...

 
 
 
C++ Dilinde constinit

C++20'de tanıtılan önemli bir özellik `constinit`'dir; bu özellik, statik depolama süresine sahip değişkenlerin derleme zamanında...

 
 
 

Comments


bottom of page