런타임 타입 정보 (RTTI)¶
RTTI(런타임 타입 정보)를 사용하지 마세요.
RTTI를 사용하면 프로그래머가 런타임에 개체의 C++ 클래스를 쿼리할 수 있습니다. 이는 typeid 또는 dynamic_cast를 사용하여 수행됩니다.
RTTI(아래 설명)에 대한 표준 대안은 문제의 클래스 계층 구조를 수정하거나 재설계해야 합니다. 때로는 그러한 수정이 실행 불가능하거나 바람직하지 않은 경우가 있으며, 특히 널리 사용되거나 성숙한 코드에서는 더욱 그렇습니다.
RTTI는 일부 단위 테스트에 유용할 수 있습니다. 예를 들어, 새로 생성된 개체에 예상되는 동적 유형이 있는지 테스트해야 하는 팩토리 클래스 테스트에 유용합니다. 또한 객체와 모의 객체 간의 관계를 관리하는 데에도 유용합니다.
RTTI는 여러 추상 객체를 다룰 때 유용합니다. 예를 들어:
bool Base::Equal(Base* other) = 0;
bool Derived::Equal(Base* other) {
Derived* that = dynamic_cast<Derived*>(other);
if (that == nullptr)
return false;
...
}
런타임에 객체 유형을 쿼리하는 것은 디자인 문제를 의미하는 경우가 많습니다. 런타임에 객체의 유형을 알아야 한다는 것은 클래스 계층 구조의 설계에 결함이 있음을 나타내는 경우가 많습니다.
RTTI를 규율 없이 사용하면 코드를 유지 관리하기가 어려워집니다. 이는 유형 기반 결정 트리나 스위치 문이 코드 전체에 흩어져 있을 수 있으며, 추가 변경 시 모두 검사해야 합니다.
RTTI는 합법적인 용도로 사용되지만 남용되기 쉬우므로 사용 시 주의해야 합니다. 단위 테스트에서는 자유롭게 사용할 수 있지만 다른 코드에서는 가능하면 사용하지 마세요. 특히 새 코드에서 RTTI를 사용하기 전에 두 번 생각하십시오. 객체의 클래스에 따라 다르게 동작하는 코드를 작성해야 하는 경우 유형을 쿼리하는 대신 다음 대안 중 하나를 고려하세요.
- 가상 메서드는 특정 하위 클래스 유형에 따라 다양한 코드 경로를 실행하는 데 선호되는 방법입니다. 이렇게 하면 작업이 개체 자체에 배치됩니다.
- 작업이 개체 외부에 속하고 대신 일부 처리 코드에 속하는 경우 방문자 디자인 패턴과 같은 이중 디스패치 솔루션을 고려하세요. 이를 통해 객체 외부의 기능이 내장 유형 시스템을 사용하여 클래스 유형을 결정할 수 있습니다.
프로그램의 논리가 기본 클래스의 특정 인스턴스가 실제로 특정 파생 클래스의 인스턴스임을 보장하는 경우에는 객체에 dynamic_cast를 자유롭게 사용할 수 있습니다. 일반적으로 absl::down_cast가 훨씬 더 나은 대안을 제공합니다.
유형을 기반으로 한 결정 트리는 코드가 잘못된 방향으로 가고 있다는 강력한 표시입니다.
if (typeid(*data) == typeid(D1)) {
...
} else if (typeid(*data) == typeid(D2)) {
...
} else if (typeid(*data) == typeid(D3)) {
...
이와 같은 코드는 클래스 계층 구조에 추가 하위 클래스가 추가되면 일반적으로 중단됩니다. 게다가 하위 클래스의 속성이 변경되면 영향을 받는 모든 코드 세그먼트를 찾아서 수정하기가 어렵습니다.
RTTI와 유사한 해결 방법을 직접 구현하지 마세요. RTTI에 대한 주장은 유형 태그가 있는 클래스 계층 구조와 같은 해결 방법에도 똑같이 적용됩니다. 더욱이 해결 방법은 실제 의도를 위장합니다.
이해하기 쉽게 설명하기¶
핵심: RTTI를 쓰지 말 것¶
RTTI는 typeid나 dynamic_cast로 런타임에 객체의 실제 클래스를 알아내는 기능입니다. 런타임에 타입을 물어봐야 한다는 것은 대개 클래스 계층 설계에 결함이 있다는 신호입니다.
대안¶
객체의 클래스에 따라 다르게 동작해야 한다면, 타입을 묻지 말고:
- 가상 메서드: 동작을 객체 자신에게 넣어, 하위 클래스마다 다른 경로를 타게 합니다. (가장 선호)
- 이중 디스패치(방문자 패턴): 동작이 객체 외부의 처리 코드에 속할 때.
// 나쁨: 타입 기반 결정 트리 — 하위 클래스가 늘면 깨짐
if (typeid(*data) == typeid(D1)) { ... }
else if (typeid(*data) == typeid(D2)) { ... }
허용되는 경우¶
- 단위 테스트: 팩토리가 만든 객체의 동적 타입 확인 등에는 자유롭게 사용.
- 프로그램 로직이 특정 파생 타입임을 보장할 때의 다운캐스트: 보통
dynamic_cast보다absl::down_cast가 더 낫습니다.
직접 구현도 금지¶
타입 태그를 단 클래스 계층 같은 "수제 RTTI"도 만들지 마세요. RTTI를 막는 이유가 그대로 적용되며, 의도만 가려집니다.