C++ Classes & Data Abstraction

19 minute read
  • C++ dilinin en güçlü olduğu alanlardan bir tanesi data abstraction.

  • Nesne yönelimli programlama data abstraction demek değil. Data abstraction anlamı veri soyutlama.

  • Problem domaininde ki bir veriyi yazılımsal olarak nasıl temsil edeceğim demek. Hikaye şöyle, user defined türlerimiz olmasa elimizde sadece primitive türler var. Bu türlerle modellenebilecek veriler var. Ama problem domaininde bizi ilgilendirecek verilerin çoğu daha komplex. a + bi gibi bir sayı gibi yada bir üniversitede okuyan bir öğrencinin bilgileri.(tüm bilgileri) En güzel örnek olarak programlama kitaplarında genelde tarih bilgisi veriliyor. Tarih bilgisi tipik olarak gün ay yıl. Dilin bize sunduğu avantajlar olmasaydı şöyle yapıcaktık, 3 tane değişken alacaktık, dilin kuralları gereği bu 3 değişken arasında bir bağlantı kurulmasa da dilin araçlarıyla logic olarak biz bir bağlantı kuracaktık. Bunun da getirdiği bir sürü problem olacaktı. Data abstraction yönünden güçlü olması demek daha komplex türleri primitive türleri kullanıyormuş gibi kolaylıkla kullanabilmektir. İşte bu konuda C++ dilinde class(sınıf)lar var.

  • Şimdi bahsedeceğim C++ dilinin sınıf kavramı. Nesne yönelimli programlamanın sınıfı değil.

  • Sınıf konusu C++ dilinde user defined bir tür.

  • class bir keyword.

  • Bu türü doğrudan Data ismi ile kullanılabilir

1class Data{}; // semicolon unutma - noktalı virgül
2
3\\-----
4
5Data x; // bir nesne tanimladik
6Data *ptr = new Data; // bir dinamik nesne tanimladik
7

Sınıf Öğeleri

  • Blok içinde sınıfın öğelerini tanıtacağız

  • Veri öğeleri (Data member)

  • Fonksiyonlar öğeleri (Member function)

  • Tür öğeleri (Nested type)

  • C de birlikler ve yapılar artık c++ da sınıf oldu.

  • Sınıf üye fonksiyonları semantik bir maske, gizleme.

  • class değilde struct ile bir sınıf oluşturulsaydı default erişim bölümü public olucaktı, public class yerine struct kullanılır

  • class bir scope fakat public, private, protected birer scope değil.

  • C++ dilinde sınıf dışı gördüğümüz fonksiyonlara global fonksiyon diyeceğiz. Sınıf içi tanımlanan fonksiyonlara member function.

  • Structlardan farklı olarak, sınıfların erişim ayrıcalığı var.

  • Sınıfın public, protected ve private bölümleri var.

 1// ERISIM BELIRLEYICILERI
 2class Data
 3{
 4public:
 5    /////
 6private:
 7    /////
 8protected:
 9    /////
10}
  • private sınıfın kendi kodlarına açık fakat sınıfın müşterilerine kapalı. compile timeda kontrol edilecek.

  • protected, kalıtımla birlikte önem kazanacak(inheritance). Sınıfın clientlarına kapalı, inherit edilince açık.

  • .(nokta) operatörü

  • ->(ok) operatörü

  • :: (çözünürlük operatörü)

  • Aynen yapılarda olduğu gibi bir sınıf nesnesi yoluyla sınıfın öğesine nokta yoluyla ulaşabiliyoruz. Önemli olan burada öğe aramanın nasıl yapıldığı.

  • Derleyici . operatörünün önce sol operandına bakar ve bir sınıf türünden olduğunu anlarsa sağ operandını class scope’ta arar.

  • Bir sınıf türünden pointer ise ptr->x ok operandının solu sınıf mı, sağ operandını o class içinde arar

1myclass.*ptr;
2ptr->x;

Name Lookup

  • Derleyici aradığı ismi bulunamassa her zaman error. Bulursa geçerli diye birşey yok. Aramada 3 durum var
  1. nokta operatörü
  2. -> operatörü (o sınıf türünden adres olucak)
  3. :: operatörü (sınıf türünden olucak)
  • operatörlerinin sağ operantları class scope ta aranır
1Myclass m;
2m.x; // isim arama önce yapılıyor, sonra class private olursa x aradım buldum, private alanına erişim hatası
  • private kodlarını gizlemek için PIMPL idiomu kullanılır, bu idiomun hem compile time hemde run time maliyeti var.

  • En katı korunan kısım private, hem clientlara hem de türetilmiş sınıflara kapalı

  • protected clientlara kapalı, türetilmiş sınıflara açık

  • public herkese açık

  • C de şöyle diyebilirdik, client bunları bil ama kullanma, ben senin bu öğelere doğrudan erişmeni istemiyorum gibi.


