Dynamic Casting & RTTI
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 62번째.
다형성을 사용할 때는 실행 시간에 객체의 실제 유형을 확인하거나 기본 클래스 포인터를 파생 클래스 포인터로 안전하게 변환해야 하는 경우가 있습니다. C++는 이러한 상황을 위해 RTTI (Runtime Type Information)와 dynamic_cast를 제공합니다.
dynamic_cast는 상속 계층 구조 내에서 포인터나 참조를 안전하게 변환합니다. static_cast와 달리 런타임 검사를 수행하며 변환이 유효하지 않으면 nullptr를 반환합니다.
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); // 성공: 유효한 포인터를 반환합니다
if (dog) {
dog->bark(); // Dog 전용 메서드를 안전하게 호출할 수 있습니다
}
Cat* cat = dynamic_cast<Cat*>(animal); // 실패: nullptr를 반환합니다중요: dynamic_cast는 다형적 타입(가상 함수가 하나 이상 있는 클래스)에서만 작동합니다. typeid 연산자를 사용하면 객체의 실제 타입을 조회할 수 있습니다.
#include <typeinfo>
Animal* pet = new Dog();
std::cout << typeid(*pet).name() << std::endl; // Dog의 타입 정보를 출력합니다dynamic_cast가 유용하긴 하지만, 자주 사용하는 것은 설계상의 문제를 나타내는 경우가 많습니다. 가능하면 가상 함수를 우선 사용하세요. 가상 함수를 사용하면 명시적인 형식 검사 없이 객체가 형식별 동작을 처리할 수 있습니다.
챌린지
쉬움dynamic_cast를 사용하여 다양한 차량 유형을 안전하게 식별하고 상호 작용하는 차량 검사 시스템을 만들어 보겠습니다. 특정 차량만 지원하는 유형별 검사를 검사자가 수행해야 하는 차량 계층 구조를 만들게 됩니다.
코드를 세 개의 파일로 구성합니다:
Vehicle.h: 시스템의 모든 차량을 나타내는 기본Vehicle클래스를 define합니다:- protected
std::string licensePlate멤버 - 차량 번호판을 초기화하는 constructor
"Vehicle: <licensePlate>"를 반환하는 virtualgetDescription()메서드- virtual destructor
- protected
Vehicles.h: 세 가지 derived 차량 유형을 define합니다:Car:- private
int seatCount멤버 - 차량 번호판과 좌석 수를 받는 constructor
getDescription()을 override하여"Car: <licensePlate>"를 반환Inspecting <seatCount> seatbelts in <licensePlate>를 출력하는inspectSeatbelts()메서드
Truck:- private
double cargoCapacity멤버(톤 단위) - 차량 번호판과 화물 용량을 받는 constructor
getDescription()을 override하여"Truck: <licensePlate>"를 반환Inspecting cargo area (<cargoCapacity> tons) in <licensePlate>를 출력하는inspectCargo()메서드
Motorcycle:- private
bool hasSidecar멤버 - 차량 번호판과 사이드카 상태를 받는 constructor
getDescription()을 override하여"Motorcycle: <licensePlate>"를 반환- 사이드카가 있으면
Inspecting helmet storage in <licensePlate>를 출력하고, 없으면No helmet storage in <licensePlate>를 출력하는inspectHelmetStorage()메서드
- private
main.cpp: 세 개의 입력을 읽습니다(각각 별도의 줄에 입력):- Car 번호판
- Truck 번호판
- Motorcycle 번호판
좌석 4개인
Car, 용량 10.5톤인Truck, 사이드카가 있는Motorcycle을 Create합니다. 세 객체를 모두Vehicle*포인터의 array에 저장합니다.array를 순회하면서 각 차량에 대해 다음을 수행합니다:
getDescription()을 사용하여 설명 출력dynamic_cast를 사용하여 각 derived 유형으로 casting 시도Car*로의 casting이 성공하면inspectSeatbelts()호출Truck*으로의 casting이 성공하면inspectCargo()호출Motorcycle*로의 casting이 성공하면inspectHelmetStorage()호출
각 차량의 검사 사이에 blank 줄을 출력합니다. 작업이 끝나면 dynamically allocated 객체를 Clean up합니다.
예를 들어 입력이 ABC-123, TRK-456, MTR-789인 경우:
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는 실제 객체 유형이 대상 유형과 일치할 때만 유효한 포인터를 반환한다는 점에 주목하세요. 각 차량에서는 세 casting 중 하나만 성공하므로, 해당 유형에 맞는 검사 메서드를 안전하게 호출할 수 있습니다. 이것이 RTTI의 힘입니다. 런타임에 실제 유형을 확인하고 그에 따라 동작할 수 있습니다.
직접 해보기
#include <iostream>
#include <string>
#include "Vehicle.h"
#include "Vehicles.h"
using namespace std;
int main() {
// 입력 읽기
string carPlate, truckPlate, motorcyclePlate;
cin >> carPlate;
cin >> truckPlate;
cin >> motorcyclePlate;
// TODO: 좌석 4개인 Car 생성
// TODO: 용량 10.5톤인 Truck 생성
// TODO: 사이드카가 있는 Motorcycle 생성 (true)
// TODO: 세 개를 모두 Vehicle* 포인터 배열에 저장
// TODO: 배열을 순회하며 각 vehicle에 대해:
// 1. getDescription()을 사용하여 설명 출력
// 2. dynamic_cast를 사용하여 각 파생 타입으로 캐스팅 시도
// 3. Car*로 캐스팅이 성공하면 inspectSeatbelts() 호출
// 4. Truck*로 캐스팅이 성공하면 inspectCargo() 호출
// 5. If cast to Motorcycle* succeeds, call inspectHelmetStorage()
// 6. 각 vehicle 검사 사이에 빈 줄 출력
// TODO: 동적으로 할당된 객체 정리
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 C++ 컴파일러