Menu
Coddy logo textTech

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ır

dynamic_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.

challenge icon

Görev

Kolay

Farklı 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 temel Vehicle sınıfı tanımlayın:
    • Bir protected std::string licensePlate üyesi
    • Plakayı başlatan bir yapıcı (constructor)
    • "Vehicle: <licensePlate>" döndüren bir sanal getDescription() metodu
    • Bir sanal yıkıcı (destructor)
  • 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çin getDescription() 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çin getDescription() 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çin getDescription() metodunu geçersiz kılın (override)
    • Eğer sepeti varsa Inspecting helmet storage in <licensePlate>, yoksa No helmet storage in <licensePlate> yazdıran bir inspectHelmetStorage() metodu
  • main.cpp: Üç girdi okuyun (her biri ayrı bir satırda):
    1. Araba plakası
    2. Kamyon plakası
    3. Motosiklet plakası

    4 koltuklu bir Car, 10.5 ton kapasiteli bir Truck ve sepeti olan bir Motorcycle oluşturun. Üçünü de bir Vehicle* işaretçi dizisinde saklayın.

    Dizi boyunca döngü kurun ve her araç için:

    1. getDescription() kullanarak açıklamasını yazdırın
    2. Her bir türetilmiş türe dönüştürmeyi denemek için dynamic_cast kullanın
    3. Eğer Car* dönüşümü başarılı olursa, inspectSeatbelts() metodunu çağırın
    4. Eğer Truck* dönüşümü başarılı olursa, inspectCargo() metodunu çağırın
    5. 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-789

dynamic_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ır

Kullanış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;
}
quiz iconKendini test et

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