런타임에 객체의 실제 타입을 확인하는 C++ 기능 (Run-Time Type Information).
무엇을 제공하나
#include <typeinfo>
Base *p = get_object();
// ① dynamic_cast — 안전한 다운캐스트
Derived *d = dynamic_cast<Derived*>(p);
if (d) { /* 실제로 Derived 였다 */ }
// ② typeid — 타입 정보 조회
if (typeid(*p) == typeid(Derived)) { … }
const char *name = typeid(*p).name();
어떻게 구현되나
각 다형 클래스의 vtable에 type_info 객체에 대한 포인터가 추가된다
vtable
┌──────────────────┐
│ [-1] type_info ─ ┼──► "7Derived" 등의 타입 정보
│ [0] 가상함수1 │
│ [1] 가상함수2 │
└──────────────────┘
dynamic_cast는 이 정보를 따라 상속 계층을 거슬러 올라가며 비교한다
dynamic_cast는 문자열 비교나 계층 순회를 포함해 생각보다 느리다.
깊은 상속 구조에서는 수백 사이클이 들 수 있다.
임베디드에서 끄는 이유
-fno-rtti
- ① 코드 크기 — 클래스마다 타입 이름 문자열과 계층 정보
- 수십~수백 클래스면 수 KB
- ② 실행 시간 — dynamic_cast 가 비결정적. WCET 분석 불가
- ③ 설계 문제 — dynamic_cast 가 필요하다는 것은 대개 설계가 잘못됐다는 신호
③이 본질적이다.
dynamic_cast가 필요하다면 설계를 의심한다
// ✗ 타입으로 분기 — 새 타입이 늘 때마다 이 함수를 고쳐야 한다
void handle(Sensor *s) {
if (auto t = dynamic_cast<TempSensor*>(s)) { t->readTemp(); }
else if (auto h = dynamic_cast<HumiSensor*>(s)) { h->readHumi(); }
}
// ✓ 가상 함수로 — 새 타입이 늘어도 이 코드는 그대로
void handle(Sensor *s) { s->read(); }
"타입을 물어보는 대신 객체에게 시킨다" 가 객체지향의 기본이다.
dynamic_cast의 대부분은 이렇게 없앨 수 있다.
끄면 무엇이 안 되나
dynamic_cast<Derived*>(p); // 컴파일 에러 (다형 타입에 대해)
typeid(*p); // 컴파일 에러
static_cast<Derived*>(p); // ○ 컴파일은 되지만 검사 없음
static_cast는 프로그래머가 타입을 보장해야 한다.
틀리면 미정의 동작이다.
그래도 타입 구분이 필요하다면 — 수동 태그
class Sensor {
public:
enum Type { TEMP, HUMI, PRESSURE };
virtual Type type() const = 0;
};
class TempSensor : public Sensor {
Type type() const override { return TEMP; }
};
// 사용
if (s->type() == Sensor::TEMP)
static_cast<TempSensor*>(s)->readTemp();
비용이 예측 가능하고(비교 한 번), 코드 크기도 작다.
dynamic_cast가 하는 일을 필요한 만큼만 손으로 구현한 것이다.
std::variant — 현대적 대안
using SensorData = std::variant<Temperature, Humidity, Pressure>;
std::visit([](auto&& v) { process(v); }, data);
닫힌 집합(타입이 미리 정해진 경우) 에는 상속보다 낫다. 힙을 쓰지 않고, 컴파일 시점에 모든 경우를 검사하며, RTTI 없이 동작한다. C++17 이상에서 임베디드에도 쓸 만하다.
단, 코드 팽창에 주의 — std::visit이 모든 조합에 대해
코드를 생성하므로 타입이 많으면 커진다.
예외와의 관계
-fno-rtti 는 -fno-exceptions 와 함께 쓰는 것이 일반적이다
예외 처리도 내부적으로 타입 정보를 쓴다 (catch 블록이 어느 예외 타입을 잡을지 판단해야 하므로)
둘 다 끄면 C++의 런타임 지원 코드가 거의 사라져 C에 가까운 크기가 된다.
표준 라이브러리 주의
-fno-rtti 로 컴파일했는데 RTTI를 쓰는 라이브러리를 링크하면 undefined reference to `typeinfo for ...' 오류가 난다 전체 프로젝트에서 일관되게 켜거나 꺼야 한다.