← Kursa Dön
📄 Text · 12 min

this Pointer, static Üyeler

Bu derste iki önemli kavramı öğreneceğiz: this pointer ve static üyeler. this bir nesnenin "kendisine" işaret eden pointer'dır. static ise nesneye değil, sınıfa ait olan üyelerdir.

İkisi birbirinin zıttı gibi: this "ben kimim?" der, static ise "ben hiç kimseye özel değilim, herkesle paylaşılırım" der.


this Pointer Nedir?

Her üye fonksiyon çağrıldığında, derleyici gizli bir parametre olarak çağıran nesnenin adresini geçirir. Bu gizli parametrenin adı this'tir.

Bunu bir nüfus cüzdanı gibi düşün. Her vatandaşın cüzdanı var ve cüzdan sahibinin kim olduğunu gösterir. Nesnenin this'i de o nesnenin "kimliği" — kendisinin bellekteki adresi.

#include <iostream>
#include <string>
using namespace std;

class Ogrenci {
private:
    string isim;
    int numara;

public:
    Ogrenci(string isim, int numara) {
        // Parametre adı üye değişkenle aynı olunca
        // this-> ile ayırt ederiz
        this->isim = isim;
        this->numara = numara;
    }

    void bilgi() {
        cout << "Nesne adresi: " << this << endl;
        cout << "İsim: " << this->isim << endl;  // this-> opsiyonel burada
        cout << "İsim: " << isim << endl;          // aynı şey
    }
};

int main() {
    Ogrenci ali("Ali", 1001);
    Ogrenci veli("Veli", 1002);

    ali.bilgi();   // farklı adres
    veli.bilgi();  // farklı adres

    return 0;
}

this Ne Zaman Kullanılır?

  1. İsim çakışması: Parametre adı üye değişkenle aynı olduğunda

  2. Method chaining: Fonksiyondan *this döndürerek zincirleme çağrı

  3. Kendini parametre olarak geçirme: Başka fonksiyona kendini gönderme

#include <iostream>
using namespace std;

class Sayac {
private:
    int deger;

public:
    Sayac(int deger = 0) : deger(deger) {}

    // 1. İsim çakışması
    void ayarla(int deger) {
        this->deger = deger;  // this-> olmadan deger = deger olur (anlamsız)
    }

    // 2. Kendini döndürme (daha sonra method chaining'de)
    Sayac& artir() {
        deger++;
        return *this;
    }

    void yazdir() {
        cout << "Değer: " << deger << endl;
    }
};

int main() {
    Sayac s;
    s.ayarla(10);
    s.yazdir();  // Değer: 10

    return 0;
}

💡 İpucu: Modern C++'ta isim çakışmasından kaçınmak için üye değişkenlere önek eklemek yaygın bir pratiktir: m_isim, isim_ veya _isim. Bu durumda this-> kullanmaya gerek kalmaz.


Method Chaining (Fluent Interface)

Fonksiyondan *this döndürerek birden fazla çağrıyı zincirleme yapabilirsin. Bu kalıba fluent interface denir.

jQuery veya JavaScript'teki .then().then().catch() zincirini biliyorsan, aynı fikir:

#include <iostream>
#include <string>
using namespace std;

class Sorgu {
private:
    string tablo;
    string kosul;
    int limit_deger;
    string siralama;

public:
    Sorgu() : limit_deger(0) {}

    Sorgu& from(const string& t) {
        tablo = t;
        return *this;  // kendini döndür
    }

    Sorgu& where(const string& k) {
        kosul = k;
        return *this;
    }

    Sorgu& limit(int n) {
        limit_deger = n;
        return *this;
    }

    Sorgu& orderBy(const string& s) {
        siralama = s;
        return *this;
    }

    string olustur() {
        string sql = "SELECT * FROM " + tablo;
        if (!kosul.empty()) sql += " WHERE " + kosul;
        if (!siralama.empty()) sql += " ORDER BY " + siralama;
        if (limit_deger > 0) sql += " LIMIT " + to_string(limit_deger);
        return sql;
    }
};

int main() {
    // Method chaining — her fonksiyon *this döndürdüğü için zincirleme çalışır
    string sql = Sorgu()
        .from("ogrenciler")
        .where("yas > 18")
        .orderBy("isim ASC")
        .limit(10)
        .olustur();

    cout << sql << endl;
    // SELECT * FROM ogrenciler WHERE yas > 18 ORDER BY isim ASC LIMIT 10

    return 0;
}

Bir de klasik "Builder" örneği:

#include <iostream>
#include <string>
using namespace std;

class HtmlBuilder {
private:
    string html;

public:
    HtmlBuilder& baslik(const string& metin) {
        html += "<h1>" + metin + "</h1>\n";
        return *this;
    }

    HtmlBuilder& paragraf(const string& metin) {
        html += "<p>" + metin + "</p>\n";
        return *this;
    }

    HtmlBuilder& liste(const string& oge) {
        html += "<li>" + oge + "</li>\n";
        return *this;
    }

    string olustur() { return html; }
};

