Ders 14 / 14
Tip Dönüşüm İşleçleri
static_cast, dynamic_cast, const_cast ve reinterpret_cast'ın hedef tipine göre bir referansa parça olarak mı yoksa yeni bir nesneye kopya olarak mı eriştiğinin ölçümü.
Yayın Güncelleme
Bu derste
- static_cast ile bir taban referansına dönüşümün yeni bir nesne kurmadığını, aynı adresi parça olarak andığını ölçer
- Aynı static_cast'ın hedef değer tipine dönüştüğünde nesneyi dilimleyerek yeni bir kopya kurduğunu gösterir
- dynamic_cast'ın başarısız aşağı dönüşümde işaretçi için nullptr, referans için std::bad_cast verdiğini doğrular
- const_cast ve reinterpret_cast'ın da yeni bir nesne kurmadığını, var olan nesneye farklı bir erişim yolu açtığını gösterir
İçindekiler
Önceki ders using bildiriminin ve ad alanı takma adının yeni bir sembol kurmadığını,
var olan sembole yerel bir erişim yolu açtığını gösterdi — ölçü ad alanı sınırındaydı.
Bu ders sınırı bir düzey daha indiriyor: tip dönüşüm işleçleri. Bir Termometre
nesnesini static_cast ile tabanı Olcer’e dönüştürdüğümüzde soru aynı kalıyor —
t hangi nesneyi anıyordu, dönüşüm hangi nesneyi veriyor? Cevap işlecin adına değil,
hedef tipe bağlı: aynı static_cast, hedef bir referans olduğunda t’nin içindeki
alt nesneyi anıyor; hedef bir değer olduğunda yepyeni bir nesne kuruyor.
Tabana Dönüştürülen Referans
Termometre, Olcer’den türüyor. t’yi const Olcer&’e static_cast ile
dönüştürdüğümüzde derleyici yeni bir nesne kurmuyor; t’nin içindeki Olcer
alt nesnesine daha dar bir tipten bakan bir referans üretiyor. Bu referansın
adresi t’nin adresiyle aynı aralıkta kalıyor — çünkü temel sınıf alt nesnesi
türetilmiş nesnenin bir parçası.
// taban-referans.cpp — static_cast ile referans donusumu yeni nesne kurar mi
#include <cstdio>
class Olcer {
public:
virtual ~Olcer() = default;
void oku() const { std::printf("olcer oku cagrildi\n"); }
};
class Termometre : public Olcer {
public:
int derece() const { return derece_; }
private:
int derece_ = 20;
};
int main() {
Termometre t;
const Olcer& taban = static_cast<const Olcer&>(t);
taban.oku();
std::printf("ayni adres mi %s\n",
(static_cast<const void*>(&taban) == static_cast<const void*>(&t)) ? "evet" : "hayir");
return 0;
}
olcer oku cagrildi ayni adres mi evet
CB45. taban, t içindeki Olcer alt nesnesini anıyor; oku() çağrısı yeni
bir nesne üzerinde değil, t’nin kendi parçası üzerinde çalışıyor. &taban ile
&t’nin aynı adres aralığında çıkması bunu doğruluyor — dönüşüm bir kurucu
çağırmadı, yalnız t’ye bakan tipi daralttı. Bu ifadenin sınıfı parça.
Değerle Dönüşüm: Aynı İşleç, Farklı Sınıf
Aynı t’yi bu kez hedefi bir referans değil bir değer olacak şekilde
static_cast<Olcer>(t) ile dönüştürelim. İşleç adı aynı — static_cast — ama
hedef tip artık bir referans değil, Olcer’in kendisi. Derleyici burada t’nin
Olcer alt nesnesini kopyalayarak yepyeni, bağımsız bir Olcer nesnesi kuruyor.
// deger-donusumu.cpp — static_cast deger hedefte yeni nesne kurar mi
#include <cstdio>
class Olcer {
public:
virtual ~Olcer() = default;
void oku() const { std::printf("olcer oku cagrildi\n"); }
};
class Termometre : public Olcer {
public:
int derece() const { return derece_; }
private:
int derece_ = 20;
};
int main() {
Termometre t;
Olcer kopya = static_cast<Olcer>(t);
kopya.oku();
std::printf("ayni adres mi %s\n",
(static_cast<const void*>(&kopya) == static_cast<const void*>(&t)) ? "evet" : "hayir");
return 0;
}
olcer oku cagrildi ayni adres mi hayir
CB46. kopya, t’den bağımsız yeni bir Olcer nesnesi; &kopya ile &t
farklı adreslerde çıkıyor. Bu, önceki bölümdeki static_cast<const Olcer&>(t)
ile aynı işleç olmasına rağmen tamamen farklı bir sonuç: burada t’nin
Termometre’ye özgü kısmı (derece_) atılıyor, yalnız Olcer kısmı kopyalanıyor
— bu, nesne dilimleme. Bu ifadenin sınıfı kopya.
Dinamik Dönüşüm: Gerçek Tipe Göre Karar
static_cast hedef tipe körü körüne güvenir; derleme anında karar verir.
dynamic_cast ise çalışma anında nesnenin gerçek tipine bakar — taban
referansının ardındaki nesne gerçekten istenen türetilmiş tipte değilse
dönüşüm başarısız olur. İşaretçi hedefinde başarısızlık nullptr döner;
referans hedefinde bir istisna fırlatır.
// dinamik-donusum.cpp — dynamic_cast basarisiz asagi donusumde ne verir
#include <cstdio>
#include <typeinfo>
class Olcer {
public:
virtual ~Olcer() = default;
};
class Termometre : public Olcer {
public:
int derece() const { return derece_; }
private:
int derece_ = 20;
};
class Barometre : public Olcer {
public:
int basinc() const { return basinc_; }
private:
int basinc_ = 1013;
};
int main() {
Termometre t;
Olcer& taban = t;
Barometre* p = dynamic_cast<Barometre*>(&taban);
std::printf("isaretci donusumu basarili mi %s\n", p ? "evet" : "hayir");
try {
Barometre& b = dynamic_cast<Barometre&>(taban);
(void)b;
std::printf("referans donusumu yakalandi mi hayir\n");
} catch (const std::bad_cast&) {
std::printf("referans donusumu yakalandi mi evet\n");
}
return 0;
}
isaretci donusumu basarili mi hayir referans donusumu yakalandi mi evet
CB47. taban’ın ardındaki gerçek nesne bir Termometre; Barometre
değil. dynamic_cast<Barometre*> bunu çalışma anında fark edip nullptr
döndürüyor — bir istisna fırlatmıyor, sadece “burada yok” diyor. Aynı
başarısızlık referans hedefinde std::bad_cast fırlatıyor; burada yalnız
yakalandığı yazdırılıyor, what()’in metni değil — mesajın kesin biçimi
kütüphane uygulamasına bağlı, dersin ölçtüğü şey değil. p nullptr olduğu
için burada üretilmiş bir nesne yok; başarısız bir dynamic_cast ne kopya
ne de parça verir, elde hiçbir şey kalmaz.
Sabitliği Kaldırmak: const_cast
const_cast yalnız bir referansın ya da işaretçinin sabitlik niteliğini
değiştirir; hedef tip aynı sınıf kalır, yeni bir nesne kurulmaz. Aşağıdaki
gozden_gecir, kendisine const bir referansla gelen nesneyi işaretlemek
için sabitliği bir kez kaldırıyor:
// const-cast.cpp — sabitligi kaldirmak yeni nesne kurar mi
#include <cstdio>
class Olcer {
public:
void isaretle() { isaretlendi_ = true; }
bool isaretlendi() const { return isaretlendi_; }
private:
bool isaretlendi_ = false;
};
static void gozden_gecir(const Olcer& o) {
const_cast<Olcer&>(o).isaretle();
}
int main() {
Olcer o;
gozden_gecir(o);
std::printf("isaretlendi mi %s\n", o.isaretlendi() ? "evet" : "hayir");
return 0;
}
isaretlendi mi evet
CB48. const_cast<Olcer&>(o), o’nun kendisine bakan, yalnız sabitlik
niteliği kaldırılmış bir referans veriyor — ne kopya ne parça, o’nun
kendisi. Bunun güvenli olmasının koşulu o’nun asıl nesnesinin gerçekten
const olarak tanımlanmamış olması; main’deki o sabit değil, bu yüzden
isaretle() çağrısı tanımsız davranışa girmiyor. Asıl const bir nesne
üzerinde aynı çağrı tanımsız davranış olurdu — bu ders böyle bir çağrıyı
yazmıyor da çalıştırmıyor da.
Bit Örüntüsünü Yeniden Yorumlamak: reinterpret_cast
reinterpret_cast en gevşek dönüşüm: hedef tipin kaynak tiple hiçbir ilişkisi
aranmaz, yalnız aynı bit örüntüsü farklı bir tipten okunur. Garanti edilen tek
şey, aynı tipe geri dönüşün orijinal değere eşit çıkması — ara değerin kendisi
taşınabilir bir anlam taşımaz, bu yüzden bu ders onu yazdırmıyor, yalnız
gidiş-dönüş eşitliğini ölçüyor.
// yeniden-yorumlama.cpp — reinterpret_cast gidis-donus esitligi
#include <cstdio>
#include <cstdint>
int main() {
int deger = 42;
int* p = °er;
std::uintptr_t sayisal = reinterpret_cast<std::uintptr_t>(p);
int* geri = reinterpret_cast<int*>(sayisal);
std::printf("gidis donus esit mi %s\n", (geri == p) ? "evet" : "hayir");
return 0;
}
gidis donus esit mi evet
CB49. reinterpret_cast de yeni bir nesne kurmuyor; p’nin taşıdığı bit
örüntüsünü önce bir tamsayı, sonra tekrar işaretçi olarak okuyor. geri == p
eşitliği aynı adresin geri geldiğini gösteriyor — ama bu garanti yalnızca
aynı tipe dönen gidiş-dönüş için geçerli; ara sayisal değerinin taşıdığı
sayının kendisi platforma bağlı ve dersin konusu değil.
Peki bir C tarzı dönüşüm — (Olcer)t, (Olcer&)t gibi — bu dört işleçten
hangisine çözülüyor? Derleyici sırayla dener: önce const_cast, sonra
static_cast, sonra const_cast ile birleşmiş static_cast, en sonunda
reinterpret_cast. İlk anlamlı olan seçiliyor — bu yüzden bir C tarzı
dönüşümün hangi işlece karşılık geldiği, hedef tip ve kaynak tip arasındaki
ilişkiye bakılarak çıkarılabilir, ama okurken bunu görmek static_cast ya da
dynamic_cast yazmaktan daha zor. Bu ders C tarzı dönüşümü kullanmıyor; adını
yalnız hangi işlece çözüldüğünü göstermek için anıyor.
Sınırlayıcı Ölçüm
Bu ders dört dönüşüm işlecinin dördünün de adının sınıfı belirlemediğini
gösterdi — belirleyen hedef tip. Aynı static_cast, hedef bir referans
olduğunda t’nin içindeki alt nesneyi anıyor (parça), hedef bir değer
olduğunda yeni bir nesne kuruyor (kopya). Bu ders bu ikisini ayrı ayrı ölçtü;
işlecin adına bakıp sınıfı tahmin etmek bu derste hiç yapılmadı.
Hatalı bir static_cast ile gerçekte o tipte olmayan bir nesneyi aşağı
dönüştürmek — örneğin taban’ın ardındaki nesne bir Termometre iken onu
static_cast<Barometre&> ile dönüştürmek — tanımsız davranıştır: derleyici
bunu engellemez, çalışma anında da bir denetim yapılmaz. Bu ders böyle bir
çağrıyı yazmadı da çalıştırmadı da; güvenli aşağı dönüşüm için bu dersin
gösterdiği yol dynamic_cast’tır. reinterpret_cast’ın ürettiği ara
değerlerin platforma bağlı yorumu, const_cast’ın gerçekten const bir
nesne üzerindeki tanımsız davranışı ve C tarzı dönüşümün çözüm sırasının
derleyiciler arası ayrıntıları da bu dersin dışında kaldı.
Özet
static_castile bir taban referansına dönüşüm yeni bir nesne kurmuyor;t’nin içindeki alt nesneyi daha dar bir tipten anıyor.- Aynı
static_cast, hedef bir değer olduğundat’yi dilimleyerek bağımsız yeni bir nesne kuruyor — işleç aynı, sınıf farklı. dynamic_castbaşarısız aşağı dönüşümde işaretçi içinnullptr, referans içinstd::bad_castveriyor; hiçbir durumda yeni bir nesne kurmuyor.const_castvereinterpret_castda yeni nesne kurmuyor, var olan nesneye farklı bir erişim yolu açıyor.- Bu dersin ölçtüğü ifade —
static_cast<const Olcer&>(t)ile elde edilen taban referansı — parça sınıfındadır:t’nin kendi adresini, dar bir tipten anıyor.
Kurs Kapanışı
Bu ders, C++ Temelleri ve Nesne Tasarımı kursunun son dersiydi. Kurs boyunca her ders aynı soruyu farklı bir sınırda sordu: bir ifade hangi nesneyle çalışıyor — kendisiyle mi, bir kopyasıyla mı, bir parçasıyla mı, yoksa adı olmayan bir geçiciyle mi?
| Ders | Ölçülen ifade | Hangi nesne | Sınırlayıcı ölçüm |
|---|---|---|---|
| C ve C++ Farkları | degerle aktarilan yapi |
kopya | Çağrı noktası sınıfı söylemez; karar parametre tipinde |
| Sınıflar ve Erişim Denetimi | uye islevin this'i |
kendisi | private nesneyi değil sınıfın sınırını korur |
| Yapıcılar ve Yıkıcılar | int'ten kurulan arguman |
geçici | const referansa bağlanan geçicinin ömrü uzar |
| RAII | koruyucunun biraktigi uye |
parça | Kaynağa referans tutan koruyucunun sarkması yalnız anlatıldı |
| Kopyalama ve Atama | derin kopyalanan tampon |
kopya | Kopyanın derinliği yalnız kopyalayan yapıcının gövdesinden okunur |
| Referanslar | referansla aktarilan yapi |
kendisi | const referans parametre geçiciye de bağlanabilir |
| İşleç Aşırı Yükleme | a + b sonucu |
geçici | a + b + c’de yalnız ara sonuç geçicidir; son sonuç doğrudan toplamda kurulur |
| Kalıtım | taban sinifin islevi |
parça | Tekli ve sanal olmayan kalıtımla sınırlı |
| Sanal Fonksiyonlar ve Sanal Tablolar | taban referansindan sanal cagri |
kendisi | Değerle atama ve sanal olmayan yıkıcı kapsam dışı |
| Soyut Sınıflar ve Arayüzler | arayuz uzerinden cagri |
kendisi | Çoklu arayüz kalıtımı bir sonraki derse bırakıldı |
| Çoklu Kalıtım ve Elmas Sorunu | sanal tabanin alt nesnesi |
parça | Sanal tabana erişimin dolaylılık maliyeti ölçülmedi |
| İstisnalar | degerle yakalanan istisna |
kopya | catch(...) ve noexcept içinde fırlatma kapsam dışı |
| Ad Alanları ve Kod Düzeni | using bildirimiyle anilan nesne |
kendisi | ADL ve #include mekaniği kapsam dışı |
| Tip Dönüşüm İşleçleri | tabana donusturulen referans |
parça | Hatalı static_cast aşağı dönüşümü yalnız anlatıldı, çalıştırılmadı |
5 kendisi · 3 kopya · 4 parça · 2 geçici; var olan 9, yeni 5
On dört ders boyunca aynı soru on dört farklı sınırda soruldu; cevap hep işlecin ya da sözdiziminin adına değil, derleyicinin gerçekte ne kurduğuna bakılarak verildi. Sıradaki kurs bu soruyu bir adım öteye taşıyor: bir nesnenin sahip olduğu kaynağı ne zaman bıraktığı, bir değeri kopyalamak yerine ne zaman taşıdığı ve standart kütüphanenin bu taşımayı nasıl kullandığı.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.