일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- C++
- increment operator
- std::endl
- conversion constructor
- discord bot
- std::cout
- member function pointer
- delete function
- this call
- std::vector
- constructor
- placement new
- dynamic_cast
- return by reference
- virtual function
- base from member
- pointer to member data
- c++ multi chatting room
- 더 지니어스 양면포커
- operator overloading
- vector size
- virtual destructor
- c++ basic practice
- std::ostream
- diamond inheritance
- suffix return type
- vector capacity
- new&delete
- virtual function table
- virtual inheritance
- Today
- Total
I'm FanJae.
[C++] Explicit casting 본문
※ 본 포스트는 코드누리 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에서는 꽤 직관적으로 오류를 알려준다.
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 |