C++에서 캐스팅(Casting)은 특정 타입의 변수나 객체를 다른 타입으로 변환하는 과정을 말합니다. C++은 C언어 스타일의 캐스팅 외에도, 더 명시적이고 안전한 4가지의 캐스팅 연산자를 제공합니다. 이 연산자들은 개발자의 의도를 명확하게 드러내고, 컴파일 타임이나 런타임에 타입 검사를 수행하여 잠재적인 오류를 줄여줍니다.
C 스타일 캐스팅의 문제점
C언어에서 사용하던 방식은 다음과 같습니다.
// (type)expression
int value = (int)float_value;
이 방식은 편리하지만, 몇 가지 문제점을 가지고 있습니다.
- 가독성 저하 : 코드 전체에서 캐스팅이 일어나는 부분을 찾기 어렵습니다.
- 위험성 : 컴파일러가 타입 변환의 안정성을 제대로 검사하지 못하는 경우가 많습니다. 예를 들어, 관련 없는 포인터 타입끼리 변환하거나, const 속성을 제거하는 등의 위험한 변환도 무분별하게 허용합니다.
- 의도 불명확 : 어떤 종류의 캐스팅(단순 형변환, 포인터 변환 등)이 일어나는지 명확히 알기 어렵습니다.
이러한 문제점을 해결하기 위해 C++에서는 4가지 목적별 캐스팅 연산자를 도입했습니다.
C++ 캐스팅 연산자 종류
1) static_cast
- 개념 : 컴파일 타임에 타입 변환의 유효성을 검사하며, 논리적으로 변환 가능한 타입 간의 변환에 사용됩니다. 가장 일반적인 캐스팅 연산자입니다.
- 사용 경우:
- int를 float으로, float을 double으로 바꾸는 등 기본적인 숫자 타입 간의 변환
- 상속 관계에 있는 클래스 간의 포인터/참조 변환 (다운캐스팅 시에는 안전성 보장 X)
- void*를 다른 타입의 포인터로 변환
int i = 10;
float f = static_cast<float>(i); // 기본 타입 변환
class Base {};
class Derived : public Base {};
Derived* d = new Derived();
Base* b = static_cast<Base*>(d); // 업캐스팅
2) dynamic_cast
- 개념 : 런타임에 타입 변환의 유효성을 검사하며, 주로 상속 관계에 있는 클래스 간의 다운캐스팅에 안전하게 사용됩니다.
- 특징:
- 런타임에 RTTI(Run-Time Type Information)를 사용하여 실제 객체의 타입을 확인합니다.
- 다운캐스팅이 유효하지 않으면 포인터의 경우 nullptr를, 참조의 경우 std::bad_cast 예외를 발생시킵니다.
- 가상 함수가 하나 이상 포함된 다형성(Polymorphic) 클래스에만 사용할 수 있습니다.
- 사용 경우:
- 부모 클래스 포인터가 실제로 자식 클래스 객체를 가리키는지 확인하고 안전하게 다운캐스팅할 때 사용합니다.
class Base { public: virtual ~Base() {} };
class Derived : public Base {};
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b);
if (d) {
// 캐스팅 성공!
} else {
// 캐스팅 실패! b는 Derived 객체를 가리키고 있지 않음
}
3) const_cast
- 개념 : 변수의 const 또는 volatile 한정자를 제거할 때 사용합니다.
- 주의 : const 객체를 const_cast로 변환하여 값을 수정하려고 시도하는 것은 미정의 동작(Undefined Behavior)을 유발하므로 매우 위험합니다. 원래 const가 아니었던 변수를 가리키는 const 포인터/참조에서 const를 제거할 때만 안전하게 사용할 수 있습니다.
- 사용 경우:
- const 멤버 함수에서 non-const 멤버 변수에 접근해야 하는 경우 (좋은 설계는 아님)
- const 한정자를 인자로 받지 않는 레거시 API에 변수를 전달해야 할 때
void print(int* p) { /* ... */ }
const int val = 10;
const int* p_val = &val;
// print(p_val); // 컴파일 에러
print(const_cast<int*>(p_val)); // 컴파일은 되지만, print 함수 내에서 값을 바꾸면 UB
4) reinterpret_cast
- 개념 : 비트 수준의 재해석을 통해 전혀 관련 없는 타입 간의 변환을 수행합니다. 가장 위험한 캐스팅 연산자입니다.
- 특징 :
- 포인터를 정수로, 정수를 포인터로 바꾸거나, 완전히 다른 타입의 포인터로 변환하는 등 매우 낮은 수준의 변환을 허용합니다.
- 컴파일러는 거의 아무런 검사를 하지 않으며, 이식성이 떨어지고 버그 발생 가능성이 매우 높습니다.
- 사용 경우:
- 메모리 주소를 직접 다루는 하드웨어 관련 코드나 저수준 프로그래밍에서 제한적으로 사용됩니다.
int i = 42;
int* p_i = &i;
// 포인터 주소를 정수로 저장
uintptr_t addr = reinterpret_cast<uintptr_t>(p_i);
// 정수를 다시 포인터로 변환
int* p_restored = reinterpret_cast<int*>(addr);
'CS > C#_C++' 카테고리의 다른 글
C# Virtual / Abstract / Interface (0) | 2025.10.13 |
---|---|
C++ 가상 함수(Virtual Function)와 가상 함수 테이블(VTBL) (0) | 2025.10.08 |
C++ STL(Standard Template Library) (0) | 2025.10.04 |
C# 클래스(Class)와 구조체(Struct)의 차이점 (0) | 2025.10.03 |
C# 박싱(Boxing) 언박싱(Unboxing) (0) | 2025.10.03 |