int main() {
    string sayfa = HtmlBuilder()
        .baslik("Merhaba")
        .paragraf("Bu bir test sayfası.")
        .liste("Madde 1")
        .liste("Madde 2")
        .olustur();

    cout << sayfa;
    return 0;
}

static Üye Değişkenler

Normal üye değişkenler her nesne için ayrı kopyadır. static üye değişken ise tüm nesneler tarafından paylaşılır — sınıfa ait, nesneye değil.

Bunu bir sınıftaki sınıf panosu gibi düşün. Her öğrencinin kendi defteri var (normal üyeler), ama panodaki bilgi herkesle ortak (static üye).

#include <iostream>
#include <string>
using namespace std;

class Ogrenci {
private:
    string isim;
    int numara;

    static int toplam_ogrenci;  // static — tüm nesneler paylaşır

public:
    Ogrenci(string i) : isim(i) {
        toplam_ogrenci++;
        numara = toplam_ogrenci;
        cout << isim << " oluşturuldu (Toplam: "
             << toplam_ogrenci << ")" << endl;
    }

    ~Ogrenci() {
        toplam_ogrenci--;
        cout << isim << " silindi (Toplam: "
             << toplam_ogrenci << ")" << endl;
    }

    static int toplamOgrenci() {
        return toplam_ogrenci;
    }

    void bilgi() {
        cout << numara << ". " << isim << endl;
    }
};

// static üye değişken sınıf DIŞINDA tanımlanmalı
int Ogrenci::toplam_ogrenci = 0;

int main() {
    cout << "Başlangıç: " << Ogrenci::toplamOgrenci() << endl;  // 0

    Ogrenci ali("Ali");
    Ogrenci veli("Veli");
    cout << "Şu an: " << Ogrenci::toplamOgrenci() << endl;  // 2

    {
        Ogrenci ayse("Ayşe");
        cout << "İç scope: " << Ogrenci::toplamOgrenci() << endl;  // 3
    }  // Ayşe silindi

    cout << "Dış scope: " << Ogrenci::toplamOgrenci() << endl;  // 2

    return 0;
}

Önemli Noktalar

class Ornek {
    static int sayac;            // BİLDİRİM — sınıf içinde
    static const int MAX = 100;  // const static başlatılabilir (integral)
    inline static int id = 0;    // C++17: inline static doğrudan başlatılabilir
};

int Ornek::sayac = 0;  // TANIM — sınıf dışında (zorunlu, C++17 öncesi)

