캐스팅(형변환, Casting)

2020. 6. 16. 19:24프로그래밍/C++

암시적(Implicit) 캐스팅

컴파일러가 형을 변환해 줌

>단, 형 변환이 허용되고

>프로그래머가 명시적으로 형 변환을 안 할 경우

 

int num1 = 3;

long num2 = num1;  // 암시적 캐스팅


명시적(Explicit) 캐스팅

프로그래머가 형 변환을 위한 코드를 직접 작성

C++ 캐스팅

1. static_cast

2. const_cast

3. dynamic_cast(C++98, 모던 C++)

4. reinterpret_cast


C 스타일 캐스팅

C++ 스타일 4개의 캐스팅 중 하나를 함

> 뭔가 명확하지 못함

명백한 실수를 컴파일러가 캐치하지 못함

> C++ 캐스팅이 이런 문제를 해결

 

C 스타일 캐스팅 대신 C++ 캐스팅 쓰는 이유?

> 컴파일 도중에 프로그래머의 실수를 잡아줄 수 있다


static_cast

 

1. 값

두 숫자 형 간의 변환

> 값을 유지하려고 노력(단, 반올림 오차는 제외)

> 이진수 표기는 달라질 수 있음

 

예시1)

int number1 = 3;

short number2 = static_cast<short>(number1);  // 앞자리 비트가 잘렸을 뿐, 값은 변하지 않음

 

예시2)

float number1 = 3.f;

int number2 = static_cast<int>(number1); // int 3으로 바이너리가 변함

 

 

2. 개체 포인터 (위의 경우와 아예 따로 동작하니까 같다고 생각하지 말자)

변수형 체크 후(너네 정말 상속관계 맞아?) 베이스 클래스를 파생 클래스로 변환

컴파일 시에만(정적, static) 형 체크 가능

실행 도중 여전히 크래시가 날 수 있음

 

예시1)

Animal* myPet = new Cat(2, "Coco");

 

Cat* myCat = static_cast<Cat*>(myPet);      // OK

 

Dog* myDog = static_cast<Dog*>(myPet);  // 컴파일은 됨. 그러나 위험

myDog->GetDogHouseName();                // Dog 클래스의 멤버를 가지고 있지 않아 크래시가 날 수 있다!

 

예시2)

Animal* myPet = new Cat(2, "Coco");

 

House* myHouse = static_cast<House*>(myPet);  // 컴파일 에러. 말도 안되는 실수를 저지르려고 할 때 

myHouse->GetAddress();                                 // 컴파일 단계에서 막을 수 있다


reinterpret_cast

재해석

 

연관 없는 두 포인터 형 사이의 변환을 허용

Cat* <-> House*

char* <-> int*

 

포인터와 포인터 아닌 변수 사이의 형 변환을 허용

Cat* <-> unsigned int

 

이진수 표기는 달라지지 않음(바이너리 자체는 변하지 않음)

> A형의 이진수 표기를 그냥 B형인 것처럼 해석

 

예시1)

// unsigned int myPetAddress;

Cat* myCat = reinterpret_cast<Cat*>(myPetAddress);

 

예시2)

int* signedNumber = new int(-10);

 

// 컴파일 에러. 유효하지 않은 형 변환

unsigned int* unsignedNumber1 = static_cast<unsigned int*>(signedNumber);

 

// 컴파일 됨. 허나 값은 더 이상 -10이 아님

unsigned int* unsignedNumber2 = reinterpret_cast<unsigned int*>(signedNumber);


const_cast 하면안되는캐스팅

 

const_cast로 형을 바꿀 수 없음

const 또는 volatile 애트리뷰트를 제거할 때 사용

 

예시)

Animal* myPet = new Cat(2, "Coco");

const Animal* petPtr = mypet;

 

Animal* myAnimal1 = (Animal*)petPtr;    // OK (C-style. 하면 다됨)

Cat* myCat1 = (Cat*)petPtr;                  // OK (C-style. 하면 다됨)

 

Animal* myAnimal2 = const_cast<Animal*>(petPtr);  // OK

Cat* myCat2 = const_cast<Cat*>(petPtr);                // 컴파일 에러. 하고 싶으면 static_cast 해야지~

 

 

포인터 형에 사용할 때만 말이 됨

> 값 형은 언제나 복사되니까... 복사해 놓은 값 아무리 써봐야 원본에 영향 없음

 

 

const_cast를 사용할 때는?

> 써드파티 라이브러리(내가 소스코드를 볼 수 없는 라이브러리)가 const를 제대로 사용하지 않을 때

>> 이 경우 아니면 진짜진짜진짜 쓰지 말자...

 

void WriteLine(char* ptr);  // 뭔가 별로인 외부 라이브러리

 

void MyWriteLine(const char* ptr)  // 우리 프로그램에 있는 함수. wrapper

{

    WriteLine(const_cast<char*>(ptr));

}


dynamic_cast

 

실행 중에 형을 판단

포인터 또는 참조 형을 캐스팅할 때만 쓸 수 있음

호환되지 않는 자식형으로 캐스팅하려 하면 NULL을 반환

> 따라서, dynamic_cast가 static_cast 보다 안전

 

그러나 이걸 쓰려면 컴파일 중에 RTTI(실시간 타입정보, Real-Time Type Information)를 켜야 함

> 켜지 않으면 static_cast와 똑같이 동작

>> C++ 프로젝트에서 RTTI를 끄는 것이 보통(성능 중요시 하므로 RTTI를 켜서 얻는 과부하를 버틸수가 없다......)


캐스팅 규칙(베스트 프랙티스)

 

제일 안전한 것 ->가장 위험한 것

 

1. 기본적으로 static_cast를 쓸 것

> reinterpret_cast<Cat*> 대신 static_cast<Cat*>

>> 만약 Cat이 Animal이 아니라면 컴파일러가 에러를 뱉음(상속관계 없으면)

 

2. reinterpret_cast를 쓸 것

> 포인터와 비포인터 사이의 변환

> 서로 연관이 없는 포인터 사이의 변환은 그 데이터형이 맞다고 정말 확신할 때만 할 것

 

3. 내가 변경권한이 없는 외부 라이브러리를 호출할 때만 const_cast를 쓸 것

 

 

출처 : 포큐아카데미 C++ 언매니지드 프로그래밍

'프로그래밍 > C++' 카테고리의 다른 글

static 키워드  (0) 2020.06.17
인라인 함수(Inline Functions)  (0) 2020.06.17
추상 클래스 / 인터페이스  (0) 2020.06.12
다중 상속  (0) 2020.06.12
가상 함수 / 동적 바인딩 / 다형성  (0) 2020.06.11