Dynamic Casting ve RTTI
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 62 / 104.
Bazen polimorfizm ile çalışırken bir nesnenin çalışma zamanındaki gerçek türünü belirlemeniz veya bir temel sınıf işaretçisini türetilmiş sınıf işaretçisine güvenli bir şekilde 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 çalışma zamanında bir 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 işaretçi döndürür
if (dog) {
dog->bark(); // Dog'a özgü metodu çağırmak güvenli
}
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 işlevi olan sınıflarla) çalışır. typeid işleci, 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 tip bilgisini yazdırırdynamic_cast kullanışlı olsa da sık kullanımı genellikle bir tasarım sorununa işaret eder. Mümkün olduğunda sanal işlevleri tercih et; çünkü bunlar, açık tür denetimi olmadan nesnenin türe özgü davranışı ele almasını sağlar.
Görev
KolayFarklı araç türlerini güvenli bir şekilde tanımlamak ve bunlarla etkileşim kurmak için dynamic_cast kullanan bir araç denetim sistemi oluşturalım. Belirli araçların desteklediği türe özgü kontrolleri bir denetçinin gerçekleştirmesi gereken bir araç hiyerarşisi oluşturacaksınız.
Kodunuzu üç dosyada düzenleyeceksiniz:
Vehicle.h: Sistemdeki herhangi bir aracı temsil eden bir temelVehiclesınıfı tanımlayın:- Korunan bir
std::string licensePlateüyesi - Plaka numarasını başlatan bir oluşturucu
getDescription()döndüren sanal bir"Vehicle: <licensePlate>"yöntemi- Sanal bir yok edici
- Korunan bir
Vehicles.h: Türetilmiş üç araç türü tanımlayın:Car:- Özel bir
int seatCountüyesi - Plaka numarasını ve koltuk sayısını alan bir oluşturucu
getDescription()döndürecek şekilde"Car: <licensePlate>"yöntemini geçersiz kılmainspectSeatbelts()yazdıran birInspecting <seatCount> seatbelts in <licensePlate>yöntemi
Truck:- Özel bir
double cargoCapacityüyesi (ton cinsinden) - Plaka numarasını ve yük kapasitesini alan bir oluşturucu
getDescription()döndürecek şekilde"Truck: <licensePlate>"yöntemini geçersiz kılmainspectCargo()yazdıran birInspecting cargo area (<cargoCapacity> tons) in <licensePlate>yöntemi
Motorcycle:- Özel bir
bool hasSidecarüyesi - Plaka numarasını ve sepet durumunu alan bir oluşturucu
getDescription()döndürecek şekilde"Motorcycle: <licensePlate>"yöntemini geçersiz kılma- Sepeti varsa
inspectHelmetStorage(), yoksaInspecting helmet storage in <licensePlate>yazdıran birNo helmet storage in <licensePlate>yöntemi
- Özel bir
main.cpp: Üç girdi okuyun (her biri ayrı bir satırda):- Car plaka numarası
- Truck plaka numarası
- Motorcycle plaka numarası
4 koltuklu bir
Car, 10.5 ton kapasiteli birTruckve sepetli birMotorcycleoluşturun. Üçünü deVehicle*işaretçilerinden oluşan bir dizide saklayın.Dizi üzerinde döngü kurun ve her araç için:
getDescription()kullanarak açıklamasını yazdırın- Türetilmiş her türe dönüştürmeyi denemek için
dynamic_castkullanın Car*türüne dönüştürme başarılı olursainspectSeatbelts()çağrısını yapınTruck*türüne dönüştürme başarılı olursainspectCargo()çağrısını yapınMotorcycle*türüne dönüştürme başarılı olursainspectHelmetStorage()çağrısını yapın
Her aracın denetimi arasında boş bir satır yazdırın. İşiniz bittiğinde dinamik olarak ayrılmış nesneleri temizleyin.
Örneğin, girdiler ABC-123, TRK-456 ve MTR-789 olduğunda:
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 işleminin 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üştürmeden yalnızca biri başarılı olur; bu da türe özgü denetim yöntemini güvenli bir şekilde çağırmanızı sağlar. RTTI'ın gücü budur: çalışma zamanında gerçek türü belirlemek ve buna göre hareket etmek.
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: Yan sepetli (true) bir Motorcycle oluştur
// TODO: Üçünü de Vehicle* işaretçilerinden oluşan bir dizide sakla
// TODO: Diziyi döngüyle gez ve her araç için:
// 1. getDescription() kullanarak açıklamasını yazdır
// 2. Her türetilmiş türe dönüştürmeyi denemek için dynamic_cast kullan
// 3. Car*'a dönüştürme başarılı olursa, inspectSeatbelts() çağır
// 4. Truck*'a dönüştürme başarılı olursa, inspectCargo() çağır
// 5. If cast to Motorcycle* succeeds, call inspectHelmetStorage()
// 6. Her aracın incelemesi 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 KonteynerKendi başına pratik yap: Online C++ derleyicisi