형변환 연산자
▶ C++형변환 연산자의 종류
1. static_cast
ㆍ 컴파일 시간에 형변환이 이루어지는 가장 일반적인 형변환 연산자.
ㆍ 기본 자료형 간의 변환, 객체 포인터 간의 변환, 함수 포인터 간의 변환에 사용
ㆍ 예를들어 double을 int로 변환, 부모클래스의 포인터를 자식 클래스의 포인터로 변환할 때 사용
double d = 3.14;
int i = static_cast<int>(d);
※ static_cast와 일반 형변환의 차이점
ㆍ C++스타일의 형변환 연산자인 static_cast
-> 컴파일 타임에 타입 체크를 수행함, 즉 잘못된 형변환을 시도하면 컴파일 에러 발생
-> static_cast의 역할만 수행
ㆍ C스타일의 (type)value
-> 컴파일 타임에 타입 체크를 수행하지 않음, 잘못된 형변환을 시도해도 컴파일 오류가 발생하지 않아 런타임에서 예상치 못한 결과를 초래할 수 있다.
-> 모든 종류의 형변환을 수행할 수 있다, 즉 const_cast, static_cast, reinterpret_cast의 형변환을 모두 수행할 수 있다
2. dynamic_cast
ㆍ 런타임에 형변환을 수행하는 연산자로, 주로 다형성을 가진 객체의 포인터나 참조형 간의 변환에 사용
ㆍ 예를들어 부모클래스 포인터를 자식 클래스 포인터로 변환할 때 변환하려는 객체가 실제로 해당 자식클래스의 인스턴스인지 런테임때 체크함
Base* b_ptr = new Derived;
Derived* d_ptr = dynamic_cast<Derived*>(b_ptr);
동적 형변환이 실패하면 nullptr반환
※ static_cast와 dynamic_cast의 차이
ㆍ static_cast는 컴파일 타임에 형변환 유효성을 검사함. 따라서 유효하지 않은 형변환을 시도하면 컴파일 에러가 발생한다. 다만, 형변환의 안정성을 완전히 보장하지는 않음
예를 들어-> 부모 클래스의 포인터를 자식 클래스의 포인터로 형변환 하는 경우(dynamic) 실제로 해당 포인터가 가리키는 객체가 자식 클래스의 인스턴스인지는 검사하지 않는다.
ㆍ dynamic_cast는 런타임에 형변환의 유효성을 검사함, 부모 클래스의 포인터를 자식 클래스의 포인터로 형변환 하는 경우 실제로 해당 포인터가 가리키는 객체가 자식 클래스의 인스턴스인지 런타임때 검사함. 만약 실패하면 nullptr을 반환한다.
※ 결론
-> dynamic_cast 의런타임 체크는 약간의 성능 오버헤드를 초래할 수 있다. 형변환이 항상 유효하다는 것을 알고 있을 때는 static_cast 를 사용하는 것이 좋다.
3. const_cast
ㆍ 객체의 상수성을 추가하거나 제거하는데 사용
ㆍ const 객체를 non-const로 변환하거나 그 반대를 할 수 있다
const int ci = 10;
int* non_const = const_cast<int*>(&ci);
ㆍ 컴파일러는 const 변수를 최적화 하는데 const속성을 사용하므로, const_cast를 사용해 const 변수를 변경하면 예상치 못한 결과를 초래할 수도 있다.
ㆍ const_cast는 const 속성 뿐만 아니라 volatile속성도 제거할 수 있다. volatile 키워드는 변수가 컴파일러에 의해 최적화 되지 않도록 지시하는 키워드
-> 컴파일러는 코드 최적화를 위해 여러 방법을 사용하는데, 예를들어 반복적인 접근을 줄이기 위해 그 값을 레지스터에 저장하거나, 변하지 않는 값은 미리 계산해두는 방법을 사용한다., volatile 키워드는 이러한 최적화를 적용시키지 않겠다는 키워드
- volatile 사용되는 상황 -
1. 멀티 스레딩 환경에서 한 스레드가 다른 스레드에 의해 변경될 수 있는 변수
2. 하드웨어 레지스터에 직접 매핑된 변수
3. 인터럽트 핸들러에서 사용되는 변수
※ 사용 예시
void changeValue(int* ptr)
{
*ptr = 10;
std::cout << *ptr << std::endl;
}
int main()
{
int x = 5;
const int* ptr = &x; // x에 대한 const 포인터를 생성
// changeValue 함수는 non-const int*를 인자로 받으므로, ptr을 직접 전달할 수 없다
// const_cast를 사용하여 ptr의 const 속성을 제거하고, 그 결과를 changeValue 함수에 전달
changeValue(const_cast<int*>(ptr));
return 0;
}
ㆍ const_cast 는 주로 const가 아닌 객체에 대한 const포인터나 참조를 non-const포인터나 참조로 변환하는데 사용됨
※ mutable?
class의 const 맴버 함수 내에서 const값을 변경할 수 있게 해주는 키워드
ㆍ const_cast는 const포인터나 참조의 상수성을 제거하는데 사용, mutable으 const 객체의 맴버 변수를 변경할 수 있게 해주는데 사용
4. reinterpret_cast
ㆍ 두 포인터 간의 형변환, 포인터와 정수간의 형변환 등 서로 다른 타입의 참조형간의 형변환에 사용
ㆍ 비트 단위로 형변환을 수행하므로 매우 낮은 수준의 형변환을 수행하고자 할 때 사용됨
int i = 10;
int* p = &i;
long address = reinterpret_cast<long>(p);
※ 주의점
ㆍ 서로 관계가 없는 타입들 간의 변환을 수행한다. 타입 시스템의 규칙을 완전히 무시한다.
※ 필요한 경우
ㆍ 하드웨어와 상호작용 하는 경우 : 종종 메모리를 특정 방식으로 해석해야 하는 경우가 있다. 예를들어 레지스터에 접근하거나 네트워크 패킷을 분석하는 경우에는 reinterpret_cast를 사용해 원시 메모리를 특정 구조체로 해석할 수 있다.
ㆍ 정수를 포인터로 변환하거나 그 반대를 수행하는 경우 : 종종 임베디드 시스템과 같이 주소가 고정된 하드웨어 리소스에 접근해야 할 때 유용
ㆍ 바이트 스트림을 특정 타입으로 해석하는 경우 : 바이너리 파일을 읽거나 네트워크를 통해 데이터를 전송받는 경우, 데이터는 일반적으로 바이트 스트림으로 전달됨. 이러한 바이트 스트림을 특정 타입의 객체로 해석려면 reinterpret_cast를 사용할 수 있다