← Kursa Dön
📄 Text · 12 min

const, constexpr ve Sabitler

Programlamada bazı değerler hiç değişmemeli. Pi sayısı her zaman 3.14159..., bir sınıfın maksimum öğrenci kapasitesi her zaman 50, yerçekimi ivmesi 9.81 m/s². Bu değerleri "yanlışlıkla değişmekten" korumak için C++ sana const ve constexpr araçlarını sunar.

Bunu bir müze vitrini gibi düşün: eseri görebilirsin, ama dokunamazsın. const tam da bunu yapar — veriyi görünür kılar ama değiştirilmesini engeller.


const Değişkenler

const bir değişkeni "salt okunur" (read-only) yapar. Bir kez değer verildikten sonra değiştirilemez:

#include <iostream>

int main() {
    const double PI = 3.14159265358979;
    const int MAX_OGRENCI = 50;
    const std::string OKUL = "Bilgi Uni";

    std::cout << "Pi: " << PI << std::endl;
    std::cout << "Max: " << MAX_OGRENCI << std::endl;
    std::cout << "Okul: " << OKUL << std::endl;

    // PI = 3.14;         // DERLEME HATASI!
    // MAX_OGRENCI = 60;  // DERLEME HATASI!

    return 0;
}

const değişkenler tanımlandığı anda başlatılmak zorundadır. Sonradan değer atanamaz:

const int x;      // HATA! Baslangic degeri yok
const int y = 10; // OK

const ile Fonksiyon Parametreleri

Fonksiyon parametrelerinde const kullanmak, fonksiyonun parametreyi değiştirmeyeceğini garanti eder:

#include <iostream>
#include <string>

// const referans — kopyalama maliyetini onler, degistirmeyi engeller
void yazdir(const std::string& metin) {
    std::cout << metin << std::endl;
    // metin = "baska";  // HATA! const referans degistirilemez
}

// Degere gore — zaten kopya, const'a gerek yok (ama zarar da vermez)
void hesapla(const int deger) {
    // deger = 5;  // HATA! ama fonksiyon icinde zaten kopya
    std::cout << deger * 2 << std::endl;
}

int main() {
    std::string isim = "Ahmet";
    yazdir(isim);
    hesapla(42);

    return 0;
}

💡 İpucu: Büyük nesneleri (string, vector, class) fonksiyona geçirirken const & (const referans) kullan. Hem kopyalama maliyetini önler hem de fonksiyonun veriyi değiştirmeyeceğini garanti eder. Bu, C++'ın en temel best practice'lerinden biridir.

const ve Referanslar

const referans hem sabit hem değişken değerlere bağlanabilir:

#include <iostream>

int main() {
    int x = 42;
    const int& ref = x;    // OK — x'i okuyabilir ama degistiremez
    // ref = 100;           // HATA! const referans

    const int& gecici = 100;  // OK! const referans gecici degere baglanabilir
    // int& hata = 100;       // HATA! non-const referans gecici degere baglanamaz

    std::cout << ref << std::endl;
    std::cout << gecici << std::endl;

    return 0;
}

Normal referans geçici değerlere (rvalue) bağlanamaz ama const referans bağlanabilir. Bu, fonksiyon parametrelerinde çok işe yarar.


constexpr — Derleme Zamanı Sabitleri

const çalışma zamanında da belirlenebilir. Ama constexpr derleyiciye "bu değeri derleme zamanında hesapla" der:

#include <iostream>

constexpr double PI = 3.14159265358979;
constexpr int KARE(int x) { return x * x; }

int main() {
    constexpr int boyut = 10;
    constexpr int alan = KARE(boyut);  // Derleme zamaninda hesaplanir

    std::cout << "Pi: " << PI << std::endl;
    std::cout << "Alan: " << alan << std::endl;

    // constexpr dizi boyutu olarak kullanilabilir
    int dizi[boyut];  // OK! boyut derleme zamaninda biliniyor
    dizi[0] = 42;
    std::cout << "Dizi[0]: " << dizi[0] << std::endl;

    return 0;
}

const vs constexpr Farkı

#include <iostream>

int kullanicidan_al() {
    return 42;  // Gercekte cin'den okunabilir
}

int main() {
    const int a = 10;              // Derleme zamaninda biliniyor (ama zorunlu degil)
    const int b = kullanicidan_al(); // Calisma zamaninda belirleniyor — gecerli!

    constexpr int c = 10;              // Derleme zamaninda biliniyor — zorunlu!
    // constexpr int d = kullanicidan_al(); // HATA! Derleme zamaninda hesaplanamaz

    std::cout << "a: " << a << std::endl;
    std::cout << "b: " << b << std::endl;
    std::cout << "c: " << c << std::endl;

    return 0;
}
Özellikconstconstexpr
Değiştirilebilir mi?HayırHayır
Derleme zamanı zorunlu mu?HayırEvet
Çalışma zamanı değer alabilir mi?EvetHayır
Dizi boyutu olarak kullanılabilir mi?BazenHer zaman
Fonksiyonlarda kullanılabilir mi?HayırEvet

constexpr Fonksiyonlar

