<개인공부>/[C++]

[C++] RTTI, 타입 정보 얻기

BlockDMask 2021. 5. 19. 00:30
반응형

안녕하세요. BlockDMask입니다.
오늘은 RTTI라는 것에 대해서 알아보려고 하는데요, 간단히 말해서 실행시간에 타입의 정보를 얻을 때 사용하는 것입니다.

즉 타입을 알아올 때 사용.

<목차>
1. C++ RTTI란?
2. C++ RTTI 사용 방법

 

 

1. C++ RTTI 설명


1-1) C++ RTTI와 typeid

RTTI는 Run Time Type information이라 하며,
프로그램 실행 중에 실시간으로 데이터의 타입을 얻어올 때 사용하는 방법입니다.

RTTI 기술을 이용해서 데이터 타입을 얻어올 수가 있는데요. 이때 사용하는 것이 typeid 연산자입니다.
typeid 연산자는 <typeinfo> 헤더에 존재합니다.

typeid(변수)
typeid(데이터 타입)

이런 식으로 데이터 타입의 정보를 얻어올 수 있습니다.
typeid 연산자의 결과로는 const std::type_info& 로 반환됩니다.

std::type_info는 타입의 정보를 가지고 있는 클래스입니다. 이거는 typeid의 반환형으로만 사용이 가능합니다.
name()이라는 멤버 함수를 통해서 타입의 이름을 얻을 수 있습니다.

 

1-2) typeid 간단 예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
#include<typeinfo>
using namespace std;
 
int main() {
    auto num = 10;
    auto str = "BlockDMask";
 
    const std::type_info& info1 = typeid(num);
    const std::type_info& info2 = typeid(str);
    
    cout << info1.name() << endl;
    cout << info2.name() << endl;
 
    return 0;
}
cs

이런 식으로. name() 멤버 함수로 접근을 하면 데이터의 현재 타입을 반환해줍니다.

 

 

2. C++ RTTI 기술을 사용한 예제


2-1) RTTI, typeid를 통해서 타입을 조사하는 방법

타입을 조사하는 방법
이렇게 typeid를 통해서 런타임 중에 데이터의 타입을 알아낼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
#include<typeinfo>
 
using namespace std;
 
int main() {
    auto num = 200;
    
    const std::type_info& info1 = typeid(num);
    const std::type_info& info2 = typeid(int);
    
    // type_info 에 담아서 사용
    if (info1.name() == info1.name())
    {
        cout << "num == int" << endl;
    }
 
 
    // typeid 바로 사용
    if (typeid(num) == typeid(int))
    {
        cout << "num == int" << endl;
    }
 
    return 0;
}
cs

두 경우 모두 "num == int" 이 결과가 나옵니다.
더 간단한 typeid(num) == typeid(int)를 사용하는 게 편하겠죠

 

 

2-2) RTTI, typeid와 클래스 상속 예제

실제 RTTI를 우리가 알게 모르게 쓰고 있었던 게 있습니다.
바로 dynamic_cast인데요. 이 dynamic_cast에서 프로그램 실행 중에 다운 캐스팅을 할 때 RTTI 기술을 이용해서 사용한다고 하네요.
원래 RTTI 목적이 프로그램 실행 중에 실제 타입을 알아오는데 쓰이려고 만들어진 것이기 때문에 dynamic_cast는 아주 적절하게 해당 기술을 이용했다고 생각이 듭니다.

Parent* pParent = new Child();라는 클래스가 있다고 할 때 부모 클래스(Parent), 자식 클래스 (Child)라 하겠습니다.

이럴 때 실제 pParent가 가리키고 있는 객체가 Child라는 것을 알 수가 없습니다. 그렇기 때문에 RTTI 기술을 사용해서 실제 객체가 무엇인지 알아서 판단을 해줄 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include<iostream>
#include<typeinfo>
using namespace std;
 
class Parent {
public:
    Parent() {}
    virtual ~Parent() {}
};
 
class Child : public Parent {
public:
    Child() {}
};
 
 
int main() 
{
    // 부모 클래스 선언
    Parent* pParent = new Parent();
    cout << typeid(pParent).name() << endl;
 
    // 다운 캐스팅
    Child* pChild = dynamic_cast<Child*>(pParent);
    cout << typeid(pChild).name() << endl;
 
    return 0;
}
cs

이렇게 다운 캐스팅이 정상적으로 되는 것을 볼 수 있습니다. 다운 캐스팅이 되는 이유는 RTTI 기술 덕분입니다.

 

좀 더 추가적으로 이야기하자면 위 예제를 보면 부모 클래스에 가상 함수가 있습니다.
만약 가상 함수가 없다면 어떻게 될까요? (위 예제에서 virtual ~ 소멸자를 지우고 프로그램을 실행시켜 보세요.)

그렇게 되면 실제 dynamic_cast가 되지 않는 것을 볼 수 있습니다.
그 이유는 RTTI 정보는 가상 함수가 사용된 클래스에 한해서만 정보가 유지됩니다.
그렇기 때문에 부모 클래스에서 virtual이 삭제되면 제대로 동작하지 않게 됩니다.

그런데 어차피
우리가 보통 부모 클래스를 선언하고 자식 클래스를 만들게 되면, "소멸자는 가상 함수로 만들어야 한다"라는 말을 듣지 않습니까? 그렇습니다. 정상적으로 "특정 클래스를 상속받는다, 상속한다" 하면 소멸자에 virtual 키워드가 붙어있어야'만'합니다. 그렇기 때문에 대부분의 상황에서는 잘 동작할 것입니다.

 

이상으로 C++ RTTI에 대해서 알아보았습니다. 감사합니다.

반응형