C 스타일에 암시적 명시정 형변환은 컴파일시 타입에 대한 오류를 잡아내지 못한다. (추후 런타임에 에러가 남)
int main()
{
char a = 10;
int *p = (int*)&a;
*p = 5; //런타임 에러
return 0;
}
static_cast<new_type>(expression)
컴파일 타임에 형변환에 대한 타입을 변환
논리적으로 변환 가능한 타입을 변환한다.
그렇기 때문에 컴파일시 타입에 대한 오류를 잡아 준다는 장점이 있다.
단 포인터 타입을 다른 것으로 변환 하는 것을 허용하지 않는다. 그러나 상속 관계에 있는 포인터 끼리 변환이 가능하다.
포인터 형변환 예시 (오류)
#include <iostream>
using namespace std;
int main()
{
// char* -> int*
char str[] = "Hello World";
int* ptr2;
ptr2 = static_cast<int*>(str);
// int* -> char*
int tmp = 10;
int* ptr3 = &tmp;
char* c = static_cast<char*>(ptr3);
}
주의 해야하는 상황 1
const 임에도 불구하고 의도하지 않게 정상작동한다.
#include <iostream>
using namespace std;
int main()
{
const int num = 10;
int* ptr = (int*)#
*ptr = 30; // const int 가 변해버림
cout << *ptr << endl;
}
주의 해야하는 상황 2
#include <iostream>
using namespace std;
class Test
{
public:
int m_test = 10;
};
int main()
{
int a = 5;
long long b = a; // 암시적 형변환. 컴파일러가 형을 변환해준다.
Test* t = (Test*)&a; // 명시적 형변환. 프로그래머가 형변환을 위한 코드를 직접 작성한다.
cout << t->m_test << endl;
}
올바른 예시
#include <iostream>
using namespace std;
int main()
{
double d = 1.0;
float f = 1.f;
int tmp_int = static_cast<int>(d); // double -> int 로 형변환
cout << tmp_int << endl;
float tmp_float = static_cast<float>(d); // double -> float 로 형변환
cout << tmp_int << endl;
double tmp_double = static_cast<double>(f); // float -> double 로 형변환
cout << tmp_int << endl;
}
상속관계에서의 static_cast는 upcast는 문제가 없지만
downcast(static_cast<자식클래스>(부모클래스))시에는 unsafe 하게 동작할 수 있다.
downcast를 안전하게 사용하려면 dynamic_cast 사용하자
upcast downcast 예시
upcast는 올바르게 동작하는 방면 downcast 는 의도치 않은 결과가 나온다.
아래 코드를 보면 downcast상황에서 triangle타입인 pt 가 OnlyTriangle()를 불렀더니 이상한 값이 나왔다
shape s는 triangle을 생성하지 않았는데 접근했기 때문이다.
#include <iostream>
using namespace std;
class shape
{
private:
int a;
public:
virtual void Draw() { cout << "shape : called Draw()" << endl; }
};
class triangle : public shape {
private:
int b;
public:
triangle() { b = 30; }
void Draw() { cout << "triangle : called Draw()" << endl; }
void OnlyTriangle() { cout << "OnlyTriangle()" << endl; cout << b << endl; }
};
int main()
{
// 1. upcast
shape* ps;
triangle t;
ps = static_cast<shape*>(&t); // 자식 -> 부모
cout << "1. upcast" << endl;
ps->Draw();
// 2.downcast
shape s;
triangle* pt;
cout << "2. downcast" << endl;
pt = static_cast<triangle*>(&s); // 부모 -> 자식
pt->Draw();
pt->OnlyTriangle();
}
C 스타일 형변환은
그렇기에 좀 더 안전하고, 사용자의 의도를 며확히 하기 위해, c++ 스타일의 형변환을 사용하자.
const<new_type>(expression)
포인터 또는 참조형의 상수형을 잠깐 제거해주는데 사용된다.
volatile키워드를 잠깐 제거해 주는 데에도 사용이 가능
#include <iostream>
using namespace std;
int main()
{
// 포인터 상수형 제거
char str[] = "apple";
const char* ptr = str;
cout << "before : " << str << endl;
//ptr[0] = 'Q'; //error
char* c = const_cast<char*>(ptr); // const char* -> char*
c[0] = 'Q';
cout << "after : " << str << endl;
// 참조형 상수성 제거
int temp = 15;
const int& ref = temp;
cout << "before : " << temp << endl;
// ref = 20; // error
int& r = const_cast<int&>(ref);
r = 30;
cout << "after : " << temp << endl;
}
reinterpret_cast<new_type>(expression)
임의의 포인터 타입기리 변환을 허용하는 캐스트 연산자
저수형을 포인터로 바꿀 수도 있다.
expression에 해당하는 것을 new_type으로 비트단위로 바꾸는 것
#include <iostream>
using namespace std;
int main()
{
int a = 0x0102;
// 1. int -> int* 로 타입캐스팅
// 변수 a의 값을 절대주소로 받는 포인터 ptr1
// 이 경우에는 주소 000번지를 가리키고 있는 pointer 가 된다.
// 어느 곳을 가리킬지 모르기 때문에 위험하다.
int* ptr1 = reinterpret_cast<int*>(a);
// 2. int* -> char* 로 타입캐스팅
// 컴파일에 따라 다르게 나온다.
int* ptr2 = &a;
char* c = reinterpret_cast<char*>(ptr2);
// 0201 0000 순서대로 들어가는데 char 타입이므로 0이 들어가서 0이 출력된다.(컴파일 따라 다름)
cout << "2. int* -> char* (cout) : " << *c << endl;
// 3. struct 내의 첫번째 int -> int*
// struct cube에는 int 형 변수 하나만 존재하므로,
// ptr3는 int a변수의 시작점을 잘 가리키고 있다.
struct Cube { int a; };
Cube cb;
cb.a = 20;
int* ptr3;
ptr3 = reinterpret_cast<int*>(&cb);
cout << "3. struct -> int* : " << *ptr3 << endl;
}
dynamic_cast<new_type>(expression)
안전한 다운캐스팅에 사용된다.
부모 클래스의 포인터에서 자식 클래스의 포인터로 다운 캐스팅 해주는 연산자.
런타임 시간에 실제로 해당 타입이 다운 캐스팅이 가능하지 검사
Class의 포인터 간 형 변호나 시, 안전하게 down casting을 위해 사용한다.
Class의 참조변수 간 형 변환 시, 안전하게 down castring을 위해 사용한다.
단, parent에 virtual 함수가 존재해야 정상 동작한다.
성공할 경우 : new_type의 value를 return 한다.
실패할 경우(new_type = point) : null pointer
실패할 경우(new_type = reference) : bad_cast(exception 처리된다.)
부모클래스의 생성자로 생성되었고 부모 포인터가 가리키고 있는 클래스를 자식클래스 포인터로 형변환 할 때 runtime error (자식클래스는 생성되지도 않았으므로.)
자식클래스의 생성자로 생성되었고 부모클래스 포인터가 가리키고 있는 클래스를 자식클래스로 형변환 할 때 OK
자식클래스의 생성자로 생성되었고 자식클래스를 포인터가 가리키고 있는 클래스를 부모클래스로 형변환 할 때는 dynamic_cast를 사용하지 않고 static_cast를 사용한다. (upcast)
#include <iostream>
using namespace std;
class Base
{
public:
Base() { cout << "Base()\n"; };
virtual ~Base() { cout << "~Base()\n"; };
void Show() {
cout << "This is Base Class\n";
}
};
class Derived : public Base
{
public:
Derived() { cout << "Derived()\n"; };
virtual ~Derived() { cout << "~Derived()\n"; };
void Show() {
cout << "This is Derived Class\n";
}
};
int main(void)
{
Base* pBase = new Derived();
pBase->Show();
Derived* pDerived = dynamic_cast<Derived*>(pBase);
if (pDerived == nullptr)
{
cout << "Runtime Error\n";
}
else
{
pDerived->Show(); //Derived 로 생성되었으므로 문제없음
}
delete pBase;
system("pause");
return 0;
}