Dynamic Casting ve RTTI
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 62 / 104.
Polimorfizm ile çalışırken bazen, çalışma zamanında bir nesnenin gerçek türünü belirlemeniz veya bir temel sınıf işaretçisini güvenli bir şekilde türetilmiş sınıf işaretçisine dönüştürmeniz gerekir. C++, bu durumlar için RTTI (Çalışma Zamanı Tür Bilgisi) ve dynamic_cast sağlar.
dynamic_cast, bir kalıtım hiyerarşisi içindeki işaretçileri veya referansları güvenli bir şekilde dönüştürür. static_cast'ten farklı olarak, bir çalışma zamanı kontrolü gerçekleştirir ve dönüşüm geçersizse nullptr döndürür:
class Animal {
public:
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void bark() { std::cout << "Woof!" << std::endl; }
};
class Cat : public Animal {};
Animal* animal = new Dog();
Dog* dog = dynamic_cast<Dog*>(animal); // Başarılı: geçerli bir işaretçi döndürür
if (dog) {
dog->bark(); // Dog'a özgü metodu çağırmak güvenlidir
}
Cat* cat = dynamic_cast<Cat*>(animal); // Başarısız: nullptr döndürürÖnemli: dynamic_cast yalnızca polimorfik türlerle (en az bir sanal fonksiyona sahip sınıflar) çalışır. typeid operatörü bir nesnenin gerçek türünü sorgulamanıza olanak tanır:
#include <typeinfo>
Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl; // Dog için tür bilgisini yazdırırdynamic_cast yararlı olsa da, sık kullanımı genellikle bir tasarım sorununa işaret eder. Mümkün olduğunda sanal fonksiyonları tercih edin; çünkü bunlar, nesnenin açık bir tür denetimi yapmadan türe özgü davranışı işlemesine olanak tanır.
Görev
KolayFarklı araç türlerini güvenli bir şekilde tanımlamak ve onlarla etkileşime geçmek için dynamic_cast kullanan bir araç muayene sistemi oluşturalım. Bir müfettişin yalnızca belirli araçların desteklediği türe özgü kontrolleri yapması gereken bir araç hiyerarşisi oluşturacaksınız.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
Vehicle.h: Sistemdeki herhangi bir aracı temsil eden bir temelVehiclesınıfı tanımlayın:- Bir protected
std::string licensePlateüyesi - Plakayı başlatan bir yapıcı (constructor)
"Vehicle: <licensePlate>"döndüren bir sanalgetDescription()metodu- Bir sanal yıkıcı (destructor)
- Bir protected
Vehicles.h: Üç türetilmiş araç türü tanımlayın:Car:- Bir private
int seatCountüyesi - Plaka ve koltuk sayısını alan bir yapıcı
"Car: <licensePlate>"döndürmek içingetDescription()metodunu geçersiz kılın (override)- Şunu yazdıran bir
inspectSeatbelts()metodu:Inspecting <seatCount> seatbelts in <licensePlate>
Truck:- Bir private
double cargoCapacityüyesi (ton cinsinden) - Plaka ve kargo kapasitesini alan bir yapıcı
"Truck: <licensePlate>"döndürmek içingetDescription()metodunu geçersiz kılın (override)- Şunu yazdıran bir
inspectCargo()metodu:Inspecting cargo area (<cargoCapacity> tons) in <licensePlate>
Motorcycle:- Bir private
bool hasSidecarüyesi - Plaka ve sepet (sidecar) durumunu alan bir yapıcı
"Motorcycle: <licensePlate>"döndürmek içingetDescription()metodunu geçersiz kılın (override)- Eğer sepeti varsa
Inspecting helmet storage in <licensePlate>, yoksaNo helmet storage in <licensePlate>yazdıran birinspectHelmetStorage()metodu
- Bir private
main.cpp: Üç girdi okuyun (her biri ayrı bir satırda):- Araba plakası
- Kamyon plakası
- Motosiklet plakası
4 koltuklu bir
Car, 10.5 ton kapasiteli birTruckve sepeti olan birMotorcycleoluşturun. Üçünü de birVehicle*işaretçi dizisinde saklayın.Dizi boyunca döngü kurun ve her araç için:
getDescription()kullanarak açıklamasını yazdırın- Her bir türetilmiş türe dönüştürmeyi denemek için
dynamic_castkullanın - Eğer
Car*dönüşümü başarılı olursa,inspectSeatbelts()metodunu çağırın - Eğer
Truck*dönüşümü başarılı olursa,inspectCargo()metodunu çağırın - Eğer
Motorcycle*dönüşümü başarılı olursa,inspectHelmetStorage()metodunu çağırın
Her aracın muayenesi arasına boş bir satır yazdırın. İşiniz bittiğinde dinamik olarak ayrılmış nesneleri temizleyin.
Örneğin, ABC-123, TRK-456 ve MTR-789 girdileriyle:
Car: ABC-123
Inspecting 4 seatbelts in ABC-123
Truck: TRK-456
Inspecting cargo area (10.5 tons) in TRK-456
Motorcycle: MTR-789
Inspecting helmet storage in MTR-789dynamic_cast'in yalnızca gerçek nesne türü hedef türle eşleştiğinde geçerli bir işaretçi döndürdüğüne dikkat edin. Her araç için, üç dönüşümden yalnızca biri başarılı olacak ve türe özgü muayene metodunu güvenli bir şekilde çağırmanıza olanak tanıyacaktır. Bu, RTTI'nın (Çalışma Zamanı Tür Bilgisi) gücüdür — çalışma zamanında gerçek türü belirlemek ve buna göre hareket etmek.
Kopya kağıdı
C++, çalışma zamanında bir nesnenin gerçek türünü belirlemek ve bir kalıtım hiyerarşisi içindeki işaretçileri güvenli bir şekilde dönüştürmek için RTTI (Runtime Type Information - Çalışma Zamanı Tür Bilgisi) ve dynamic_cast sağlar.
dynamic_cast bir çalışma zamanı kontrolü gerçekleştirir ve dönüşüm geçersizse nullptr döndürür:
class Animal {
public:
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void bark() { std::cout << "Woof!" << std::endl; }
};
Animal* animal = new Dog();
Dog* dog = dynamic_cast<Dog*>(animal); // Başarılı: geçerli işaretçi döndürür
if (dog) {
dog->bark(); // Dog sınıfına özgü metodu çağırmak güvenlidir
}
Cat* cat = dynamic_cast<Cat*>(animal); // Başarısız: nullptr döndürürÖnemli: dynamic_cast yalnızca polimorfik türlerle (en az bir sanal fonksiyona sahip sınıflar) çalışır.
typeid operatörü bir nesnenin gerçek türünü sorgular:
#include <typeinfo>
Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl; // Dog için tür bilgisini yazdırırKullanışlı olsa da, dynamic_cast'in sık kullanımı genellikle bir tasarım sorununa işaret eder. Mümkün olduğunda sanal fonksiyonları tercih edin.
Kendin dene
#include <iostream>
#include <string>
#include "Vehicle.h"
#include "Vehicles.h"
using namespace std;
int main() {
// Girdileri oku
string carPlate, truckPlate, motorcyclePlate;
cin >> carPlate;
cin >> truckPlate;
cin >> motorcyclePlate;
// TODO: 4 koltuklu bir Car oluştur
// TODO: 10.5 ton kapasiteli bir Truck oluştur
// TODO: Sepetli (true) bir Motorcycle oluştur
// TODO: Üçünü de bir Vehicle* işaretçi dizisinde sakla
// TODO: Diziyi döngüye al ve her araç için:
// 1. getDescription() kullanarak açıklamasını yazdır
// 2. Her bir türetilmiş türe dönüştürmeyi denemek için dynamic_cast kullan
// 3. Car* türüne dönüştürme başarılı olursa, inspectSeatbelts() fonksiyonunu çağır
// 4. Truck* türüne dönüştürme başarılı olursa, inspectCargo() fonksiyonunu çağır
// 5. Motorcycle* türüne dönüştürme başarılı olursa, inspectHelmetStorage() fonksiyonunu çağır
// 6. Her aracın denetimi arasına boş bir satır yazdır
// TODO: Dinamik olarak ayrılmış nesneleri temizle
return 0;
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner