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; // OKconst 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;
}| Özellik | const | constexpr |
|---|---|---|
| Değiştirilebilir mi? | Hayır | Hayır |
| Derleme zamanı zorunlu mu? | Hayır | Evet |
| Çalışma zamanı değer alabilir mi? | Evet | Hayır |
| Dizi boyutu olarak kullanılabilir mi? | Bazen | Her zaman |
| Fonksiyonlarda kullanılabilir mi? | Hayır | Evet |
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
| Özellik | constexpr | consteval |
|---|---|---|
| Derleme zamanında çalışır | Evet (mümkünse) | Evet (zorunlu) |
| Çalışma zamanında çalışır | Evet | Hayır |
| Esneklik | Yüksek | Düşük |
| Garanti | Yok (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 gosterebilir2. const Pointer (Pointer kendisi sabit)
int* const ptr = &x;
// ptr baska yere gosteremez, ama gosterdigi deger degistirilebilir3. const Pointer to const (Her ikisi de sabit)
const int* const ptr = &x;
// Ne ptr baska yere gosterebilir, ne gosterdigi deger degistirilebilirHepsini 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 | #define | const | constexpr |
|---|---|---|---|
| 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?
Tip güvenliği yok: Preprocessor sadece metin değiştirme yapar, tip kontrolü olmaz
Kapsam yok:
#defineher yerde geçerli, namespace veya sınıf sınırı tanımazDebug zor: Hata mesajlarında makro adı yerine değiştirilen metin görünür
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:
#definesabitleri ve makro fonksiyonları modern C++'ta kullanma.constveconstexprtip güvenli, kapsam duyarlı ve debug edilebilir alternatifler sunar.#definesadece 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
constyapNesneyi değiştirmeyen metotları
constişaretleFonksiyon parametrelerini mümkünse
const &alBu 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/constexprkullanconsteval (C++20) fonksiyonun mutlaka derleme zamanında çalışmasını zorunlu kılar
const correctness prensibini uygula: değişmeyecek her şeyi
constyap — hatalardan korur ve niyetini açıkça belirtir
AI Asistan
Sorularını yanıtlamaya hazır