PIMPL IDIOMU HAKKINDA

  • Madem amaç private bölümü gizlemek, engellemek o zaman neden private sınıf tanımında yazıyoruz ki? Muhtemelen başlık dosyasında olucak, eee madem clientların bilmesini istemiyorum başlık dosyasına koyarsam clientlar bunu görmeyecek mi? Malesef görecekler, böyle bir açık var ama teknik zorunluluktan oluyor. Neden? Çünkü client ın bunu kullanması yanında cpp dosyasıda bunu kullanıyor. Özel önlem alınabilir ama default durum client bunu görebiliyor, bunları göstermemenin yolu var buna pimpl idiomu deniyor. (Pointer Implemantatıon)

  • Compile time maliyeti var, runtime maliyeti var (pimpl idiomu)

  • Veri elemanları private bölümü, member functionlar sınıfın public bölümüne, sınıfın public bölümü genelde bu sınıf için işleri yapıcak fonksiyonlardan oluşacak

  • Pimpl idiomu için ayrıcı bir makale yazıcam. Konuyu dağıtmamak için devam ediyoruz…. Bir önce ki makalede de belirttiğim gibi :: çözünürlük operatörü ile ilgili şöyle ufak bir örnek yapalım.

 1// :: operatoru
 2#include <iostream>
 3
 4
 5using namespace std;
 6
 7int x = 56;
 8
 9int main()
10{
11    int x = 20;
12
13    cout << x << endl;      // 20
14    cout << ::x << endl;      // 56
15
16    return 0;
17}
18// Global alanda x isimli birsey bulunamadi
19
20#include <iostream>
21
22
23using namespace std;
24
25
26int main()
27{
28    int x = 20;
29
30    cout << x << endl;
31    cout << ::x << endl;    // No member named 'x' in the global namespace; did you mean simply 'x'?
32
33    return 0;
34}
  • Üye fonksiyon uydurmasının anlamı şu, daha önce global scope ta bulunan fonksiyonlar artık class scope içine dahil oluyorlar, amaç clienta daha iyi bir interface vermek, mydate.set() mydate in set fonksiyonu gibi.

Artık üye fonksiyonlar geldi, global fonksiyonlar hiç kullanılmayacak mı?

  • Hayır alakası yok, C de zaten başka yolumuz yok ama C++ da sınıfla ilgili iş yapan fonksiyonların artık çoğu class içinde olucak ama global fonksiyonlarda olabilir
 1//Function Member
 2
 3#include <iostream>
 4
 5using namespace std;
 6
 7int x = 9999;
 8
 9class Data {
10    int x;
11public:
12    void set(int val);
13    void display();
14};
15
16void Data::set(int i)
17{
18    x = i;
19}
20
21void Data::display()
22{
23    cout << "x = " << x << endl;
24}
25
26int main()
27{
28    Data mydata;
29
30    mydata.set(100);
31    mydata.display();
32
33    return 0;
34}

Uygulama pratiğinde her sınıf için bir header dosyası oluşturulur

  1// Multiple Inclusion || Include Guard
  2#ifndef MYHEADER_H
  3#define MYHEADER_H
  4// header file contents go here..
  5#endif // MYHEADER_H
  6// Scope
  7
  8#include <iostream>
  9
 10using namespace std;
 11
 12int x = 9999;
 13
 14class Data {
 15    int x;
 16public:
 17    void set(int val);
 18    void display();
 19};
 20
 21void Data::set(int i)
 22{
 23    x = i;
 24}
 25
 26void Data::display()
 27{
 28    int x = 3333;
 29    cout << "x = " << x << endl;         // x = 3333
 30    cout << "x = " << Data::x << endl;     // x = 100
 31    cout << "x = " << ::x << endl;         // x = 9999
 32}
 33
 34int main()
 35{
 36    Data mydata;
 37
 38    mydata.set(100);
 39    mydata.display();
 40
 41    return 0;
 42}
 43#include <iostream>
 44
 45using namespace std;
 46
 47
 48class Date {
 49    char str[1000];
 50public:
 51    void set(int d, int m, int y);
 52    void display();
 53};
 54
 55
 56
 57void Date::set(int d, int m, int y)
 58{
 59    sprintf(str, "%d/%d/%d", d, m, y);
 60}
 61
 62void Date::display()
 63{
 64    cout << str << endl;   // 5/12/1997
 65}
 66
 67
 68
 69int main()
 70{
 71    Date mydate;
 72
 73    mydate.set(5, 12, 1997);
 74    mydate.display();
 75
 76    return 0;
 77}
 78#include <iostream>
 79
 80using namespace std;
 81
 82class Myclass {
 83    int x, y;
 84public:
 85   void func();
 86};
 87
 88
 89
 90//////myclass.cpp
 91
 92
 93void Myclass::func()
 94{
 95    cout << "this = " << this << endl;   // this = 0x7fff5fbff760
 96}
 97
 98int main()
 99{
100    Myclass m;
101
102    m.func();
103
104    cout << "&m   = " << &m << endl;   // // &m = 0x7fff5fbff760
105
106    return 0;
107}

