[기술 질문] #15_RTTI, 런타임 타입 정보
C++의 RTTI에 대해 알아보겠습니다.
Overview
- 개념
- typeid
- 캐스팅
#0. 개념
1. RTTI
- [정의] : C++의 RTTI는 "Run-Time Type Information"의 약자로, 프로그램 실행 중 객체의 동적 타입 정보를 식별하고 검색하는 기능을 제공합니다.
- [특징] : 다형성 제공 : C++의 가상 함수는 다형성을 구현하는 중요한 매커니즘 중 하나입니다. 파생 클래스는 기본 클래스의 가상 함수를 오버라이딩 할 수 있으며, 동일한 시그니처의 함수를 하나의 데이터 타입에 귀속되지 않고, 여러 유형으로 표현될 수 있도록 해줍니다. 가상 함수는 동적 바인딩을 통해 객체의 동적 타입에 맞는 적절한 함수 호출을 보장합니다. 만약, 기본 클래스의 포인터가 파생 클래스 유형의 객체를 가리킬 경우, 가상 함수 호출 시 동적 바인딩이 발생해 파생 클래스의 가상 함수가 호출됩니다. 이처럼, 객체의 동적 타입 정보를 식별하기 위해 우리는 RTTI를 활용합니다.
- [종류] : (1) typeid : typeid 연산자는 객체의 현재 타입 정보를 담고 있는 type_info 객체를 반환합니다. type_info 객체는 클래스 이름과 관련된 정보들을 담고 있으며, 클래스의 가상 테이블 등의 정보를 확인할 수 있습니다. (2) dynamic_cast : dynamic_cast 연산자는 객체의 포인터나 참조를 안전하게 다운캐스팅 해줍니다. 일반적으로, 파생 클래스 객체를 가리키는 기본 클래스 유형의 포인터를 dynamic_cast 연산자를 통해 파생 클래스 유형으로 다운캐스팅하는데 활용됩니다.
- [주의할 점] : (1) 런 타임 오버헤드 : RTTI 사용 시 주의할 점은 성능 저하입니다. 런 타임에 객체의 동적 타입 정보를 확인하는데 추가적인 시간이 필요하기 때문에 적절한 사용 처에만 활용하는 것이 좋아보입니다. (2) dynamic_cast : 기본 클래스 객체를 가리키는 기본 클래스 유형의 포인터를 계층 구조의 하위 레벨의 클래스 유형으로 dynamic_cast 하면 예외가 반환되어, 주의해야합니다.
2. 동적 바인딩
동적 바인딩은 실행 시간에 객체의 실제 타입을 확인하고 해당 타입에 맞는 메서드를 호출하는 메커니즘입니다. C++은 다형성을 지원하기 위해 virtual 키워들르 통해 가상 함수를 선언할 수 있으며, 이를 하위 클래스에서 오버라이딩 할 수 있습니다. 이때, 컴파일러는 함수를 호출한 객체 유형의 vtable을 통해 해당 함수와 관련된 가상 포인터를 찾아 실제 타입에 맞는 가상 함수를 호출합니다.
#1. typeid
1. typeid
typeid(타입)
typeid(객체)
- [개념] : C++의 typeid 연산자는 객체의 현재 타입 정보를 type_info 객체에 담아 반환합니다. type_info 객체를 통해 클래스 이름과 관련된 정보를 얻을 수 있으며, 클래스의 가상 테이블 등의 정보를 담고 있습니다.
- [특징] : typeid 연산자는 인자로 타입 이름을 받거나, 객체를 전달 받아 타입 혹은 객체의 타입 정보를 std::type_info 클래스 객체로 반환합니다.
- [주의할 점] : typeid 연산자를 사용하기 위해 반드시 해당 클래스가 가상 함수를 포함해야 합니다. 왜냐하면, 객체의 동적 타입 정보를 가상 함수 테이블(vtable)을 통해 확인하기 때문입니다. 가상 함수를 포함하지 않는 클래스는 컴파일러가 가상 함수 테이블을 생성하지 않습니다. 가상 멤버 함수가 없는 클래스의 typeid 연산은 정적 타입 정보(컴파일 시점에 결정되는 객체의 타입 정보)를 반환하기 때문에 이는 본래의 의도와 벗어난 결과입니다.
2. 예제 코드1
#include <iostream>
#include <typeinfo>
class Base {};
class Derived : public Base {};
int main() {
// #1. 타입 이름 전달
std::cout << typeid(int).name() << std::endl;
// #2. 객체 전달
Base* b = new Derived;
if (typeid(*b) == typeid(Derived)) {
std::cout << "b는 Derived 클래스의 객체입니다." << std::endl;
}
return 0;
}
#2. 캐스팅
1. 캐스팅
- [정의] : 캐스팅은 자료형 간의 형 변환 혹은 포인터 간의 형 변환 기능을 제공합니다.
- [종류] : static_cast, dynamic_cast, reinterpret, 그리고 const_cast
2. static_cast
int a = 10;
double b = static_cast<double>(a); // int 타입의 변수 a를 double 타입으로 변환
Details
- [정의] : static_cast는 컴파일 시점에 수행되는 형 변환 연산자 중 하나로, 기본 데이터 타입 간의 명시적인 타입 변환 및 다양한 타입 간의 상속 관계에서 형 변환을 수행합니다.
- [주의할 점] : static_cast는 사용이 비교적 쉽지만, 런 타임 검사를 진행하지 않아 위험할 수 있습니다. 따라서, 확실한 순간에만 활용해야 합니다.
3. dynamic_cast
class Base {
virtual void foo() {}
};
class Derived : public Base {
void foo() override {}
};
Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base); // Up-casting 후 다운캐스팅
if (derived != nullptr) {
// 캐스팅이 성공한 경우
derived->foo();
}
Details
- [정의] : dynamic_cast는 런 타임에 안전한 형 변환을 수행하는데 사용됩니다. 주로, 객체의 동적 타입 정보를 확인하고 계층 구조의 다운캐스팅을 수행합니다.
- [특징] : dynamic_cast는 RTTI를 통해 런 타임에 객체의 실제 형식에 대해 검사하고 안전한 다운 캐스팅을 제공합니다.
- [주의할 점] : (1) 런 타임 오버헤드 : 런 타임에 객체의 동적 타입 정보를 확인하는 작업은 런 타임 오버헤드를 발생시킬 수 있습니다. (2) 클래스의 가상 함수 테이블(vtable) : RTTI를 활용하는 만큼, 가상 함수를 포함하는 클래스에게만 한정적으로 다운캐스팅을 수행합니다. 객체의 동적 타입 정보를 확인하기 위해 "가상 함수 테이블(vtable)"이 필요하기 때문이죠!
4. reinterpret_cast
#include <iostream>
using namespace std;
int main() {
int n = 10;
void* ptr = &n;
// void* 포인터를 int* 포인터로 reinterpret_cast
int* intPtr = reinterpret_cast<int*>(ptr);
cout << "n = " << n << endl;
cout << "*intPtr = " << *intPtr << endl;
return 0;
}
Details
- reinterpret_cast는 다른 포인터 타임끼리 또는 포인터와 정수형 타입 간의 변환을 수행할 때 사용합니다.
- reinterpret_cast는 포인터의 비트 패턴을 변경하여 다른 타입으로 캐스팅합니다. C++의 포인터 캐스팅 연산자 중 가장 위험도가 높은 연산자입니다. 따라서, reinterpret_cast 사용 시 타입 간의 관계와 메모리 레이아웃에 대한 충분한 이해가 필요합니다.
- 정리하자면, reinterpret_cast는 포인터와 관련된 저수준 작업에서만 사용하고, 상위 수준의 코드에서는 사용을 최소화해야 합니다.
5. const_cast
const int value = 10; // const 변수 선언
int* ptr = const_cast<int*>(&value); // const_cast를 사용하여 포인터의 상수성 제거
*ptr = 20; // 값을 변경
Details
- const_cast는 포인터나 참조의 상수성을 제거하는 캐스트 연산자입니다.
- const 키워드를 사용해 변수, 함수, 매개 변수, 그리고 클래스 등을 변경할 수 없도록 상수성을 부여합니다. 이때, const를 사용해 상수성을 부여한 변수나 클래스등은 본래의 의도와 달리 const_cast를 통해 상수성 제거와 값에 대한 변경이 이루어지면 예기지 않은 문제가 발생할 수 있기 때문에, 적절히 사용해야 합니다.
'언어 > 기술 질문' 카테고리의 다른 글
[기술 질문]#18_알고리즘 복잡도 (0) | 2023.03.31 |
---|---|
[기술 질문]#17_Lambda (0) | 2023.03.27 |
[기술 질문]#15_가상 함수 (0) | 2023.03.20 |
[기술 질문]#14_템플릿, Template (0) | 2023.03.12 |
[기술 질문]#13_6가지 디폴트 멤버 메서드 (0) | 2023.03.05 |