Computer Science/C & C++

[C++] RTTI | 실행 시간 데이터형 정보

lww7438 2020. 7. 31. 20:40

RTTI (Runtime Type Identification)

 

실행 시간 데이터형 정보

 

 

 

- 많은 클래스 라이브러리들이 실행 시간 중 데이터형을 결정하는 방법을 제각기로 제공하기 때문에
  이들이 서로 호환되도록 하기 위해서는 RTTI를 위한 언어 표준이 필요하다.

 


C++에서의 RTTI 요소

 

1. dynamic_cast 연산자

dynamic_cast<Type *>(pt)
// 포인터 pt를 Type * 형으로 변환하여 리턴한다.
// (*pt)가 Type형이거나, Type형으로부터 직/간접적으로 파생된 객체이면, pt를 Type *형 포인터로 변환을 허용한다.
// 변환할 수 없다면, 0을 리턴한다.


if (pt1 = dynamic_cast<exClass *>(pt2))
    pt1->exMethod();
// pt2 포인터를 exClass * 형으로 변환 가능하다면,
// 변환하여 pt1에 대입하고,
// pt1객체의 exMethod() 메서드를 실행한다.


- 기초 클래스형을 지시하는 포인터로부터 파생 클래스형을 지시하는 포인터를 생성하며,
  이것이 불가능한 경우엔 0을 리턴한다.

- 포인터가 지시하는 객체형을 알려주지는 않는 대신,
  해당 객체의 주소를 특정형의 포인터에 대입 가능한지에 대한 여부를 알려 준다.

 

 


* 참조형에 대한 dynamic_cast 연산자
- NULL값에 해당되는 참조값은 존재하지 않기 때문에,
  참조형에 대한 타입 캐스팅이 실패하면 bad_cast형 예외를 발생시킨다.

- bad_cast형 예외는 exception 클래스로부터 파생되었으며, typeinfo 헤더파일에 선언되어 있다.

#include <typeinfo> // for bad_cast

...

try {
	Superb & rs = dynamic_cast<Superb &>(rg);
	...
}
catch(bad_cast &){
	...
};

 

 


2. typeid 연산자
- 객체의 정확한 데이터형을 나타내는 값을 리턴한다.
- typeid연산자는 type_info 객체에 대한 참조를 리턴한다.
- typeid 연산자에 요구되는 매개변수는 "클래스 이름" 또는 "객체로 평가되는 식"이다.

typeid(className)
// 또는
typeid(object)


typeid 연산자의 매개변수가 NULL이면, bad_typeid 예외를 발생시킨다.
- bad_typeidexception 클래스로부터 파생되었으며, typeinfo 헤더 파일에 선언되어 있다.

* typeid(*pt).name() 메서드
- 포인터 pt가 가리키고 있는 객체의 클래스 이름을 문자열로 리턴한다.

* type_info
- 어떤 특별한 데이터형에 대한 정보를 저장한 구조체이다.
- type_info 클래스는 typeinfo 헤더파일 (typeinfo.h)에 선언되어 있다.
- 데이터형에 대한 비교를 위해 == 연산자와 != 연산자가 이에 맞춰서 오버로딩 되어있다.

typeid(Magnificent) == typeid(*pg)
// 비교 연산자가 오버로딩 되어있기에 가능한 식


※ RTTI는 Virtual Function을 가진 클래스에만 사용할 수 있다.
- 파생 객체들의 주소를 기초 클래스 포인터에 대입하는 작업은
  상속 계층구조를 가진 클래스의 가상 함수에서만 필요하기 때문이다.

 


Usage RTTI (RTTI 사용예시)

// rtti1.cpp -- using the RTTI dynamic_cast operator
#include <iostream>
#include <cstdlib>
#include <ctime>
using std::cout;

class Grand {
private:
	int hold;
public:
	Grand(int h = 0) : hold(h) {}
	virtual void Speak() const { cout << "I am a grand class!\n";}
	virtual int Value() const { return hold; }
};

class Superb : public Grand {
public:
	Superb(int h = 0) : Grand(h) {}
	void Speak() const {cout << "I am a superb class!!\n"; }
	virtual void Say() const { cout << "I hold the superb value of " << Value() << "!\n";}
};

class Magnificent : public Superb {
private:
	char ch;
public:
	Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}
	void Speak() const {cout << "I am a magnificent class!!!\n";}
	void Say() const {cout << "I hold the character " << ch << " and the integer " << Value() << "!\n"; }
};

Grand * GetOne() { // generate one of three kinds of objects randomly
	Grand * p;
    
	switch( std::rand() % 3) {
		case 0: p = new Grand(std::rand() % 100);
			break;
		case 1: p = new Superb(std::rand() % 100);
			break;
		case 2: p = new Magnificent(std::rand() % 100, 'A' + std::rand() % 26);
			break;
	}

	return p;
}

int main() {
	std::srand(std::time(0));
	Grand * pg;
	Superb * ps;
    
	for (int i = 0; i < 5; i++) {
		pg = GetOne();
		pg->Speak();
		if( ps = dynamic_cast<Superb *>(pg))
			ps->Say();
	}
    
	return 0;
}


// main() 함수의 내용을 typeid 만으로 구현하는 방법
Grand * pg;
Superb * ps;
Magnificent * pm;

for (int i = 0; i < 5; i++) {
		pg = GetOne();
		if (typeid(Magnificent) == typeid(*pg)) {
			pm = (Magnificent *) pg;
			pm->Speak();
			pm->Say();
		}
		else if (typeid(Superb) == typeid(*pg)) {
			ps = (Superb *) pg;
			ps->Speak();
			ps->Say();
		}
		else
			pg->Speak();
}