프로그래밍 언어/C & C++ 정리

다이나믹 캐스트

뽀또치즈맛 2024. 4. 4. 03:36

 

다이나믹 캐스트란?

 

C++ 캐스팅 연산자에는 4가지 유형의 캐스트가 있다.

그 중의 하나인 다이나믹 캐스트는 

교차 캐스트를 수행할 수도 있으며

이를 통해 동일한 클래스 계층 구조를 사용하기가 핵심이다.

 

++

구글 C++ 가이드에 따르면 RTTI 항목에 보면

다이나믹 캐스트와 typeid 사용을 지양하고있다.

 

이는 클래스 구조를 잘못 짠 원인이며,

이를 고려하지 않은 구조가 유지 보수하기 더 수월하다는 의견이 있다.

그만큼 다이나믹 캐스트는 신중하게 사용해야 하나보다 했다.

++

 

 

 

다이나믹 캐스팅을 말하기 전에,
캐스팅이란,

 

C++언어에서는 클래스가 가상 함수를 포함하는 기본 클래스에서 파생되는 경우

해당 기본 클래스 형식에 대한 포인터를 사용하여 파생 클래스 개체에 있는 가상 함수의 구현을 호출할 수 있다.

가상 함수를 포함하는 클래스를 "다형 클래스"라고도 한다.

 

파생 클래스에는 해당 클래스가 파생되는 모든 기본 클래스의 정의가 완전히 포함되어 있으므로

포인터를 이러한 기본 클래스에 대한 클래스 계층 구조 위로 캐스팅해도 된다.

기본 클래스에 포인터를 지정하면 포인터를 계층 구조 아래로 캐스팅 해도 안전할 수 있다.

포인터가 지정 중인 개체가 실제로 기본 클래스에서 파생된 형식이면 안전하다.

이 경우 실제 개체는 "전체 개체"라고 한다.

기본 클래스에 대한 포인터는 전체 개체의 "하위 개체"를 가리킨다.

예를 들어 다음 그림과 같은 클래스 계층 구조를 고려해보자

 

클래스 계층 구조

 

 

그럼 인간형식의 개체는 다음 그림과 같이 시각화될 수 있다.

 

 

하위 개체 포유류 및 동물 포함 인간 클래스

 

 

인간 클래스의 인스턴스를 제공하면 포유류 하위 개체 및 동물 하위 개체가 있다.

인간 및 동물 하위 개체를 포함한 포유류 클래스의 인스턴스는 완전한 개체이다.

 

런타임 형식 정보를 사용하면 실제로 포인터가 완전한 개체를 가리키며 해당 계층 구조에서

다른 개체를 가리키도록 안전하게 캐스팅될 수 있는지 여부를 확인할 수 있다.

dynamic_cast 연산자를 사용하면 이러한 유형의 캐스트를 만들 수 있다.

이러한 연산자는 안전하게 작업하는 데 필요한 런타임 검사도 수행한다.

 

비다형성 타입 변환의 경우 static_cast 연산자를 사용할 수 있다.

(이 항목의 경우 스태틱 및 다이나믹 캐스팅 변환의 차이점과

언제 각각 사용하는 것이 적절한 경우를 설명하고 있다.)

 

 

다이나믹 캐스트의 사용 구문 예

dynamic_cast < type-id > ( expression )

 

다이나믹 캐스팅을 하기 위해서는,

포인터 type-id 또는 이전에 정의한 클래스 형식에 대한 참조 또는  "void에 대한 포인터"여야한다.

type-id이 포인터인 경우 expression의 형식은 포인터여야 하며

type-id이 참조인 경우에는 L-value이다.

 

정적 및 동적 캐스팅 변환 간의 차이점과 각각을 사용하는 것이 적절한 경우의 설명은

static_cast 정리에서 추가하겠다.

 

dynamic_cast의 동작에는 두 가지 변경 사항이 있다.

 

  • dynamic_cast 는 상자형 enum의 기본 형식에 대한 포인터에 대한 포인터는
    런타임에 실패하면 실패한 반환된 포인터 대신에 0을 반환한다.
  • dynamic_cast는 값 유형에 대한 내부 포인터인 경우 캐스트는 런타임에 실패하게 된다.
    type-id를 더 이상 예외로 throw하지 않고, 0을 반환한다.

 

type-id가 모호하지 않은 접근 가능한 직접 또는 간접 기본 클래스의 표현에 대한

포인터라면 type-id의 고유한 하위 개체에 대한 포인터가 그 결과이다.

 

 

// dynamic_cast
// compile with: /c

class B {};
class C : public B {};
class D : public C {};

void f(D* pd) {
	C* pc = dynamic_cast<C*>(pd);	// ok : C is a direct base class
									// pc points to C subobject of pd
	B* pb = dynamic_cast<B*>(pd);	// ok : B is an indirect base class
									// pb points to B subo
}

 

 

 

파생 클래스로 클래스 계층 위로 이동시키기 때문에 "업캐스트"라고 불린다.

업캐스트는 암묵적인 변환이다. 

 

type-id가 void* 인 경우, 실제 식의 유형을 결정하기 위해 런타임 체크가 이루어진다.

결과는 표현 식으로 가리키는 완전한 객체에 대한 포인터이다.

 

class A { virtual void f(); };
class B { virtual void f(); };

void f() {
	A* pa = new A;
	B* pb = new B;

	void* pv = dynamic_cast<void*>(pa);
	// pv now points to an object of type A

	pv = dynamic_cast<void*>(pb);
	// pv now points to an object of type B
}

 

 

type-id가 void*가 아닌 경우,

식으로 가리키는 객체가 type-id로 가리키는 유형으로 반환될 수 있는지

확인하기 위해 런타임 체크가 이루어진다.

 

유형이 type-id 유형의 기본 클래스인 경우,

실제로 type-id 유형의 완전한 객체를 가리키는지 확인하기 위해 런타임 체크가 이루어진다.

이것이 사실이면 결과는 type-id 유형의 완전한 개체에 대한 포인터이다.

 

즉 다이나믹 캐스팅은 안전한 다운캐스팅을 할 때 사용하는 연산자이다.

즉, 주어진 객체가 어떤 클래스 상속 계통에 속한 특정 타입인지 아닌지를 결정하는 작업에 쓰인다.

구형 스타일의 캐스트 문법으로는 흉내조차 낼 수 없는 유일한 캐스트이다.

덤으로 신경 쓰일 정도로 런타임 비용이 높은 캐스트 연산자로도 유일하다.

 

 

 

 

학습 출저

https://www.youtube.com/watch?v=BmXODdi7fcE&list=PLDV-cCQnUlIar6Wx3rkXHs7wbfmlz34Fz&index=9

https://learn.microsoft.com/ko-kr/cpp/cpp/dynamic-cast-operator?view=msvc-170