⚠️ Dikkat: static üye değişken sınıf içinde bildirilir ama sınıf dışında tanımlanmalıdır (C++17'deki inline static hariç). Tanımlamayı unutursan linker hatası alırsın.


static Üye Fonksiyonlar

static fonksiyonlar nesne olmadan çağrılabilir. this pointer'ları yoktur — sadece static üyelere erişebilirler:

#include <iostream>
using namespace std;

class Matematik {
public:
    static double pi() { return 3.14159265; }

    static double daire_alani(double r) {
        return pi() * r * r;
    }

    static int faktoriyel(int n) {
        if (n <= 1) return 1;
        return n * faktoriyel(n - 1);
    }

    static double mutlak(double x) {
        return x < 0 ? -x : x;
    }
};

int main() {
    // Nesne oluşturmadan çağrılır
    cout << "Pi: " << Matematik::pi() << endl;
    cout << "Daire alanı (r=5): " << Matematik::daire_alani(5) << endl;
    cout << "5! = " << Matematik::faktoriyel(5) << endl;
    cout << "|-7| = " << Matematik::mutlak(-7) << endl;

    return 0;
}

static Fonksiyonların Kısıtlamaları

class Ornek {
    int x;           // normal üye
    static int y;    // static üye

public:
    static void staticFn() {
        // y = 10;       // OK — static üyeye erişebilir
        // x = 10;       // HATA! Non-static üyeye erişemez
        // this->x = 10; // HATA! this pointer yok
    }

    void normalFn() {
        x = 10;   // OK
        y = 10;   // OK — normal fonksiyon static üyeye de erişebilir
    }
};
int Ornek::y = 0;

Singleton Pattern (Kısa Örnek)

Singleton, bir sınıftan sadece bir nesne oluşturulmasını garanti eden tasarım kalıbıdır. static üye fonksiyon ve private constructor ile uygulanır:

#include <iostream>
#include <string>
using namespace std;

class Ayarlar {
private:
    string tema;
    int font_boyutu;

    // Constructor private — dışarıdan nesne oluşturulamaz
    Ayarlar() : tema("koyu"), font_boyutu(14) {
        cout << "Ayarlar oluşturuldu" << endl;
    }

public:
    // Kopyalama yasak
    Ayarlar(const Ayarlar&) = delete;
    Ayarlar& operator=(const Ayarlar&) = delete;

    // Tek nesneye erişim noktası
    static Ayarlar& getInstance() {
        static Ayarlar instance;  // İlk çağrıda oluşturulur, sonra aynı döner
        return instance;
    }

    void setTema(const string& t) { tema = t; }
    string getTema() const { return tema; }

    void setFontBoyutu(int b) { font_boyutu = b; }
    int getFontBoyutu() const { return font_boyutu; }
};

int main() {
    // Ayarlar a;  // HATA! Constructor private

    Ayarlar& ayar1 = Ayarlar::getInstance();
    Ayarlar& ayar2 = Ayarlar::getInstance();

    ayar1.setTema("açık");
    cout << ayar2.getTema() << endl;  // "açık" — aynı nesne!

    // Adresleri kontrol et
    cout << "&ayar1: " << &ayar1 << endl;
    cout << "&ayar2: " << &ayar2 << endl;  // aynı adres!

    return 0;
}

Bu Singleton implementasyonu Meyers' Singleton olarak bilinir ve C++11'den itibaren thread-safe'dir (yerel static değişkenler thread-safe olarak başlatılır).

💡 İpucu: Singleton çok tartışmalı bir pattern'dir. Global state oluşturur ve test etmeyi zorlaştırır. Kullanmadan önce gerçekten gerekli mi diye düşün. Alternatif olarak dependency injection tercih edilebilir.


Pratik Örnek: ID Generator

#include <iostream>
#include <string>
using namespace std;

class IdGenerator {
private:
    static int sonraki_id;

public:
    static int yeniId() {
        return ++sonraki_id;
    }

    static int mevcutId() {
        return sonraki_id;
    }

    static void sifirla() {
        sonraki_id = 0;
    }
};

int IdGenerator::sonraki_id = 0;

class Urun {
private:
    int id;
    string isim;
    double fiyat;

public:
    Urun(string i, double f)
        : id(IdGenerator::yeniId()), isim(i), fiyat(f) {}

    void yazdir() {
        cout << "#" << id << " " << isim << " - " << fiyat << " TL" << endl;
    }
};

int main() {
    Urun u1("Klavye", 250);
    Urun u2("Mouse", 150);
    Urun u3("Monitor", 3500);

    u1.yazdir();  // #1 Klavye - 250 TL
    u2.yazdir();  // #2 Mouse - 150 TL
    u3.yazdir();  // #3 Monitor - 3500 TL

    cout << "Toplam ürün: " << IdGenerator::mevcutId() << endl;

    return 0;
}

Pratik Örnek: Bağlantı Havuzu (Connection Pool Konsepti)

#include <iostream>
using namespace std;

class Baglanti {
private:
    int id;
    bool aktif;

    static int toplam_baglanti;
    static const int MAX_BAGLANTI = 5;

public:
    Baglanti() : aktif(false), id(0) {}

    static bool yeniBaslat(Baglanti& b) {
        if (toplam_baglanti >= MAX_BAGLANTI) {
            cout << "HATA: Maksimum bağlantı sayısına ulaşıldı!" << endl;
            return false;
        }
        toplam_baglanti++;
        b.id = toplam_baglanti;
        b.aktif = true;
        cout << "Bağlantı #" << b.id << " açıldı ("
             << toplam_baglanti << "/" << MAX_BAGLANTI << ")" << endl;
        return true;
    }

    void kapat() {
        if (aktif) {
            aktif = false;
            toplam_baglanti--;
            cout << "Bağlantı #" << id << " kapatıldı ("
                 << toplam_baglanti << "/" << MAX_BAGLANTI << ")" << endl;
        }
    }

    static int aktifSayisi() { return toplam_baglanti; }
};

int Baglanti::toplam_baglanti = 0;

int main() {
    Baglanti b1, b2, b3, b4, b5, b6;

    Baglanti::yeniBaslat(b1);
    Baglanti::yeniBaslat(b2);
    Baglanti::yeniBaslat(b3);
    Baglanti::yeniBaslat(b4);
    Baglanti::yeniBaslat(b5);
    Baglanti::yeniBaslat(b6);  // HATA: Maksimum!

    b2.kapat();
    Baglanti::yeniBaslat(b6);  // Şimdi açılabilir

    return 0;
}

Özet

  • this pointer, üye fonksiyon içinde çağıran nesnenin adresini tutan gizli bir pointer'dır. Parametre ismi ile üye değişken çakıştığında this-> ile ayırt edilir.

  • Method chaining (fluent interface), fonksiyonlardan *this döndürerek zincirleme çağrı yapmayı sağlar. Builder pattern ve sorgu oluşturucularda yaygın kullanılır.

  • static üye değişkenler tüm nesneler tarafından paylaşılır, sınıfa aittir. Sınıf dışında tanımlanmalıdır (C++17 inline static hariç).

  • static üye fonksiyonlar nesne olmadan Sinif::fonksiyon() şeklinde çağrılır. this pointer'ları yoktur, sadece static üyelere erişebilirler.

  • Singleton pattern, private constructor + static getInstance() ile bir sınıftan tek nesne oluşturmayı garanti eder. Meyers' Singleton C++11'den itibaren thread-safe'dir.

  • static üyeler global state oluşturur; dikkatli kullan. Nesne sayacı, ID üreteci, konfigürasyon gibi gerçekten paylaşılması gereken durumlar için uygundur.