본문 바로가기
스터디/C++

[ C++ ] C++ 형 변환 연산자 총 정리( static_cast, const_cast, dynamic_cast, reinterpret_cast )

by 알 수 없는 사용자 2019. 12. 7.

C 스타일의 형 변환 연산자는 무적의 형 변환 연산자(Old C-style cast operator)이기에.. C 언어와의 호환성을 위해서 존재하지만, C++에서의 새로운 형 변환 연산자를 사용하는 것이 안전하다.

 

C++에서 발생할 수 있는 C의 형 변환의 문제점은 기초 클래스의 포인터 형을 유도 클래스의 포인터 형으로 변환하는 것이 있기에, 일반적인 연산이 아니다.

 

그래서 C++은 다음과 같이 총 4개의 연산자를 추가로 제공하면서 용도에 맞는 형 변환 연산자의 사용을 권하고 있다.

 

  • dynamic_cast

dynamic_cast<T>(expr)

<> 사이에 변환하고자 하는 자료형의 이름을 둔다. 하지만 객체의 포인터 또는 참조형이 와야 한다.

( ) 사이에는 변환의 대상이 와야 한다.

 

 dynamic_cast는 다음과 같은 상황에만 형 변환이 가능하다.

 

"상속 관계에 놓여 있는 두 클래스 사이에서 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환하는 경우"

 

 

예제를 보자.

기초 클래스의 포인터 및 참조형 데이터를 유도 클래스의 포인터 및 참조형 데이터로 바꿀 때 컴파일 에러가 나는 것을 확인할 수 있다.

 

즉, dynamic_cast는 상속관계에 있는 유도 클래스의 포인터 및 참조형 데이터를 기초 클래스의 포인터 및 참조형 데이터로 형 변환을 할 때 사용이 된다.

 

하지만 18행에 있는 변환은 필요할 경우도 있다. 이럴 때는 static_cast를 사용하면 된다.

 

  • static_cast

static_cast<T>(expr)

 

static_cast는 dynamic_cast와 다르게 유도 클래스의 포인터 및 참조형 데이터기초 클래스의 포인터 및 참조형 데이터로 뿐만 아니라, 기초 클래스의 포인터 및 참조형 데이터유도 클래스의 포인터 및 참조형 데이터로 아무런 조건 없이 형 변환시켜주지만, 그에 대한 책임은 프로그래머가 져야 한다.

 

예제를 보자.

 

 

22행 같은 경우에 주의를 해야 한다.

19행은 실제로 포인터가 가리키는 객체가 Children이기에 책임을 질 수 있겠지만, 

22행은 실제로 포인터가 가리키는 객체가 Parent이기에 Children에 없는 멤버가 있을 수 있기에 책임을 확실히 져야 한다.

 

static_cast 연산자는 dynamic_cast 연산자와 달리, 보다 많은 형 변환을 허용한다. 하지만 그에 대한 책임을 져야 하기 때문에 신중하게 선택해야 합니다. 

 

 

그리고 static_cast는 기본 자료형 데이터 간의 형 변환에도 사용이 됩니다.

 

그럼 static_cast 연산자를 사용하면, 그에 대한 연산 결과를 프로그래머가 책임을 져야 하면, C언어의 형 변환 연산자를 사용하는 것과 무슨 차이가 있을까?

 

C언어의 형 변환 연산자는 다음과 같이 정말 일반적이지 않는 형 변환도 허용하기 때문에 static_cast 연산자의 사용을 권한다.

 

포인터에서 const 상수 값을 바꿔버린다.. 이렇듯 C언어의 형 변환 연산자를 사용을 권하지 않는 이유이다.

 

  • dynamic_cast의 특징

 

그럼 dynamic_cast와 static_cast를 정리하면 다음과 같다.

 

  • [ 유도 클래스의 포인터 및 참조형 데이터 -> 기초 클래스의 포인터 및 참조형 데이터 ] 형 변환할 경우에는 dynamic_cast 연산자를 사용한다.
  • [ 기초 클래스의 포인터 및 참조형 데이터 -> 유도 클래스의 포인터 및 참조형 데이터 ] 형 변환할 경우에는 static_cast 연산자를 사용한다.

 

하지만 dynamic_cast 연산자도 기초 클래스의 포인터 및 참조형 데이터유도 클래스의 포인터 및 참조형 데이터로 형 변환을 허용한다. 

 

단, 기초 클래스가 Polymorphic 클래스일 때만 가능하다.

 

Polymorphic(다형성) 클래스란 하나 이상의 가상 함수를 가진 클래스를 뜻한다.

 

 

예제를 보자.

A클래스 Polymorphic클래스이다. Show가상 함수이기 때문이다.

하지만 가상 함수가 없을 경우에는 에러가 발생한다.

 

다음과 같은 연산이 성공한 이유는 aptr가 실제 가리키는 객체가 B객체이기 때문이다.

만약에 aptr가 가리키는 객체가 A객체면 어떻게 될까?

 

다음과 같이 안정적이지 못한 형 변환에서 dynamic_cast 연산자는 NULL을 반환한다.

 

이렇듯 dynamic_cast는 안정적인 형 변환을 보장한다. 하지만, 컴파일 시간이 아닌 런타임에 안정성을 검사하도록 컴파일러가 바이너리 코드를 생성한다.

실행 속도가 늦어지지만, 그만큼 안전적인 형 변환이 가능하다. 그래서 dynamic으로 연산자의 이름이 정해졌다.

 

staic_cast는 안전성을 보장하지 않는다. 컴파일러는 무조건 형 변환이 되도록 바이너리 코드를 생성하기에 결과에 따른 책임은 프로그래머가 져야 한다. 그렇지만 실행 속도는 빠른 장점이 있다. 그래서 static으로 연산자의 이름이 정해졌다.

 

 

  • const_cast

const_cast<T>(expr)

const_cast는 포인터와 참조자의 const 성향을 제거하는 형 변환 연산자이다.

 

이러한 연산은 const의 가치를 떨어뜨릴 수 있다고 생각할 수 있는데, 다음과 같은 형태에서는 유용할 수 있다.

 

이렇게 함수의 인자 전달 시 const 선언으로 인한 형(type)의 불일치가 발생해서 인자의 전달이 불가능한 경우에 유용하다. ( volatile의 성향을 제거하는데도 사용할 수 있다. )

 

 

  • reinterpret_cast

reinterpret_cast<T>(expr)

 

reinterpret_cast 연산자는 전혀 상관이 없는 자료형으로의 형 변환에 사용이 된다.

 

다음과 같이 두 클래스가 있다고 가정하자.

클래스 A와 B는 상속으로 관계를 맺은 것도 아니니, 서로 전혀 상관없는 클래스이다.

 

이렇게 reinterpret_cast 연사자는 포인터를 대상으로 하는, 그리고 포인터와 관련이 있는 모든 유형의 형 변환을 허용한다.

 

그럼 reinterpret_cast 연산자는 왜 존재할까? 어디에 의미 있게 사용이 될까?

 

예제를 보자.

이렇게 원하는 데이터 단위로 접근을 하기 위해서 쓰일 때 유용하다.

그리고 포인터와 관련이 있는 모든 유형의 형 변환을 허용한다고 했기에 다음도 가능하다.

 

크게 의미를 부여하긴 어렵겠지만, 특정 상황에서는 유용하게 사용되기도 한다.