RTTI는 'runtime type identification의 약자로, 실행 시간 데이터형 정보라는 뜻이다. 실행 도중에 데이터형을 결정하는 표준 방법을 제공한다는데, 그 종류 하나하나 배워보면 뭔 말인지 알 것이다.
기초 클래스와 파생 클래스 간에 형 변환인 업캐스팅 다운캐스팅을 배웠다. 업캐스팅은 되고 다운캐스팅은 안됐다. 근데 좀 복잡한 형변환이 있을 수도 있다. 예를 들어 다음과 같다.
class Grand {}
class Superb : public Grand {}
class Magnificent : public Superb {}
Grand * pg = new Grand;
Grand * ps = new Superb;
Grand * pm = new Magnificent;
Magnificent * p1 = (Maginificent*) pm; // #1
Magnificent * p2 = (Maginificent*) pg; // #2
Superb * p3 = (Maginificent*) pm; // #3
#1 ~ #3 중에 무엇이 되고 무엇이 안 되는가? 판별하기 너무 어렵다. 그래도 알아보자면, #1, #3은 되고 #2는 안 된다. 왜? 일단 #1은, Magnificent형 포인터가 Magnificent형 객체를 지시하도록 설정하기 때문에 안전하다. #2는 Magnificent형 포인터가 Grand형 객체를 지시해서 안된다. 왜? 파생 클래스 포인터가 기초 클래스 포인터를 받고 있기 때문이다. 이는 다운캐스팅이다. #3은 Superb 포인터가 파생 클래스인 Magnificent 객체를 지시해서 안전하다.
이 예시에서는 잘 알았는데, 실제에서는 잘 모를 수도 있지 않은가? 일일이 보는 것도 좀 시간 걸리고,, 그래서 dynamic_cast 연산자를 사용한다. 이런 식으로 쓴다.
Superb* pm = dynamic_cast<Superb*>(pg);
이 뜻은, pg 포인터가 Superb*형으로 안전하게 변환될 수 있는지를 물어본다. 가능하면 객체의 주소를 리턴하고, 안되면 널 포인터인 0을 리턴한다. 이를 아주 완벽하게 알아볼 수 있는 코드가 있다.
class Grand
{
private:
int hold;
public:
Grand(int h = 0)
: hold(h)
{}
virtual void Speak() const
{
cout << "Grand::Speak()" << endl;
}
virtual int Value() const
{
return hold;
}
};
class Superb : public Grand
{
public:
Superb(int h = 0)
:Grand(h)
{
}
void Speak() const
{
cout << "Superb::Speak()" << endl;
}
virtual void Say() const
{
cout << "Superb::Say()" << endl;
}
};
class Magnificent : public Superb
{
private:
char ch;
public:
Magnificent(int h = 0, char c = 'A')
:Superb(h), ch(c)
{
}
void Speak() const
{
cout << "Magnificent::Speak()" << endl;
}
void Say() const
{
cout << "Magnificent::Say()" << endl;
}
};
Grand* GetOne()
{
Grand* p;
switch (rand() % 3)
{
case 0:
{
p = new Grand(rand() % 100);
break;
}
case 1:
{
p = new Superb(rand() % 100);
break;
}
case 2:
{
p = new Magnificent(rand() % 100, 'A' + rand() % 26);
break;
}
return p;
}
}
int main()
{
srand(time(0));
Grand* pg;
Superb* ps;
Magnificent* pm;
for (int i = 0; i < 5; i++)
{
cout << "i : " << i << endl;
pg = GetOne();
pg->Speak();
if (ps = dynamic_cast<Superb*>(pg))
ps->Say();
if (pm = dynamic_cast<Magnificent*>(pg))
pm->Say();
}
}
형 변환이 가능하면 각각 Say 함수가 호출될 것이다.
실행 결과.
만약 pg가 Grand*형이면 Say 함수가 호출이 되지 않았다. 이 뜻은, 형변환이 불가능해, dynamic_cast가 0을 리턴해서 if문 안으로 들어가지 않았다는 말이다. pg가 Superb*형이면 Superb*형으로만 변환이 가능하다. Magnificent*형은 제일 막내(?) 파생 클래스이므로, 모두 가능하다.