[c++] Type conversions (Casting Operators)

숭글·2023년 1월 10일
0

dynamic_cast, static_cast, reinterpret_cast, const_cast에 대해 다루는 글.

dynamic_cast <new_type> (expression)
reinterpret_cast <new_type> (expression)
static_cast <new_type> (expression)
const_cast <new_type> (expression)

Implicit conversion

: 캐스팅할 타입이 compatible type일 경우 컴파일러가 자동으로 캐스팅한다.

  • 몇 포인터들과 numerical types, bool타입에서 허용되는 캐스팅이다.
  • standard conversion이라고도 한다.
short a=2000;
int b;
b=a;

static_cast

: 포인터와 그에 관련된 클래스 사이에서 수행되는 conversion이다.

  • implicit conversion(user-defined conversion 포함)를 수행한다.
  • 자식클래스에서 부모클래스로 캐스팅하는 업캐스팅과 부모클래스에서 자식클래스로 캐스팅하는 다운캐스팅을 할 수 있다.
  • 런타임 중에 타입 검사를 해주지 않으므로 변환이 안전하다 보장할 수 없다.(스스로 체크해야 함)
    - 안정성 문제가 있다면 런타임 타입 검사를 해주는 dynamic_cast를 사용해야한다.
    - dynamic_cast에서는 애매하다 생각해서 fail을 주는 케이스를 static_cast에선 캐스팅을 해줄 수도 있다.
1  class B {};
2
3  class D : public B {};
4
5  void f(B* b, D* d) {
6     D* d2 = static_cast<D*>(b); 
7
8     B* b2 = static_cast<B*>(d);  
9  }

코드 출처
위 같은 경우 D클래스는 B 클래스를 상속받았고 다운캐스팅과 업캐스팅을 수행한다.
line 6 처럼 static_cast를 통해 다운캐스팅하는 경우 에러는 발생하지 않는다. 하지만 B클래스는 없지만 D클래스가 갖는 멤버를 호출하는 경우 문제가 생길 수 있다.

class B {
public:
   virtual void Test(){}
};
class D : public B {};

void f(B* pb) {
   D* pd1 = dynamic_cast<D*>(pb);
   D* pd2 = static_cast<D*>(pb);
}

pb는
1. 처음부터 B클래스의 인스턴스로 생성된 경우
2. 자식인 D클래스의 인스턴스였지만 B 타입으로 업캐스팅된 경우
둘 중 한 경우에 속할 것이다.

만약 1인 경우 dynamic_cast는 bad_cast로 판단하고 0을 리턴할 것이고 2인 경우 정상적으로 캐스팅을 수행할 것이다.
static_cast면 두 경우의 차이를 구분하지못하고 캐스팅을 수행할 것이다.

dynamic_cast

: 포인터와 래퍼런스를 클래스로 변환한다.

  • pointer upcast, downcast, implicit conversion까지 수행한다.
  • 다운캐스팅의 경우 캐스팅하려는 타입이 유효하고 polymorphic 클래스인 겨웅에만 캐스팅을 수행한다.
    - Run-Time Type Information (RTTI) 을 수행하므로 static_cast보다 오버헤드가 있다.
  • 캐스팅이 실패했을 경우 포인터와 래퍼런스의 리턴값이 다르다.
    - 포인터 캐스팅을 실패할 경우 0을 반환한다.
    • 래퍼런스 캐스팅을 실패한 경우 bad_cast 예외가 발생한다.

reinterpret_cast

: 포인터 타입을 다른 포인터 타입으로 변환한다(관련없는 포인터라도 변환한다).
모든 포인터 변환이 가능하지만 가르키는 content나 포인터 타입은 체크하지 않는다.

  • 정수형 -> 포인터, 포인터 -> 정수형으로도 변환 가능하다.
  • intptr_t 같이 포인터를 다 표현할 수 있을만큼 범위가 큰 정수형이라면 다시 포인터로 변환했을 때 유효하게 사용될 수 있음을 보장한다.
  • 타입의 이진 표현을 reinterpret한 low-level 연산이므로 시스템 사이에서 nonportable하다.
  • const, volatile, __unaligned 속성은 변환할 수 없다. (const_cast를 쓰자!)
  • NULL 포인터를 변환할 타입의 NULL 포인터 값으로 변환한다.
Data *test = new Data(34, "qwe");

char a = reinterpret_cast<char>(test);
test = reinterpret_cast<Data *>(a);

Data 객체를 가르키는 포인터를 char형으로 변환을 시도한 코드이다.

main.cpp:47:12: error: cast from pointer to
      smaller type 'char' loses information
  ...char a = reinterpret_cast<char>(test);

위처럼 정보를 잃을 수 있다는 컴파일 에러가 발생한다.

const_cast

: 가르키는 포인터의 const속성을 설정하거나 제거한다.

  • NULL 포인터를 변환할 타입의 NULL 포인터 값으로 변환한다.
  • 참조하는 개체의 타입에 따라 write작업이 undefined behavior를 야기할 수 있다.

example

void print (char * str)
{
  cout << str << '\n';
}

int main () {
  const char * c = "sample text";
  print(c);
  return 0;
}

output>>

main.c:11:3: error: no matching function for
      call to 'print'
  print(c);
  ^~~~~
main.c:4:6: note: candidate function not viable:
      1st argument ('const char *') would lose
      const qualifier
void print (char * str)

const가 없다며 매칭되는 함수가 없어 컴파일 에러가 발생한다.

void print (char * str)
{
  cout << str << '\n';
}

int main () {
  const char * c = "sample text";
  print ( const_cast<char *> (c) );
  return 0;
}

output>>

sample text

📖 Type conversions
📖 Casting Operators

profile
Hi!😁 I'm Soongle. Welcome to my Velog!!!

0개의 댓글