
Mesela bir yazıyı veya sayıyı saklamak istiyorsunuz. Kullanıcı kendi ismini girsin ve program o ismi hafızada tutsun istiyorsunuz. O zaman kullanacağımız şey bir değişken. Farklı bir örnek vererek devam edelim.
Diyelim ki bir yaş tutacağız. Değişken, bellekte bir yere kayıt ettirdiğimiz isim + tür + değer üçlüsünü simgeler.
Tür, o yerin kaç bayt tutacağını ve o baytların nasıl yorumlanacağını söyler.
İsim, o yere kodumuzdan ulaşmamızı sağlar.
Değer, o anda orada duran bitleri simgeler. Yani “yaş” dediğimiz şey, isimlendirilmiş bir bellek parçası olarak düşünebiliriz.
Örnek değişken isimleri: yas,isim,bakiye,para gibi aklımıza gelebilecek her türlü şey
Örnek değişken türleri: int (tam sayı tutar), string (bir yazı tutar), bool (true false değeri tutar toplam 2 değer)
Örnek değişken değerleri: 15,Ahmet,299.5, 3000000, true
Örnek bir kod:
#include <iostream>
#include <string>
int main() {
std::string isim = "Ahmet";
int yas = 15;
return 0;
}gördüğünüz üzere int i direkt kullandık ama string std isim uzayında olduğu için std::string yazdık. Şimdi bu kod çalışıyor ancak bize bişey söylemiyor sadece hafızada girdilerimizi tuttu. Şimdi ekrana bunları nasıl yazdırırız?
#include <iostream>
#include <string>
int main() {
std::string isim = "Ahmet";
int yas = 15;
std::cout << "Merhaba benim ismim " << isim << ". Yasim " << yas;
return 0;
}Merhaba benim ismim Ahmet. Yasim 15Türkçe karakter kullanmayın henüz. İleride değineceğim. Burada mantık şematiğini çizeyim. Sadece yukarıdaki kodda cout olan satıra odaklanın:
std::cout
↓
"Merhaba benim ismim "
↓
isim
↓
". Yasim"
↓
yas
↓
Cümle : "Merhaba benim ismim Ahmet. Yasim 15"isim yazan yerde hafızaya bakılır. O değişkenin "Ahmet" yazısını tuttuğu görülür. Ardından "" işaretini görüp içini doğrudan yazar ardından yas değişkeninin değerine bakılır 15 demiştik bu sefer yas yazdığımız yere 15 yazar.
İleri C++ (şablonlar, taşınma semantiği, bellek modeli) yine bu 3lüye dayanır. Burada yanlış alışkanlık edinirsek ileride doğru alışkanlığı edinmek zor olabilir. Bu derste en önemli şey henüz kullanmadık ama değişkeni tanımladığımız anda anlamlı bir değer vermek; ve bunu {} ile yapmak. Yani değişkenleri int sayi = 15; değil de int sayi{15}; şeklinde tutmaktan bahsediyorum birazdan değineceğim.
Bellekte ne durur?
Mesela fonksiyonun içinde int yas; yazdık. Bu otomatik ömürlü bir değişkendir: blok bitince ömrü biter. int yas; yalnızca tanımdır, başlatma değildir. Tanımlamak nesneyi hayata geçirir; başlatmak ona bilinen bir değer verir. Başlatmadan bırakırsan C++ o nesneye “çöp duruyor, basarsın” demez. Otomatik ömürlü bir inti başlatmadan okumak C++23’te tanımsız davranıştır. Derleyici 4 bayt ayırmış gibi görünebilir; bu yaygın bir uygulama, standart her platformda 4 bayt diye garanti etmez. Boyut için sizeof(int) bakılır. O yüzden tanımlar tanımlamaz bir değer ver.
int ham; // tanımlandı, başlatılmadı — problemli olabilir
int yas = 21; // kopya başlatma
int boy(175); // doğrudan başlatma
int kilo{72}; // küme ile başlatma — modern varsayılanAynı int; ilkinde (int ham;) başlatılmadı, diğer üçü farklı başlatma yazımı. Modern C++ üçüncüyü ({}) varsayılan yapar.
Neden başlatılmamış değişken yasak? (int ham;)
Normalde int skor; yazılır, sonra bir değer verilirdi. Şimdi şöyle ki: otomatik ömürlü, başlatılmamış bir intin değerini okumak C++23’te tanımsız davranıştır (undefined behavior). “Bellekte çöp duruyor” tek başına bu kuralı karşılamaz. Çöp, rastgele bir sayı basılacağı izlenimi verir; tanımsız davranışta programın ne basacağı, çöküp çökmeyeceği, derleyicinin o kodu nasıl dönüştüreceği belirsizdir. Gördüğün çıktıya güvenilmez. Çözüm çöpü yorumlamak değil: ya gerçek değeri vermek ya {} ile değer başlatmak (int için 0).
Mesela
int skor; // başlatılmadı — değerini okumak tanımsız davranış
std::cout << skor; // yasak: okuma
int skor{}; // 0, değer başlatıldı
std::cout << skor; // doğruKural
Yerel değişkeni (int skor;) gibi bırakma. Ya hemen gerçek değeri ver, ya {} ile sıfırla.
Üç başlatma yazımı (tanımladık ancak başlatma çeşitleri nelerdir?)
C++ içinde 3 tane yöntem var. Hepsinden bahsedeceğm ancak birinden devam edeceğim. Diyelim ki 5 sayısını tutacağız. Üç yol da çalışıyor gibi duruyor. Modern pratikte üçüncüyü varsayılan olarak kullanacağım.
int x = 5;kopya başlatma. Okunur ama daraltmaya (narrowing) izin verebilir. bunun ne demek olduğundan bahsedicemint x(5);doğrudan başlatma.5gibi bir değer varken işlev sanılmaz. Karışıklıkint x();yazınca çıkar: derleyici bunu değişken değil,intdöndürenxişlevi sanır (most vexing parse).int x{5};küme başlatma. Daraltmayı reddeder, en az sürprizlidir.int x{};de her zaman değişkendir.
Neden modern C++ {} ister?
{} iki şeyi birden çözer. Birincisi *daraltma: daha büyük veya kesirli bir değeri, kendisi için normalde uygun olmayan bir türe sessizce sıkıştırmamızı engellemek için. İkincisi *ayrıştırma: int x(); bir değişken değil, “int döndüren x adında işlev/fonksiyon” diye okunabilir. int x{}; her zaman değişken olduğunu bize gösterir. Mesela 3.14yi inte vermeyi deniyoruz. Eskiden çoğu derleyici uyarıyla 3.14 ü direkt 3 yapardı. Şimdi {} ile hata bekleriz. Çünkü hata çıkarsın ki 3.14 ü 3 yapmadan bize neler döndüğünü göstersin.
Yani demek istediğim şey şu: int dediğimiz şey bir tam sayı tutan değişken türü. Biz böyle bir değişken oluşturup içine 3.14 değerini yani kesirli bir değer verirsek {} haricindeki diğerleri bunu 3 e dönüştürür (virgülden sonrasını almaz). Bundan haberimiz olmazsa belki de bu hesap hatasına yol açar. O yüzden eğer int c{3.14} kullanırsak int değeri olduğu için bunu değiştirmeden hata çıkartır. Mesela
int a = 3.14; // çoğu derleyicide uyarıyla 3 olur — bilgi kaybı yani daraltma dediğimiz şey
int b(3.14); // aynı daraltma
int c{3.14}; // hata: narrowing. Modern C++ bunu istemez.
int t{}; // 0, değişken
int u(); // işlev bildirimi! most vexing parse yani u() yu ileride göreceğimiz şekilde fonksiyon sanabilir. {} daraltmayı derleme hatasına çevirir. Diğerleri gibi sayıyı manipüle etmez
İleri seviyede std::vector<int> v{1, 2, 3}; gibi yapıcıları da aynı sözdizimiyle kuracağız o yüzden şuan zorlanmanız daha iyi. Şimdi int x{5} öğrenirsen, yarın nesne kurmayı öğrenirken daha basit gelir yani öyle düşünüyorum. Bu yüzden bu seride hemen hemen her yerde {} kullanacağız. Yani şuana kadar "yeni bir isim görünce yanında bir değer de görünsün"i anlamış olmanızı beklerim
auto ile {} tuzakları
Değişken oluştururken illa değişken türünü belirtmen gerekmiyor. Mesela auto kullanırsan verdiğin değere göre otomatikmen kendisini şekillendiriyor. Mesela int a = 5 yerine auto a = 5 yapabilirsin. auto türü sağ taraftaki değerden türünü çıkarıyor. auto x = 5; bir int. Ama auto x{5}; ile auto x = {5}; aynı şey değil. İkincisi std::initializer_list<int> yani bir liste o olabiliyor. Modern pratik: auto kullanırken = ile tek değer ver ancak listeyi auto ile kullanma. Bu kısmı tamamen anlaman gerekmiyor. Sadece auto üstünden geçmek istedim
auto a = 5; // int, net şekilde
auto b = 5.0; // double, net şekilde
auto c{5}; // int. C++17'den beri bu kural kesin: tek elemanlı {} auto ile o elemanın türüdür
auto d = {5}; // initializer_list — çoğu zaman bunu oluşturmazsın zaten hönkürtmeO yüzden auto yu dikkatli kullan
Değişkeni nasıl tutmalıyız?
İleri seviyeye giden biri için değişken tutma yöntemleri uzun değil o kadar. Eskiden “zaten tanımladım, biyerlerde kullanırım” tarzında bi yaklaşım vardı. Hatayı ve okunabilirliği de en aza indirmek için dar kapsam (ismi yalnızca kullanıldığı blokta yaşatmak), hemen başlatmak (direkt değer), değişmeyecekse kilitlemek (const) en doğrusudur. Dar kapsam ile daraltma (narrowing) ayrı şeylerdir. Daraltma, int x{3.14} gibi bilgi kaybıdır. Dar kapsam, değişkenin görünür olduğu satır aralığını küçük tutmaktır.
- Mümkün olan en dar kapsamda tanımla.
- Tanıdığın anda başlat.
{}varsayılanın olsun. (önemli) - Değişmeyecekse
constyap.const int kapasite{16};hem belge hem derleyici kilididir. (constant yani sabit) - Türü doğru-mantıklı seç. Meslea yaş için
doublekullanma; adet içinintveyastd::size_tkullan. (double ondalıklı sayıları temsil eder) (double ve std::size_t bir değişkendir int gibi, kafanız karışmasın)
Kötü ve iyi örnek farkı
// Eski / tavsiye edilmeyen
int n;
n = 10;
double x = 10; // daraltma değil; 10 bir int, double'a genişler. Yaş gibi tam sayı için yanlış tür.
int flag = 1; // bool olması gereken şey
// Modern
const int kayit_sayisi{10}; //kayit sayisi değişmeyecekse mesela
const double sicaklik{10.0}; //ondalikli değerde de değişmeyecek mesela
const bool aktif{true}; //aktif olmayı false yapılmayacak yani bir sabit
auto isim = std::string{"Ahmet"}; //burda da etiketin string olduğunu biz söyledik yoksa derleyici farklı bi tür sanabilirdi. ama gerek yok std::string isim{"Ahmet"}; de kullanabilirizYukarı taraf belirsiz ve geç başlıyor. Aşağı taraf daralmış, başlatılmış, const ve okunaklı oldu.
Yani en nihayetinde int x{0}; yazmak derleyiciye “bu bir değişkendir, daraltma yok, okunabilir bir değer var” demektir. İleri C++’ta sağlam bir kod mimarı olmak istiyorsan bilmelisin.
Alıştırma
Diyelim ki bir veri tutacağız: bir yaş (int) ancak {} ile başlatın. Sonra yaşa 3.5 vermeyi deneyin ve derleyicinin neden hata verdiğine bakıp sonra düzeltip bakın mesela int yerine double kullanarak.



