개발하는 리프터 꽃게맨입니다.

[C++] 캐스팅 4총사 본문

언어/C, C++

[C++] 캐스팅 4총사

파워꽃게맨 2024. 1. 8. 12:15

캐스팅 이란?

캐스팅은 쉽게 말해서 형 변환입니다.

기본 데이터 타입 int, char, short, float 등..

데이터를 담는 그릇을 바꿔준다고 보시면 되겠습니다.

 

다음 예시를 보겠습니다.

이런 식으로 바꿔주고 싶은 데이터형을 앞에 명시해 준다면,

자동적으로 적당한 데이터로 컴파일러가 데이터 형을 변환합니다.

 

위 예시는 'C언어 스타일 캐스팅' 방법입니다.

C 스타일 캐스팅

C 언어에서는 하나의 캐스팅밖에 존재하지 않았습니다.

이 예시들을 보면 알 수 있듯

그냥 간편하게 괄호 안에 데이터형을 명시해 주면 강제로 캐스팅해 줍니다.

 

강제로 캐스팅해 준다. 이거 참 무섭지 않나요?

런타임에서 충분히 오류가 발생할 수 있는 코드도 컴파일에서 통과시켜 준다는 것입니다.

 

1) float에서 int로 바꾸는 것은 기본적으로 허용이 되는 캐스팅이기 때문에 오류가 발생하지 않습니다.

 

2) pet 포인터에는 Dog 개체가 들어가 있는데, Dog 개체를 Cat*에 캐스팅해 줍니다.

Cat* 에 Dog 개체가 들어가 있다.. 벌써부터 오류의 냄새가 나지 않나요?

그런데도 오류는 발생하지 않습니다.

 

3) 전~혀 관계가 하나도 없는 Table*에 Dog 개체를 넣으려고 해도 오류 없이 잘 캐스팅됩니다.

심각한 코드 오류인데, 컴파일러단에서는 잡아주고 있지 않죠.

 

4) 이건 진짜 놀라운 코드인데, const 포인터 데이터 형은 원래 초기화로 사용할 수 없습니다.

const로 기껏 수정 못하게 막아놨는데,

그냥 포인터로 const의 주소를 참조해서 값을 조작하는 일이 발생할 수도 있기 때문이죠.

 

그런데, 4번째 코드는 const조차 무시해 버리고 주소를 반환하고 있습니다.

이러면 const 형 데이터의 내부를 고칠 수 있다는 무서운 일이 발생합니다.

 

5) int* 형을 char* 형으로 강제로 바꾸려고 하고 있습니다.

기본적인 데이터 형식이라서 딱히 문제점을 발견하지 못하실 수도 있는데,

 

animal* 형을 아무런 상관없는 int* 로도 캐스팅이 됩니다.

사실 이렇게 전혀 연관 없는 데이터 형으로 캐스팅이 가능하다는 것부터가

그 결과를 예측할 수 없기에 굉장히 위험한 행동이라는 것이죠.

 

 

C++에서는 캐스팅의 종류를 4개로 나눠서

잘못된 캐스팅의 경우 컴파일 단에서 에러가 발생하도록 구현했습니다.

 

강제로 캐스팅해 주던 C 스타일 캐스팅을 기능을 4가지로 쪼갠 것이지요

static_cast, dynamic_cast, reinterpret_cast, const_cast

총 4가지가 존재합니다.

 

캐스팅 4 총사

1. static_cast

가장 단순하며 많이 쓰이는 형변환입니다.

 

1) 값에 대한 스태틱 캐스트

값에 대해서 스태틱 캐스트를 하면

값의 원형을 최대한 유지하려고 합니다.

 

값을 유지하려는 특성 때문에

2진수 표기는 달라질 수 있습니다.

 

2) 개체 포인터에 대한 스태틱 캐스트

컴파일 시에, 단순한 상속관계만 확인해서 캐스팅해줍니다.

 

먼저, Dog 개체를 Cat*으로 변환하는 것을 보겠습니다.

먼저 Animal과 Cat, Dog은 상속관계를 가지고 있습니다.

 

그래서 Animal 포인터인 pet은 Cat과 일단 상속관계를 가지고 있으니까

알맹이는 Dog 개체라도 일단은 캐스팅을 허용해 주는 것입니다.

(그래서 런타임 오류는 피할 수 없습니다.)

 

그러나 Table과 같이 Animal과 아무런 연관도 없는 형으로의 캐스팅은

컴파일 오류로 막아주고 있습니다.

 

결국, 조금 더 안전한 형변환이라 볼 수 있죠.

그래도 여전히 런타임 에러가 발생할 수 있다는 가능성이 존재합니다.

 

앞서 알아본 5개의 예시를 전부 static_cast로 바꿔서 오류를 체크해 보겠습니다.

 

 

static_cast는 간단히 말해서

