형변환과 RTTI


다음 내용들을 다룹니다.

  • Upcasting과 Downcasting
  • dynamic_cast
  • RTTI (Run-Time Type Information)



Upcasting, Downcasting


업캐스팅(Upcasting)

업캐스팅은 파생 클래스(derived class)의 객체를 기본 클래스(base class)의 포인터나 참조로 사용하는 것입니다.
파생 클래스 객체를 기본 클래스 포인터에 할당하면, 파생 클래스의 고유 멤버는 사용할 수 없고, 기본 클래스에 정의된 멤버만 접근할 수 있습니다.

특징 :

  • 안전성 : 업캐스팅은 항상 안전합니다. (자식 클래스는 부모 클래스의 모든 멤버를 포함하므로)
  • 암시적 변환 : 별도의 명시적 캐스트 없이 자동으로 변환됩니다.
  • 다형성 활용 : 여러 파생 클래스를 기본 클래스 타입으로 관리할 수 있어, 하나의 인터페이스로 다양한 객체를 제어할 수 있습니다.
  • 가상 소멸자 (Virtual Destructor) : 업캐스팅한 객체를 기본 클래스 포인터로 delete할 때, 기본 클래스의 소멸자가 virtual이어야 파생 클래스의 소멸자가 올바르게 호출됩니다. 그렇지 않으면 메모리 누수나 예기치 않은 동작이 발생할 수 있습니다.
class Base
{
public:
	virtual ~Base() {}
	virtual void show() { std::cout << "Base" << std::endl; }
};

class Derived : public Base
{
public:
	void show() override { std::cout << "Derived" << std::endl; }
	void derivedFunc() { std::cout << "Derived function" << std::endl; }
};

int main()
{
	Derived d;
	Base* pBase = &d;  // 업캐스팅: Derived -> Base (암시적 변환)
	pBase->show();     // 다형성에 의해 Derived의 show() 호출
	// pBase->derivedFunc(); // 오류: Base에는 derivedFunc가 없음
	return 0;
}


다운캐스팅(Downcasting)

다운캐스팅은 기본 클래스의 포인터나 참조를 다시 파생 클래스의 포인터나 참조로 변환하는 것입니다.
다운캐스팅을 통해 파생 클래스의 고유 멤버(예, derivedFunc())에 접근할 수 있습니다.

특징 :

  • 명시적 변환 필요 : 다운캐스팅은 암시적으로 이루어지지 않으므로, 명시적으로 캐스팅을 해주어야 합니다.
  • 안정성 문제 : 실제 객체가 변환하려는 파생 클래스가 아니라면, 잘못된 캐스팅이 발생할 수 있습니다. 예를 들어, C 스타일 캐스팅의 경우에는 컴파일러가 강제로 타입 변환을 수행하므로 정의되지 않은 동작을 초래할 수 있습니다.
    • dynamic_cast : 다형성이 적용된 (즉, 최소한 하나 이상의 virtual 함수가 있는) 기본 클래스의 경우, dynamic_cast를 사용하면 RTTI를 사용해서 런타임에 타입 체크가 이루어져 안전하게 다운캐스팅할 수 있습니다. 만약 실제 객체가 변환하려는 파생 클래스가 아니라면, 런타임 에러(nullptr 반환 또는 bad_cast 예외)가 발생합니다.
class Base
{
public:
	virtual ~Base() {}
};

class Derived : public Base
{
public:
	void derivedFunc() { std::cout << "Derived function" << std::endl; }
};

int main()
{
	Base* pBase = new Derived;  // 업캐스팅은 안전
	// Derived* pDerived = (Derived*)pBase; // C 스타일 캐스팅: 위험할 수 있음

	// 안전한 다운캐스팅: dynamic_cast 사용
	Derived* pDerived = dynamic_cast<Derived*>(pBase);
	if (pDerived)
	{
		pDerived->derivedFunc();  // Derived 클래스 고유 함수 호출 가능
	}
	else
	{
		std::cout << "다운캐스팅 실패" << std::endl;
	}
	return 0;
}


danamic_cast 시에 만약 파생 클래스가 아니라면,

  • 포인터 변환의 경우: 잘못된 캐스팅으로 인해 nullptr을 반환합니다.
  • 참조 변환의 경우: std::bad_cast 예외를 발생시킵니다.
class Base
{
public:
	virtual ~Base() {}
};

class Derived : public Base
{
};

int main()
{
	Base* pBase = new Base();
	Derived* pDerived = dynamic_cast<Derived*>(pBase); // nullptr 반환
	if (nullptr == pDerived)
	{
		std::cout << "nullptr" << std::endl;
	}

	try
	{
		Base b;
		Derived pDerived1 = dynamic_cast<Derived&>(b);
	}
	catch (std::bad_cast& e)
	{
		std::cout << "Exception : " << e.what(); // "Bad dynamic_cast!"
	}

	return 0;
}



런타임 타입 정보(RTTI)

dynamic_cast는 런타임에 실제 객체의 타입 정보를 확인하기 위해 RTTI를 사용합니다.

Microsoft Learn : RTTI

RTTI(Run-Time Type Information) : 프로그램 실행 중에 객체의 타입이 결정될 수 있도록 하는 메커니즘입니다. 가상 함수 테이블에 있는 type_info 객체에 대한 포인터를 사용합니다.

  • typeid 연산자 : 객체의 정확한 타입을 식별하는 데 사용됩니다.
  • type_info 클래스 : 연산자가 반환한 타입 정보(typeid)를 보관하는 데 사용됩니다.


typeid 연산자

<typeinfo> 헤더에 존재하는 typeid 연산자를 통해 데이터의 타입을 얻어올 수 있습니다.

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

반환 타입은 const std::type_info& 입니다.


type_info 클래스

Microsoft Learn : type_info 클래스

type_info는 typeid로 얻어온 데이터 타입을 보관하는 클래스입니다.

  • type_info 클래스의 객체를 생성하는 유일한 방법은 typeid 연산자의 반환값을 사용하는 것입니다. (상수 객체)
  • 복사 생성자, 복사 대입 연산자는 삭제되어 객체를 복사 생성하거나 할당할 수 없습니다.
  • name()이라는 멤버 함수를 통해서 타입의 이름을 얻을 수 있습니다.
#include<iostream>
#include<typeinfo>
using namespace std;

int main()
{
	int num = 10;
	auto str = "ABC";

	const std::type_info& info1 = typeid(num);
	const std::type_info& info2 = typeid(str);

	cout << info1.name() << endl;
	cout << info2.name() << endl;

	return 0;
}

결과 :

matrix


이렇게 활용할 수 있습니다.

#include<iostream>
#include<typeinfo>
using namespace std;

int main()
{
	int num = 10;

	const std::type_info& info1 = typeid(num);

	if (*info1.name() == *"int")
	{
		cout << "num is int type" << endl;
	}

	if (typeid(num) == typeid(int))
	{
		cout << "num is int type" << endl;
	}
	
	return 0;
}

결과 :

matrix

Categories:

Updated:

Leave a comment