- İşaretçinin ne olduğunu açıklamak,
&ile adres almak ve*ile değere erişmek - İşaretçileri referanslarla karşılaştırmak ve
nullptrdenetimi yapmak newvedeleteile dinamik belleğin nasıl ayrıldığını ve risklerini açıklamak- RAII ilkesini ve
std::unique_ptr'ı uygulamak
Bir arkadaşına kitap vermek istiyorsun ama kitap çok ağır. Onu taşımak yerine bir kâğıda adresini yazıyorsun: “Nizami Caddesi 12, 3. raf”. Arkadaşın adresle kitabı kendisi bulabilir. C++'ta işaretçi tam olarak böyle bir kâğıttır: değerin kendisini değil, bellekteki adresini saklar. İşaretçiler C++'a hız ve esneklik kazandırır, ama dikkatsizlik pahalıya mal olabilir.
Adresler ve işaretçiler
Bilgisayarın belleği numaralandırılmış hücrelerden oluşur ve her değişken belirli bir adreste bulunur. & operatörü bir değişkenin adresini döndürür. Adres genellikle on altılık sistemde yazdırılır ve program her çalıştığında farklı olabilir:
#include <iostream>
int main() {
int coins = 50;
std::cout << "Value: " << coins << '\n';
std::cout << "Address: " << &coins << '\n';
return 0;
}0x7ffc5a2b1c4c gibi bir adres görürsün; her bilgisayarda ve her çalıştırmada farklıdır.Başka bir değişkenin bellek adresini saklayan değişken. int* p yazımı “p, int türünde bir değere işaretçidir” anlamına gelir. *p ifadesi o adresteki değere erişir; buna İngilizcede dereference denir.
#include <iostream>
int main() {
int coins = 50;
int* ptr = &coins;
std::cout << "coins = " << coins << '\n';
std::cout << "*ptr = " << *ptr << '\n';
*ptr = 75;
std::cout << "coins = " << coins << '\n';
std::cout << std::boolalpha << (ptr == &coins) << '\n';
return 0;
}coins = 50 *ptr = 50 coins = 75 true
*ptr = 75, işaretçinin adresindeki değeri, yani coins'in kendisini değiştirir.| Yazım | Anlamı |
|---|---|
&x | x değişkeninin adresi |
int* p | int'e işaretçi bildirimi |
*p | p adresindeki değer |
nullptr | hiçbir yeri göstermeyen işaretçi |
p->name | (*p).name ifadesinin kısa yazımı |
Referanslar ve nullptr
Önceki derslerde referanslarla (int& ref = a;) tanışmıştın: referans, bir değişkenin ikinci adıdır. İşaretçiden farklı olarak referans, bildirildiği anda mutlaka bir değişkene bağlanır ve sonra başka bir değişkene bağlanamaz. İşaretçi ise istenildiği zaman başka bir adrese yöneltilebilir, hatta hiçbir yeri göstermeyebilir.
#include <iostream>
int main() {
int a = 10;
int b = 20;
int& ref = a;
int* ptr = &a;
ref += 5;
std::cout << "a = " << a << '\n';
ptr = &b;
*ptr += 5;
std::cout << "b = " << b << '\n';
std::cout << "a = " << a << '\n';
return 0;
}a = 15 b = 25 a = 15
Hiçbir yeri göstermeyen işaretçiye nullptr atanır. nullptr üzerinden değere erişmek (*p) tanımsız davranıştır ve genellikle programı çökertir. Bu yüzden işaretçi boş olabiliyorsa kullanmadan önce denetle:
#include <iostream>
void printValue(const int* p) {
if (p == nullptr) {
std::cout << "No value\n";
return;
}
std::cout << "Value: " << *p << '\n';
}
int main() {
int level = 3;
printValue(&level);
printValue(nullptr);
return 0;
}Value: 3 No value
Dinamik bellek: new ve delete
Sıradan yerel değişkenler yığında (stack) oluşturulur ve fonksiyon bitince otomatik olarak silinir. Ama bazen belleği program çalışırken ve gereken boyutta ayırmak gerekir. new operatörü öbek (heap) bellekte yer ayırır ve adresini döndürür. Bu bellek kendiliğinden serbest kalmaz: onu delete ile (dizi ise delete[] ile) elle geri vermen gerekir.
#include <iostream>
int main() {
int* p = new int(42);
std::cout << *p << '\n';
delete p;
p = nullptr;
int size = 4;
int* squares = new int[size];
for (int i = 0; i < size; i++) {
squares[i] = i * i;
}
std::cout << squares[3] << '\n';
delete[] squares;
return 0;
}42 9
RAII ve akıllı işaretçiler
Modern C++ bu sorunları RAII (Resource Acquisition Is Initialization) ilkesiyle çözer: bir nesne kaynağı sahiplenir ve kendisi yok edildiğinde kaynağı otomatik olarak geri verir. std::vector ve std::string tam olarak böyle çalışır. Tek bir nesne için <memory> dosyasındaki akıllı işaretçi std::unique_ptr vardır: belleğin tek sahibidir ve kapsam dışına çıktığında belleği kendisi siler.
#include <iostream>
#include <memory>
#include <utility>
int main() {
std::unique_ptr<int> score = std::make_unique<int>(100);
*score += 50;
std::cout << "Score: " << *score << '\n';
std::unique_ptr<int> other = std::move(score);
std::cout << std::boolalpha << (score == nullptr) << '\n';
std::cout << "Other: " << *other << '\n';
return 0;
} // other deletes the memory automaticallyScore: 150 true Other: 150
unique_ptr kopyalanamaz; sahiplik yalnızca std::move ile devredilebilir, ardından score boş kalır.Önemli noktalar
- İşaretçi başka bir değişkenin adresini saklar:
int* p = &x;. &adresi verir,*ise adresteki değere erişir.- Referans her zaman tek bir değişkene bağlıdır; işaretçi başka yere yöneltilebilir ve
nullptrolabilir. newile ayrılan bellekdelete(diziler içindelete[]) ile geri verilmelidir, yoksa sızıntı oluşur.- RAII:
std::vector,std::stringvestd::unique_ptrbelleği otomatik olarak geri verir; onları tercih et.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
&x ifadesi ne verir?