Adres Bilgisi

  • Sınıfta public veya private önce olacağı durumlar olucak, belli senaryolar durumda bunda özgürüz.

  • Global bir fonksiyon ile sınıfın üye fonksiyonu asla overload değildir.

  • Sınıfın üye fonksiyonu default u nonstatic

  • Eğer sınıf içinde bir fonksiyonda static keywordu varsa bunu C deki ile karıştırmayacağız. Bu sınıf nesnesinin adresine ihtiyaç duymayan demek


Sınıfın Üye Fonksiyonları Arasında İsim Arama (name lookup)

  • non static, nesneye ihtiyacı var(static keywordu almayanlar)

  • C dilinde block scope, global scope alanı vardı, şimdi class scope eklendi.(C++ için)

  • Block scope un class scopa önceliği var.

  • isim arama yapılır, eğer aranan isim bulunursa arama durur (javada böyle değil), yani isim 1 kez aranır, bulunan isimden sonra asla isim arama yapılmaz.

 1
 2//Scope && Erişim Konusu
 3
 4#include <iostream>
 5
 6using namespace std;
 7
 8
 9class Data {
10    int x;
11public:
12    void func();
13};
14
15Data g;
16
17
18void Data::func()
19{
20    Data y;
21    // sinifin uye fonksiyonu, private bolume erisebilir
22    x = 12;     // class scope, bu fonksiyon hangi x icin cagirirsam onun ismi
23    g.x = 13;    // global nesne, private erisimi var
24    y.x = 14;    // yerel nesne, private erisimi var
25}

Const Öğeler

  • Const öğelerin amacı nesneyi değiştirmek değil, bu bir get fonksiyonu, yani bu nesnenin varlık nedeni nesneyi değiştirmek değil

  • Const olmayan bir üye fonksiyon const üye fonksiyonu çağırabilir ama const üye fonksiyon const olmayan bir üye fonk çağıramaz.

  • Const overloading (yine function overloading tabi ama const)

  • Native erişim biçimi, this göstericisi ile ve çözünürlük operatörü ile

  • C++ dilinde this->x olarak kullanmayın… (java, c# taki gibi değil), normal koşullarda bunu kullanma

  • Sınıfın veri elemanlarına this gösterici ile erişilebiliriz


This Pointerı

  • Bu fonksiyon hangi nesne için çağırıldı ise o nesnenin adresi

  • *this demek o adresteki nesne

  • this kendisi const olan bir gösterici

 1// this
 2
 3#include <iostream>
 4
 5using namespace std;
 6
 7class Myclass {
 8    int x, y;
 9public:
10   void func(Myclass &r);
11
12};
13
14Myclass g;
15
16void Myclass::func(Myclass &r)
17{
18    *this = r;
19    ////
20}
21
22int main()
23{
24    Myclass m1, m2;
25    ////
26
27    m1.func(m2);        // m2'yi m1'e atiyoruz
28    // *this m1 demek zaten, *this nesnenin kendisi demek
29    return 0;
30}
  • Nesnenin kendisi ise nokta operatörü

  • Pointer ise ok(->) operatörü kullan

 1// Genel Bir Ornek
 2
 3#include <iostream>
 4
 5using namespace std;
 6
 7///date.h
 8class Date {
 9    int md, mm, my;
10public:
11    Date &set(int d, int m, int y);
12    void display()const;
13    int getMonthDay()const;
14    int getMonth()const;
15    int getYear()const;
16};
17
18//////////date.cpp
19//include "date.h"
20
21Date &Date::set(int d, int m, int y)
22{
23    md = d;
24    mm = m;
25    my = y;
26    return *this;
27}
28
29
30int Date::getMonthDay()const
31{
32    return md;
33}
34
35int Date::getMonth()const
36{
37    return mm;
38}
39
40int Date::getYear()const
41{
42    return my;
43}
44
45void Date::display()const
46{
47    cout << md << "/" << mm << "/" << my << endl;
48}
49
50int main()
51{
52    Date x;
53
54    x.set(12, 4, 1999).display();      //    12/4/1999
55
56    int val = x.getMonth();
57
58    Date y;
59
60    y.set(23, 5, 1987);
61
62
63    return 0;
64}

Ufak bir tekrar

  • Sınıflarda .(nokta) operatörü

  • Pointerlarda ->(ok) operatörü

  • :: çözünürlük operatörü(erisim) Myclass::x

  • Sadece ::x dersek global değişken

  • :: 2 kullanımı var… SINIFLARDA ve NAMESPACE alanlarında

  • cons olan fonksiyon, cons olmayan üye fonksiyonu çağıramaz

  • cons olmayan bir fonksiyon, cons olan fonk çağırabilir

Üyeleri 3 ayrı erişim var

  • Native doğrudan erişim x = 45; y = 26; gibi

  • this ile erişim

  • çözünürlük operatörü(::) ile erişim

Dereference edersek;

  • this, Myclass m; m.func();
  • *this m’nin kendisi yani hangi nesne için çağırılırsa o nesnenin kendisi
  • this kendisi const olan bir göstericidir. Data *const this;
  • this atama yapamayız ama *this e atama yapabiliriz
1this -> x++; ile (*this).x++; aynıdır