14. 프렌드, 예외, 기타사항(5) - RTTI(dynamic_cast)

WanJu Kim·2022년 12월 18일
0

C++

목록 보기
63/81

RTTI는 'runtime type identification의 약자로, 실행 시간 데이터형 정보라는 뜻이다. 실행 도중에 데이터형을 결정하는 표준 방법을 제공한다는데, 그 종류 하나하나 배워보면 뭔 말인지 알 것이다.

dynamic_cast

기초 클래스와 파생 클래스 간에 형 변환인 업캐스팅 다운캐스팅을 배웠다. 업캐스팅은 되고 다운캐스팅은 안됐다. 근데 좀 복잡한 형변환이 있을 수도 있다. 예를 들어 다음과 같다.

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*형은 제일 막내(?) 파생 클래스이므로, 모두 가능하다.

profile
Question, Think, Select

0개의 댓글