본문 바로가기
연구소👨‍💻/CS연구소

[C++] RTTI

by 신그자체김상범 2024. 4. 26.

한 개의 개념을 이해하기 위해선 절대적인 정의가 중요한게 아니라 상대적인 관계 속 위치가 중요하다.

RTTI의 정의

아예 RTTI에 대해서 아예 몰랐을 때 이렇게 정의해봤다

RTTI는 실행시간에 일어나는 타입 결정자다.

G쌤은 이 말에 대해서 틀렸다고 한다

RTTI(Run-Time Type Information)는 "타입 결정자"라고 부르기보다는 "타입 식별자"나 "타입 정보 제공자"에 더 가깝습니다. "타입 결정자"라는 표현은 변수나 객체의 타입을 선언하거나 초기화할 때 사용되는 개념과 혼동될 수 있습니다. 예를 들어, 프로그램 코드에서 변수를 선언할 때 사용하는 타입(int, float 등)은 그 변수의 타입을 "결정"합니다.

반면, RTTI는 프로그램이 실행 중일 때 객체의 타입 정보를 조회하거나, 객체 간의 타입 호환성을 검사하는 기능을 제공합니다. 이는 이미 선언되어 있는 객체의 타입을 확인하거나 안전한 타입 변환을 수행하는 데 사용됩니다. 따라서 RTTI는 타입을 결정하는 것이 아니라, 실행 시점에 이미 결정된 타입 정보를 사용하여 추가적인 타입 관련 작업을 수행하는 역할을 합니다.


여기서 선언, 초기화라는 말이 눈에 띈다. 우선 이 점을 간단하게 생각해봤다.

int a = 3;

변수를 만드는 행위는 선언과 초기화 두개로 나눌 수 있다.
선언은 간단히 자료형에 대한 메모리를 얻고 변수에 매핑하는 행위
초기화는 매핑된 메모리에 값을 정해진 수로 바꾸는 행위다.

그러니까 한마디로 RTTI는 위의 두 행위에 맞지 않다는 것


종류

그런데 이상한 점이 있다.
종류상 RTTI의 메커니즘은 typeid, dynamic_cast 두개가 메인이라고 하는데.
dynamic_cast는 실행시간 동안 변수의 자료형을 바꾼다고 나와있다.
그리고 C++은 내가 사용하는 선에선 객체의 타입을 결정할 필요가 없어보인다. 왜냐면 어떤 객체든 선언하고 초기화 할라면 변수의 자료형을 명시해 줘야하니까.
이렇게 생각하니까 의문이 생긴다.

여기에 G쌤은

C++에서 RTTI (Run-Time Type Information)를 사용하는 이유와 `typeid` 및 `dynamic_cast` 연산자의 역할을 이해하려면, 다형성과 상속 구조 내에서의 객체 타입 관리를 고려해야 합니다. C++에서는 객체의 타입이 컴파일 타임에 명시되지만, 다형성을 사용할 때 실행 시간에 객체의 실제 타입을 확인하거나 안전한 타입 변환을 수행할 필요가 있습니다.

왜 `typeid`가 필요한가?
`typeid` 연산자는 객체의 정확한 타입 정보를 제공합니다. 다형성을 활용하는 상황에서, 특히 클래스 계층에서 기반 클래스의 포인터나 참조를 통해 파생 클래스의 객체를 다룰 때, `typeid`를 사용하여 객체의 실제 타입을 알아내고 이에 따라 특정한 행동을 결정할 수 있습니다. 예를 들어, 여러 파생 클래스가 있을 때 어떤 클래스의 인스턴스인지 확인하고 그에 맞는 처리를 할 수 있습니다.

왜 `dynamic_cast`가 필요한가?
`dynamic_cast`는 실행 시간에 타입의 안전한 캐스팅을 보장합니다. 주로 다형성을 사용하는 클래스 계층에서 사용되며, 기반 클래스 포인터나 참조를 안전하게 파생 클래스 타입으로 변환할 수 있습니다. 이 변환은 실행 시간에 객체의 실제 타입을 체크하여, 타입이 호환되지 않으면 캐스팅이 실패합니다 (예: 포인터인 경우 `nullptr`을 반환). 이는 프로그램의 안정성을 높이며, 잘못된 타입 변환으로 인한 런타임 에러를 방지합니다.

RTTI는 왜 타입 식별자에 가까운가?
RTTI는 주로 이미 선언된 객체의 타입을 실행 시간에 식별하고 확인하는데 사용됩니다. 따라서 RTTI는 타입을 결정하는 것이 아니라, 이미 결정된 타입을 확인하거나 타입 정보를 바탕으로 안전한 연산을 수행할 수 있도록 돕습니다. 이는 RTTI가 "타입 식별자"로 분류되는 이유입니다.

요약하자면, RTTI는 C++에서 다형성을 안전하게 활용하고 객체의 타입에 따른 조건부 처리를 가능하게 하는 중요한 메커니즘입니다. `typeid`와 `dynamic_cast`는 이런 상황에서 필수적인 도구로, 타입 정보를 기반으로 코드의 동작을 조절하거나 안전하게 타입을 변환하는 데 사용됩니다.


라고 대답했다. 주된 이유는 바로 다형성
다형성 자체에 대한 개념이 희끗희끗하지만 일단 객체지향의 원칙중 하나라는 것은 기억하고 있다.
이부분을 간단하게만 얘기해보면

다형성은 하나의 클래스, 메소드가 다양한 방법으로 동작하는 것.
애초에 문제풀이 정도로만 C++을 쓰니까 이부분을 잘 모른다.
다형성의 가장 큰 물리적 구현은 오바로딩과 오버라이딩인데,
그래도 오버로딩은 많이 쓰다시피 같은 이름의 메소드를 입력을 다르게 해서 여러개 두는 것이고, 오버라이딩은 부모의 메소드를 자식이 덮어 씌우는 것이다. 일단은 이정도로 정리하자.




비교군

실행시간이란말은 비교적 생소하다. 정확히 실행시간이 뭘 의미하는지는 비교군들을 비교해봐야 알 수 있다. 실행시간에 가장 반대되는 개념은 컴파일 시간이다. 즉 코드가 실행되기 전에 일어나는 일을 말할 것, 이 말을 처음 들었을 때 변수를 선언 할 때 자료형을 같이 선언하지 않는 파이썬에서는 항상 실행시간에 타입이 결정될 것이라는 생각에는 쉽게 닿았으나 그런데도 정확히 실행시간이 뭐고 컴파일 시간이 뭔지에 대해서는 아직도 공부가 필요하다.

 

 

간단하게 정리하면 컴파일은 프로그램의 소스코드가 기계코드로 바뀌는 과정을 말한다. 이때 컴파일러가 변수의 타입, 함수의 타입 정보를 결정하고 확인한다.

반대로 RTTI의 경우에는 다형성에 의해서 실행시간동안 객체의 타입을 바꾸거나 확인해야하는 일이 생기기 때문에 실행시간에 타입 식별이 일어난다고 할 수 있다. 

단순히 정적 캐스팅 이외에도 void* 이나 any같은 어떤 타입도 담을 수 있는 컨테이너도 있다. 다만 이것들역시 런타임시 일어나는 타입결정자 라기보단, 더 유연하게 타입을 결정할 수 있는 놈 정도라고 할 수 있다.

 

이런 동적 타이핑의 예와 RTTI의 가장큰 차이점은 RTTI가 다형성을 위해서 타입을 확인하고 다운캐스팅 하는데 활용되는 반면 any 같은 동적 타이핑은 나중에 데이터의 형태를 결정하기 타입선언을 유보 하는 것이다.