inline, Scope ve Lifetime
Bir evin odalarını düşün. Her odanın kendi eşyaları var — yatak odasındaki yastık salon da mevcut değil. Kapıdan çıkınca odadaki eşyalara erişemezsin. Programlamada scope (kapsam) tam olarak bu: bir değişkenin görülebilir ve erişilebilir olduğu kod bölgesi. Lifetime (ömür) ise o değişkenin bellekte ne kadar süre yaşadığı. Bu derste ayrıca inline fonksiyonları ve static local değişkenleri de öğreneceksin.
inline Fonksiyonlar
Normal bir fonksiyon çağrısında şunlar olur: argümanlar stack'e yerleştirilir, program akışı fonksiyona atlar, fonksiyon çalışır, dönüş değeri geri aktarılır ve akış çağrılan yere döner. Bu adımlar küçük ama her çağrıda tekrarlanır.
inline fonksiyonlar derleyiciye bir öneridir: "Bu fonksiyonu çağırmak yerine kodunu doğrudan çağrı noktasına yerleştir." Bu, fonksiyon çağrı maliyetini ortadan kaldırır.
#include <iostream>
using namespace std;
inline int kare(int x) {
return x * x;
}
inline int maksimum(int a, int b) {
return (a > b) ? a : b;
}
int main() {
// Derleyici bunu şöyle dönüştürebilir:
// int sonuc = 5 * 5; (fonksiyon çağrısı yok)
int sonuc = kare(5);
cout << "5² = " << sonuc << endl;
cout << "Max: " << maksimum(10, 20) << endl;
return 0;
}inline Hakkında Önemli Noktalar
inlinebir öneridir, zorunluluk değil. Derleyici inline yapmayı uygun görmezse (mesela fonksiyon çok büyükse) normal çağrı yapar.Modern derleyiciler
-O2ve üstü optimizasyon seviyelerindeinlineyazmasan bile küçük fonksiyonları otomatik inline eder.inlineanahtar kelimesinin asıl pratik anlamı: aynı fonksiyon tanımını birden fazla çeviri biriminde (translation unit / .cpp dosyası) kullanabilmeyi sağlar. Bu header dosyalarında fonksiyon tanımlamak için kritik.
// utils.h — inline olmadan birden fazla .cpp'den include edilirse
// "multiple definition" linker hatası alırsın
inline double daireAlani(double r) {
return 3.14159265 * r * r;
}Ne Zaman inline Kullan?
Küçük, basit fonksiyonlar: 1-5 satır, sık çağrılan
Header dosyasındaki tanımlar: Birden fazla .cpp'den include edilecekse
constexpr fonksiyonlar: Zaten örtük olarak inline
// constexpr — derleme zamanında hesaplanabilir, örtük inline
constexpr int kup(int x) {
return x * x * x;
}💡 İpucu: Modern C++'ta
inlineanahtar kelimesini performans için değil, header'da tanım yazmak için kullan. Performans optimizasyonunu derleyiciye bırak — derleyici bu konuda insandan çok daha iyi karar verir.
Local Scope — Yerel Kapsam
Bir değişken süslü parantez {} içinde tanımlandığında, sadece o blokta erişilebilir. Blok bittiğinde değişken yok olur.
#include <iostream>
using namespace std;
int main() {
int x = 10; // main'in scope'u
if (x > 5) {
int y = 20; // if bloğunun scope'u
cout << x << endl; // OK — x üst scope'ta
cout << y << endl; // OK — y bu scope'ta
}
cout << x << endl; // OK
// cout << y << endl; // DERLEME HATASI — y burada erişilemez!
return 0;
}Fonksiyon parametreleri ve içinde tanımlanan değişkenler local scope'a sahiptir:
void hesapla(int n) { // n: local scope
int sonuc = n * 2; // sonuc: local scope
cout << sonuc << endl;
}
// n ve sonuc burada mevcut değilDöngü Değişkenleri
for döngüsünde tanımlanan değişken sadece döngü içinde yaşar:
for (int i = 0; i < 5; i++) {
cout << i << endl; // OK
}
// cout << i << endl; // HATA — i burada yok
// while ve do-while'da değişken dışarıda tanımlanır
int j = 0;
while (j < 5) {
cout << j << endl;
j++;
}
cout << j << endl; // OK — j hâlâ erişilebilir (değeri 5)Global Scope — Evrensel Kapsam
Tüm fonksiyonların dışında tanımlanan değişkenler global scope'a sahiptir. Programın her yerinden erişilebilir.
#include <iostream>
using namespace std;
int sayac = 0; // Global değişken
void arttir() {
sayac++; // Global değişkene erişim
}
void yazdir() {
cout << "Sayaç: " << sayac << endl;
}
int main() {
arttir();
arttir();
arttir();
yazdir(); // Sayaç: 3
return 0;
}Global Değişkenlerin Tehlikeleri
Global değişkenler kullanışlı görünür ama büyük projelerde ciddi sorunlara yol açar:
Kim değiştirdi? Herhangi bir fonksiyon her an değiştirebilir — hata ayıklama kabusu
İsim çakışması: Farklı dosyalarda aynı isimde global değişken tanımlanabilir
Test edilemezlik: Fonksiyonlar global duruma bağımlı olunca birim testi zorlaşır
Thread güvenliği: Çok iş parçacıklı programlarda yarış koşulları oluşur
⚠️ Dikkat: Global değişkenlerden mümkün olduğunca kaçın. Veriye ihtiyaç duyan fonksiyonlara parametre olarak geçir. Global değişken kullanman gereken ender durumlar: sabitler (
const/constexpr) ve konfigürasyon ayarları.
// İYİ — global sabit
constexpr double PI = 3.14159265358979;
const int MAX_KULLANICI = 1000;
// KÖTÜ — global değişken (değiştirilebilir)
int aktifKullaniciSayisi = 0; // Kim, ne zaman değiştirecek?Block Scope ve Variable Shadowing
İç içe bloklarda aynı isimde değişken tanımlayabilirsin. İç bloktaki değişken dış bloktakini gölgeler (shadow). Bu demek ki iç blokta dış değişkene erişemezsin — aynı isim iç bloktaki değişkeni ifade eder.
#include <iostream>
using namespace std;
int x = 100; // Global
int main() {
int x = 50; // Local — global x'i gölgeler
cout << "main x: " << x << endl; // 50
{
int x = 10; // İç blok — üst scope'taki x'i gölgeler
cout << "iç blok x: " << x << endl; // 10
}
cout << "main x (tekrar): " << x << endl; // 50 — iç blok bitti
// Global x'e erişmek için scope resolution operatörü
cout << "global x: " << ::x << endl; // 100
return 0;
}::x söz dizimi scope resolution operatörü ile global scope'taki x'e doğrudan erişir.
Shadowing Tehlikeli mi?
Çoğu zaman evet. Farklı scope'larda aynı isimli değişkenler kafa karıştırır ve hataya yol açar. Derleyicilere -Wshadow uyarısını açarak bu durumları yakalayabilirsin.
// KÖTÜ — gölgeleme, kafa karıştırıcı
void hesapla(int deger) {
for (int i = 0; i < 10; i++) {
int deger = i * 2; // Parametre gölgelendi!
cout << deger << endl; // Hangisi? İç bloktaki!
}
cout << deger << endl; // Parametredeki orijinal
}
// İYİ — farklı isimler, açık ve net
void hesapla_temiz(int giris) {
for (int i = 0; i < 10; i++) {
int sonuc = i * 2;
cout << sonuc << endl;
}
cout << giris << endl;
}💡 İpucu: Derleyici uyarılarını aç:
g++ -Wall -Wshadow. Shadow uyarıları çok değerli bug'ları erken yakalar.
static Local Değişkenler
Normal local değişkenler fonksiyon her çağrıldığında oluşturulur ve fonksiyon bitince yok edilir. static local değişkenler ise farklıdır: fonksiyon ilk çağrıldığında bir kez oluşturulur ve program bitene kadar yaşar. Sonraki çağrılarda aynı değişken kullanılır — değeri korunur.
#include <iostream>
using namespace std;
void sayacFonksiyon() {
static int sayac = 0; // Sadece ilk çağrıda 0'a ayarlanır
sayac++;
cout << "Çağrı sayısı: " << sayac << endl;
}
int main() {
sayacFonksiyon(); // Çağrı sayısı: 1
sayacFonksiyon(); // Çağrı sayısı: 2
sayacFonksiyon(); // Çağrı sayısı: 3
sayacFonksiyon(); // Çağrı sayısı: 4
return 0;
}static int sayac = 0; satırı sadece bir kez çalışır. Sonraki çağrılarda atlanır ve sayac'ın önceki değeri korunur.
static Local Ne Zaman Kullanılır?
Çağrı sayacı: Fonksiyonun kaç kez çağrıldığını takip etmek
Lazy initialization: Pahalı bir nesneyi ilk ihtiyaç anında oluşturmak
Singleton pattern: Tek bir nesne örneğini garanti etmek
#include <iostream>
#include <string>
using namespace std;
// Lazy initialization — sadece ilk çağrıda oluşturulur
const string& getConfig() {
static string config = "Varsayılan ayarlar yüklendi";
// Pahalı başlatma sadece ilk çağrıda yapılır
return config;
}
// Benzersiz ID üreteci
int benzersizId() {
static int sonId = 0;
return ++sonId;
}
int main() {
cout << getConfig() << endl;
cout << "ID: " << benzersizId() << endl; // 1
cout << "ID: " << benzersizId() << endl; // 2
cout << "ID: " << benzersizId() << endl; // 3
return 0;
}Lifetime — Değişkenlerin Ömrü
C++'ta üç tür ömür vardır:
1. Automatic Lifetime (Otomatik Ömür)
Local değişkenler — tanımlandığında oluşur, scope bitince yok olur.
void fonksiyon() {
int x = 10; // x oluşturuldu
string s = "hi"; // s oluşturuldu
} // x ve s yok edildiBu en yaygın ve en güvenli ömür türüdür. Bellek yönetimi otomatiktir — sen bir şey yapmana gerek yok.
2. Static Lifetime (Statik Ömür)
Global değişkenler ve static local değişkenler — program başında oluşur (veya ilk kullanımda), program bitince yok olur.
int globalDegisken = 42; // Program başında oluşur
void fonksiyon() {
static int sayac = 0; // İlk çağrıda oluşur
sayac++;
}
// Her ikisi de program sonuna kadar yaşar3. Dynamic Lifetime (Dinamik Ömür)
new ile oluşturulan nesneler — programcı oluşturur, programcı yok etmeli (delete). Bunu ilerideki derslerde detaylı öğreneceksin.
void fonksiyon() {
int* p = new int(42); // Dinamik ömür — programcı yönetir
// ... kullan ...
delete p; // Programcı silmeli!
}Lifetime Özet Tablosu
| Tür | Ne Zaman Oluşur | Ne Zaman Yok Olur | Örnek |
|---|---|---|---|
| Automatic | Tanımlandığında | Scope bitince | Local değişkenler |
| Static | Program başında / ilk kullanım | Program sonunda | Global, static local |
| Dynamic | new çağrıldığında | delete çağrıldığında | Heap nesneleri |
⚠️ Dikkat: Dynamic lifetime en tehlikeli olandır —
deleteunutursan bellek sızıntısı (memory leak), yanlış zamandadeleteyaparsan çöken program. Modern C++'taunique_ptrveshared_ptrkullanarak bu riski ortadan kaldırabilirsin (ilerideki ders konusu).
Her Şeyi Bir Arada — Pratik Örnek
#include <iostream>
#include <string>
using namespace std;
// Global sabit — OK, değişmiyor
constexpr int MAX_LOG = 100;
// inline fonksiyon — küçük, sık çağrılan
inline string seviyeEtiketi(int seviye) {
switch (seviye) {
case 0: return "DEBUG";
case 1: return "INFO";
case 2: return "WARNING";
case 3: return "ERROR";
default: return "UNKNOWN";
}
}
void logYaz(const string& mesaj, int seviye = 1) {
// static local — çağrılar arası durum koruma
static int logSayaci = 0;
if (logSayaci >= MAX_LOG) {
cout << "[UYARI] Log limiti aşıldı!" << endl;
return;
}
logSayaci++;
// Block scope — etiket sadece burada yaşar
{
string etiket = seviyeEtiketi(seviye);
cout << "[" << logSayaci << "] ["
<< etiket << "] " << mesaj << endl;
}
// etiket burada erişilemez — temiz scope
}
int main() {
logYaz("Sistem başlatıldı");
logYaz("Kullanıcı giriş yaptı");
logYaz("Disk alanı azalıyor", 2);
logYaz("Veritabanı hatası", 3);
return 0;
}Bu örnekte:
MAX_LOG: global constexpr sabit (static lifetime)seviyeEtiketi: inline fonksiyonlogSayaci: static local değişken (çağrılar arası değer korur)etiket: block scope değişken (scope bitince yok olur)
Özet
inline fonksiyonlar derleyiciye "çağrı yerine kodu yerleştir" önerisidir — asıl pratik faydası header'da tanım yapabilmektir
Local scope süslü parantez
{}ile belirlenir — değişken blok bitince erişilemez ve yok olurGlobal scope fonksiyonların dışındadır — her yerden erişilebilir ama tehlikelidir, mümkünse
constexprsabitler dışında kullanmaVariable shadowing iç bloktaki değişkenin dış bloktakini gölgelemesidir — kafa karıştırır,
-Wshadowile yakalastatic local değişkenler fonksiyon çağrıları arasında değerini korur — sayaç, lazy init ve singleton için kullanışlı
Lifetime: automatic (scope ile), static (program ömrü), dynamic (
new/deleteile) — modern C++'ta smart pointer'lar dynamic ömrü güvenli hale getirir
AI Asistan
Sorularını yanıtlamaya hazır