I'm FanJae.

[C++] Explicit casting 본문

C++/Basic

[C++] Explicit casting

FanJae 2024. 8. 11. 16:19

※ 본 포스트는 코드누리 C++ Basic 강의 내용을 보고 정리한 포스트입니다.

 

1. C언어 캐스팅의 문제점

 

- Explicit Casting

기본적으로, C언어의 캐스팅 방법과 C++ 언어의 캐스팅 방식에는 약간의 차이가 존재한다.

int* p1 = (int *) malloc(sizeof(int)*10);
int* p2 = static_cast<int *>(malloc(sizeof(int)*10));

- C++ 언어로 넘어오면서 아래 방법을 더 권장하기 시작했다.

 

C 방식 캐스팅의 문제점

#include <iostream>
#include <cstdlib>

int main()
{
    int* p1 = (int *)malloc(sizeof(int)*10);
    free(p1);
    
    int n = 10;
    double* p2 = &n;
    *p2 = 3.4;
}

- 논리적으로 위험한 캐스팅 코드도 대부분 허용한다.

- 이에 따라서, 이것이 의도적인 캐스팅인지 개발자의 실수인지 구분이 어렵다.

int* p1 = (int *)malloc(sizeof(int)*10);

- void * 포인터는 크기가 정의되어있지 않으며, 메모리에 접근하려면 실제 데이터 타입으로 변환이 필요하다.

- 따라서, 위는 개발자의 의도적 캐스팅이고, 반드시 필요한 캐스팅이다.

int n = 10;
double* p2 = &n; // Ok
double *p2 = (double*) &n; // Error
*p2 = 3.4;

- int 주소를 double * 변수에 담는것은 매우 위험한 행위다. 따라서, 기본적으로 Compile Error가 발생하는데 이를 캐스팅하여 넣는 행위는 논리적으로는 오류가 발생하지 않는다.

- 그렇다면, 이는 개발자의 의도일까? 아니면 실수 일까?

const int c = 10;
// int* p3 = &c; // Error
int* p3 = (int *)&c; // Ok

*p3 = 20;
std::cout << c << std::endl;

- 상수 변수 주소를 비 상수를 가리키는 포인터로 캐스팅한 예시이다.

- 위 문장이 의도된 문장이라고 하더라도, 컴파일러는 정의되지 않는 동작을 수행한다.

 

※ C++ 에서는 이와 같이 C의 캐스팅 방식이 개발자의 의도인지 실수인지 파악하기 어려운 문제 때문에 이를 명시하여 개발자가 의도된 캐스팅을 할 수 있도록 하고 실수한 부분이 있는지 파악할 수 있도록 4개의 명시적 캐스팅 연산자를 제공하고 있다. 오늘은 그 중 3개의 캐스팅을 다뤄보고자 한다. Dynamic_cast는 상속에 대한 개념에서 별도 포스트로 얘기하고자 한다.


2. static_cast

- 논리적으로 맞고, 반드시 필요한 경우의 캐스팅만 허용한다.

// int* p1 = (int *)malloc(100); // Ok
   int* p1 = static_cast<int*>(malloc(100));
   free(p1);

- 위 경우는 동적 메모리 할당을 위해서 반드시 형 변환이 일어나야 한다

- 이는 C++ 캐스팅이 된다고 하더라도 반드시 필요하다고 판단하는 것이다.

- 즉,  void * -> 다른 타입 포인터(int *)는 허용한다.

int  n = 10;
double* p2 = static_cast<double *>(&n); // Error

- int *를 double *로 변환하는 것을 위험하다고 판단하여 허락하지 않음.

const int c = 10;
// int* p3 = (int *)&c; 
int * p3 = static_cast<int *>(&c);

- 이와 같은 상수 변수 주소를 비 상수를 가리키는 포인터로 캐스팅 하는 형태도 허용하지 않는다.

 

이외에도 primitive type 간의 캐스팅, 상속 관계 타입 간의 캐스팅, 사용자 정의 변환 연산자나 변환 생성자가 있는 경우도 허락한다. 상속 및 사용자 정의 변환 연산자 등은 차후 이를 익힌 후에 포스트를 정리하고자 한다.

 

아래는 primitive type 간의 값 캐스팅 예시이다.

int n = static_cast<int>(3.4);

3. reinterpret_cast

- 메모리의 재해석(reinterpret)

- 메모리의 해석 방식을 변경하는 캐스팅

 

int n = 10;

char* p1 = static_cast<char *>(&n); // error
char* p2 = reinterpret_cast<char*>(&n); // ok

- 이와 같이 메모리의 해석 방식을 변경하는 방법이다.

- 이렇게 서로 다른 타입의 주소(참조) 캐스팅은 저수준 메모리 접근에 사용된다.

int* p3 = static_cast<int*>(1000); // Ok
int* p4 = reinterpret_cast<int*>(1000); // Ok

- 정수 <-> 주소 사이의 캐스팅도 허용한다.

double d = reinterpret_cast<double>(3); // Error.

- 이는 원래 캐스팅이 필요한 작업이 아니지만, 이는 에러로 간주한다.

- Visual Studio에서는 꽤 직관적으로 오류를 알려준다.

reinterpret_cast 오류

 


4. const_cast

- 객체(변수)의 상수성을 제거하는 캐스팅이다.

int main()
{
    const int c = 10;
    
    int* p1 = static_cast<int*>(&c);      // Error
    int* p2 = reinterpret_cast<int*>(&c); // Error
    int* p3 = const_cast<int*>(&c) // ok
    
 //   *p3 = 20; // 이 행위는 하지 말 것.
    
    
 }

- 상수성을 가진 변수를 일반 포인터 변수에 담는 것이 가능하다.

- 하지만, 이렇게 하더라도 값을 변경하는 행위를 하는 것은 매우 위험하다.

 

나는 이것의 사용처가 잘 이해되지 않아서 몇개 조사해본 결과, API 호환성 문제나, const 객체로의 포인터를 함수에 전달할때 유용하다라고 적혀 있었다. 하지만 아직 그 내용만 봐서는 잘 와닿지가 않았다. 학습이 더 필요할듯 싶다. 

'C++ > Basic' 카테고리의 다른 글

[C++] Constructor / Destructor  (0) 2024.08.13
[C++] OOP(Object Oriented Programming) I  (0) 2024.08.12
[C++] nullptr  (0) 2024.08.11
[C++] Reference  (0) 2024.08.09
[C++] 연산자와 제어문  (0) 2024.08.09
Comments