constexpr fonksiyonlar hem derleme zamanında hem de çalışma zamanında çalışabilir:

#include <iostream>

constexpr int faktoriyel(int n) {
    return (n <= 1) ? 1 : n * faktoriyel(n - 1);
}

constexpr int fibonacci(int n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

int main() {
    // Derleme zamaninda hesaplanir
    constexpr int f5 = faktoriyel(5);   // 120
    constexpr int fib10 = fibonacci(10); // 55

    std::cout << "5! = " << f5 << std::endl;
    std::cout << "fib(10) = " << fib10 << std::endl;

    // Calisma zamaninda da calisir
    int n = 7;
    int f7 = faktoriyel(n);  // Calisma zamaninda hesaplanir
    std::cout << "7! = " << f7 << std::endl;

    return 0;
}

constexpr fonksiyon parametreleri derleme zamanında biliniyorsa derleme zamanında, bilinmiyorsa çalışma zamanında hesaplanır. Bu esneklik çok güçlüdür.


consteval — Sadece Derleme Zamanı (C++20)

C++20 ile gelen consteval bir fonksiyonun mutlaka derleme zamanında çalışmasını zorunlu kılar. constexpr'ın daha katı versiyonudur:

#include <iostream>

consteval int derleme_zamani_kare(int x) {
    return x * x;
}

int main() {
    constexpr int sonuc = derleme_zamani_kare(5);  // OK — derleme zamaninda
    std::cout << "Kare: " << sonuc << std::endl;   // 25

    // int n = 5;
    // int s = derleme_zamani_kare(n);  // HATA! n derleme zamaninda bilinmiyor

    return 0;
}

consteval fonksiyonlara "immediate function" da denir. Sadece derleme zamanı sabitleriyle çağrılabilirler. Çalışma zamanı değerlerle çağırmaya çalışırsan derleme hatası alırsın.

constexpr vs consteval Karşılaştırma

Özellikconstexprconsteval
Derleme zamanında çalışırEvet (mümkünse)Evet (zorunlu)
Çalışma zamanında çalışırEvetHayır
EsneklikYüksekDüşük
GarantiYok (olabilir de olmayabilir de)Kesin derleme zamanı

Çoğu durumda constexpr yeterli. consteval sadece "bu mutlaka derleme zamanında hesaplansın" demen gereken nadir durumlarda kullan.


const ve Pointer'lar — Üç Farklı Anlam

const ve pointer birlikte kullanıldığında üç farklı durum ortaya çıkar. Bu, C++'ın en kafa karıştırıcı konularından biridir — ama mantığı kavrarsan basit:

1. Pointer to const (İşaret edilen değer sabit)

const int* ptr;  // veya: int const* ptr;
// ptr'nin gosterdigi deger degistirilemez, ama ptr baska yere gosterebilir

2. const Pointer (Pointer kendisi sabit)

int* const ptr = &x;
// ptr baska yere gosteremez, ama gosterdigi deger degistirilebilir

3. const Pointer to const (Her ikisi de sabit)

const int* const ptr = &x;
// Ne ptr baska yere gosterebilir, ne gosterdigi deger degistirilebilir

Hepsini bir örnekte görelim:

#include <iostream>

int main() {
    int a = 10;
    int b = 20;

    // 1. Pointer to const — deger degistirilemez
    const int* ptr1 = &a;
    // *ptr1 = 100;  // HATA! Degeri degistiremezsin
    ptr1 = &b;       // OK! Pointer baska yere gosterebilir

    // 2. const Pointer — pointer degistirilemez
    int* const ptr2 = &a;
    *ptr2 = 100;     // OK! Degeri degistirebilirsin
    // ptr2 = &b;    // HATA! Pointer baska yere gosteremez

    // 3. Her ikisi de const
    const int* const ptr3 = &a;
    // *ptr3 = 200;  // HATA!
    // ptr3 = &b;    // HATA!

    std::cout << "a: " << a << std::endl;  // 100 (ptr2 ile degisti)

    return 0;
}

💡 İpucu: const'un *'dan önce mi sonra mı geldiğine bak. *'dan önce → işaret edilen değer sabit. *'dan sonra → pointer kendisi sabit. Sağdan sola okuma kuralı: const int* const ptr → "ptr bir const pointer to const int".


#define vs const vs constexpr

C'den kalma #define makroları hâlâ C++'ta çalışır ama modern C++'ta bunların yerine const ve constexpr kullanılır:

#include <iostream>

// KOTU — C tarzı makro
#define PI_MAKRO 3.14159
#define KARE_MAKRO(x) ((x) * (x))

// IYI — Modern C++
constexpr double PI = 3.14159265358979;
constexpr int kare(int x) { return x * x; }

int main() {
    // Makro tuzagi
    int sonuc_makro = KARE_MAKRO(2 + 3);
    // KARE_MAKRO(2+3) = ((2+3) * (2+3)) = 25 — parantezler kurtarır
    // Ama parantez unutulursa: x*x = 2+3*2+3 = 2+6+3 = 11!

    // constexpr — hic sorun yok
    int sonuc_modern = kare(2 + 3);  // kare(5) = 25

    std::cout << "Makro: " << sonuc_makro << std::endl;
    std::cout << "Modern: " << sonuc_modern << std::endl;
    std::cout << "Pi: " << PI << std::endl;

    return 0;
}

Karşılaştırma Tablosu

Özellik#defineconstconstexpr
Tip güvenliği❌ Yok✅ Var✅ Var
Kapsam (scope)❌ Global✅ Var✅ Var
Debug edilebilir❌ Zor✅ Kolay✅ Kolay
Derleme zamanı✅ Evet❌ Garanti değil✅ Garanti
Fonksiyon olabilir mi?❌ Makro (tehlikeli)❌ Hayır✅ Evet

#define Neden Tehlikeli?

  1. Tip güvenliği yok: Preprocessor sadece metin değiştirme yapar, tip kontrolü olmaz

  2. Kapsam yok: #define her yerde geçerli, namespace veya sınıf sınırı tanımaz

  3. Debug zor: Hata mesajlarında makro adı yerine değiştirilen metin görünür

  4. Beklenmedik davranış: Parantez unutulunca ifade yanlış hesaplanır

#include <iostream>

// TEHLIKELI makro
#define MAX(a, b) ((a) > (b) ? (a) : (b))

// GUVENLI constexpr
constexpr int guvenli_max(int a, int b) {
    return (a > b) ? a : b;
}

int main() {
    int x = 5, y = 3;

    // Makro ile yan etki
    int sonuc = MAX(x++, y);
    // MAX(x++, y) = ((x++) > (y) ? (x++) : (y))
    // x iki kere artirilir! Beklenmedik sonuc
    std::cout << "Makro sonuc: " << sonuc << std::endl;
    std::cout << "x simdi: " << x << std::endl;

    x = 5;
    int sonuc2 = guvenli_max(x++, y);  // x sadece bir kere artar
    std::cout << "constexpr sonuc: " << sonuc2 << std::endl;
    std::cout << "x simdi: " << x << std::endl;

    return 0;
}

⚠️ Dikkat: #define sabitleri ve makro fonksiyonları modern C++'ta kullanma. const ve constexpr tip güvenli, kapsam duyarlı ve debug edilebilir alternatifler sunar. #define sadece include guard (#ifndef) ve platform koşullu derleme (#ifdef) gibi preprocessor işlemleri için kullan.


constinit (C++20) — Kısa Giriş

constinit global/statik değişkenlerin derleme zamanında başlatılmasını garanti eder ama const yapmaz:

#include <iostream>

constinit int global_sayac = 0;  // Derleme zamaninda baslatilir

int main() {
    global_sayac = 42;  // Degistirilebilir! (const degil)
    std::cout << "Sayac: " << global_sayac << std::endl;

    return 0;
}

constinit "static initialization order fiasco" adlı sinsi bir hatayı önlemek için kullanılır. Başlangıçta bu detayı bilmene gerek yok — ileride global değişkenler ve static initialization konusunda karşına çıkacak.


const Doğruluğu (const Correctness)

"const correctness" bir kodlama disiplinidir: değiştirilmemesi gereken her şeyi const yap. Bu prensip C++'ta çok önemsenir:

#include <iostream>
#include <string>
#include <vector>

class Ogrenci {
    std::string isim_;
    int not_;

public:
    Ogrenci(std::string isim, int not_degeri)
        : isim_(std::move(isim)), not_(not_degeri) {}

    // const metod — nesneyi degistirmez
    const std::string& isim() const { return isim_; }
    int not_degeri() const { return not_; }

    // non-const metod — nesneyi degistirir
    void not_guncelle(int yeni_not) { not_ = yeni_not; }
};

// const referans — listeyi degistirmez
void listele(const std::vector<Ogrenci>& liste) {
    for (const auto& o : liste) {
        std::cout << o.isim() << ": " << o.not_degeri() << std::endl;
    }
}

int main() {
    std::vector<Ogrenci> sinif;
    sinif.emplace_back("Ahmet", 85);
    sinif.emplace_back("Ayse", 92);

    listele(sinif);

    return 0;
}

const correctness ilkeleri:

  • Değişmeyecek değişkenleri const yap

  • Nesneyi değiştirmeyen metotları const işaretle

  • Fonksiyon parametrelerini mümkünse const & al

  • Bu alışkanlığı baştan edin — sonradan eklemek çok daha zor


Özet

  • const değişkeni salt okunur yapar; tanımlanırken başlatılmalıdır

  • constexpr derleme zamanı hesaplaması garanti eder; sabitler ve fonksiyonlarda kullanılır

  • const pointer ile pointer to const farklıdır: *'dan önce → değer sabit, *'dan sonra → pointer sabit

  • #define makroları tip güvensiz ve kapsamsızdır; modern C++'ta const/constexpr kullan

  • consteval (C++20) fonksiyonun mutlaka derleme zamanında çalışmasını zorunlu kılar

  • const correctness prensibini uygula: değişmeyecek her şeyi const yap — hatalardan korur ve niyetini açıkça belirtir