단순한 형 변환을 지원하고,

말도 안 되는 형 변환은 막아준다.

 

라고 이해하시면 되겠습니다.

 

2. dynamic_cast

static_cast 보다 조금 더 안전한 캐스팅입니다.

 

포인터 형 변환에 대해서 조금 더 똑똑하게 반응합니다.

 

Cat* 캐스팅해주려면, 컴파일러가 해당 개체가 진짜 Cat 개체인지 확인합니다.

아니라면 NULL을 반환하죠.

 

그래서 일단 위험한 캐스팅 중 하나인

pet을 Table* 에 대한 캐스팅도 허용됩니다.

단, 말이 안 되는 캐스팅이기에 NULL을 반환합니다.

 

그러나, 매우 매우 좋고 안전한 캐스팅 방법이지만

실시간으로 형을 파악하기 때문에 (RTTI 옵션) 속도가 느립니다!

 

그렇기 때문에 많은 프로젝트에서는 RTTI 옵션이 꺼져 있습니다. 

이는 속도면에서 부담이 크기 때문이죠.

 

그래서 안전한 static_cast를 사용하는 것을 지향하도록 합니다.

 

사실 포인터 형변환을 쓰는 일이 많이 없기 때문에,

개체를 생성할 때 Type을 enum으로 파준 다음에

Type이 맞으면 static_cast를 해주는 방식으로 사용할 수 있겠죠.

 

3. reinterpret_cast

재해석 캐스트라고 불리는 캐스팅 문법이며

상당히 위험한 캐스팅입니다.

 

위 예시를 보면

Dog* -> Cat* 허용

Dog* -> Table* 허용

int* -> char* 허용

Dog* -> int 허용

 

제가 앞서 말한 '위험한 캐스팅'을 모두 허용합니다.

용도는 다음과 같습니다.

 

1) 연관 없는 두 포인터 형 사이의 변환을 허용해 준다.

2) 포인터와 포인터가 아닌 변수 사이의 형 변환을 허용한다.

3) 형 변환을 하더라도 이진수 표기는 달라지지 않는다.

 

뭔가 설명을 들으면 더 위험해 보이죠?

 

reinterpret_cast를 사용하는 상황은 굉장히 한정적입니다.

'이진수 표기는 달라지지 않는다'라는 특성을 이용해서

파일에 포인터 개체를 저장하는 용도로 사용합니다.

 

컴퓨터는 포인터 개체를 해석할 수 있는 능력이 없으므로,

포인터 개체를 int 형으로 바꿔서 파일로 저장한 후

불러와서 데이터를 복구하는 용도로 사용하는 캐스팅 방법입니다.

 

추가적인 용도는 일단 저로서는 사용해 본 적이 없어서 모르겠네요!

 

4. const_cast

 

정말 위험한 캐스팅 중 하나입니다.

const를 제거하는 용도로 사용하는 것이 const_cast입니다.

단, const를 제거할 수만 있고 추가적인 형변환은 지원하지 않습니다.

 

const_cast는 쓸 일이 없을뿐더러 쓰면 안 되는 캐스팅입니다.

솔직히 수정하면 안 된다는 의미에서 const를 지정하였는데, 수정할 일이 생겼다는 것부터가 설계를 잘못했다는 것이죠.

 

 

마치며

캐스팅 4 총사

말은 좀 어려워 보이지만 그저 C 스타일의 캐스팅을 4가지 기능으로 쪼갠 것에 불과합니다.

 

그런데, 코드 작성에 있어서 실수를 방지하고

'내가 이런 캐스팅을 할 것이다!'라고 명시할 수 있다는 장점이 있죠.

가독성이 좋아집니다.

 

기본적으로는 static_cast를 이용하고

매우 매우 특수한 경우에는 reinterpret_cast를 사용하도록 합시다.

 

그러면 코드를 훑어보다가

'어..? reinterpret_cast를 사용하네? 무슨 일을 하나?'

라고 캐치하기 쉽습니다.

이것이 캐스팅 문법의 장점이죠.

 

const_cast는 진짜 사용할 일이 없습니다.

외부에서 다운로드한 라이브러리 설계가 잘못되어서 const를 제거해야만 할 경우 아니면

사용하지 않아요.

 

개인적으로 dynamic_cast가 매우 유용하다고 생각하긴 합니다만

속도문제 때문에 속도가 중요한 프로그램에선 사용을 지양해야 한다는 것이 아쉬움으로 남습니다.

'언어 > C, C++' 카테고리의 다른 글

[C++] static 에 대해서  (1) 2024.01.08
[C++] 인라인 함수  (1) 2024.01.08
[C++] 다형성  (0) 2024.01.07
[C++] 클래스 상속  (1) 2024.01.07
[C++] 클래스가 암시적으로 정의하는 함수들  (0) 2024.01.06