본문 바로가기
개발 언어/cpp

CPP 4가지 타입 변환 연산자 (Casting)

by eeeun:) 2022. 3. 20.
반응형

CPP에서는 안전한 casting을 보장하기 위해 casting을 도와주는 4가지 타입 변환 연산자가 있다.

( static_cast, dynamic_cast, const_cast, reinterpret_cast )

1. static_cast

- 실수와 정수, 열거형과 정수형, 실수와 실수 캐스팅 가능

- 컴파일 타임에 형 변환을 하기 때문에 컴파일 당시 오류를 확인할 수 있는 장점이 있다.

 

static_cast <바꾸려는 타입>(바꿀 대상)

 

밑과 같이 부모를 private, protected로 상속받을 경우에는 캐스팅이 되지 않음!

class A {
};

class B : A {
};

int main() {
    A aa;
    B bb;

    // 둘 다 불가능
    aa = bb;
    aa = static_cast<A>(bb);
}

 

부모를 public으로 상속받을 경우에만 캐스팅이 됨!

static_cast를 사용하지 않아도 컴파일러가 자동으로 만든 부모 class의 할당 연산자를 통해 자동으로 자식 -> 부모 캐스팅해서 값이 들어가게 된다.

class A {
};

class B : public A {
};

int main() {
    A aa;
    B bb;

    // 둘 다 가능. 하지만 static_cast로 하는게 당연히 더 안전함.
    aa = bb;
    aa = static_cast<A>(bb);
}

 

static_cast는 업 캐스팅은 가능하지만, 다운 캐스팅은 막아준다!

클래스 포인터의 경우에는 둘 다 가능! <- 밑에 자세히 나온다.

class A {
};

class B : public A {
};

int main(void) {
    A aa;
    B bb;
    // 가능
    aa = static_cast<A>(bb);
    // 불가능
    bb = static_cast<B>(aa);
}

 

static_cast는 function -> function pointer, array -> array pointer의 캐스팅은 지원한다.

pointer를 무언가로 캐스팅하는 것은 지원하지 않는다.

하지만 클래스의 pointer는 예외적으로 가능하다.

다운 캐스팅 업 캐스팅 둘 다 컴파일 오류가 나지는 않는다. 하지만 다운 캐스팅 같은 경우 안전이 보장되지 않는다.

 

class A {
};

class B : public A {
};

int main(void) {
	int *a = new int[4];
	// 불가능
	int b;
	b = static_cast<int *>(a);

	// 가능
	A *aClass;
	B *bClass;
	aClass = static_cast<A *>(bClass);
	bClass = static_cast<B *>(aClass);
}

 

2. dynamic_cast

위의 static_cast에서 클래스의 포인터로 캐스팅을 했을 때 다운 캐스팅의 안정성을 보장받을 수 없었다.

이를 해결하기 위해(?) dynamic_cast가 존재한다.

 

dynamic_cast는 RTTI( Runtime Type Information )을 통해 다운 캐스팅을 할 수 있게 해 준다.

다운 캐스팅이 항상 위험한 것은 아니다

밑의 예제와 같은 상황이 있다고 보자.

자식 -> 부모 -> 자식
(업 캐스팅)(다운 캐스팅)

자식에서 부모로 업 캐스팅을 하고 다시 자식으로 다운 캐스팅을 하는 경우는 위험하지 않다.

RTTI는 실행 중 포인터가 가리키는 객체의 타입을 알 수 있게 해 준다.

 

RTTI를 참조하는 주소를 가상 테이블 vtable의 첫 번째 요소인 type_info에 저장한다.

한마디로 모든 클래스마다 RTTI를 사용할 수 있는 것이 아닌 virtual function이 있는 클래스

즉, 다형성을 가진 클래스만 사용할 수 있다.

 

class A {
// public:
// 	virtual void hi() {
// 		std::cout << "hi" << std::endl;
// 	}
};

class B : public A {
};

int main(void) {
	A * a;
	B * b;

	a = dynamic_cast<A *>(b);
        // 오류
	b = dynamic_cast<B *>(a);
        //오류
	b = dynamic_cast<B *>(a);
}

위의 코드와 같이 A 클래스의 가상 함수를 없애면 다운 캐스팅과 (자식 -> 부모 -> 자식)의 다운 캐스팅이 안된다.

A 클래스의 virtual 함수를 만들면 다운 캐스팅이 가능해진다.

 

dynamic_cast는 reference, pointer 두 가지의 타입만 허용한다.

reference에서 타입 오류가 났을 때는 bad_cast 예외를 throw 한다.

pointer에서 타입 오류가 났을 때는 NULL를 리턴한다. bad_cast 예외를 throw 한다.

 

3. const_cast

const_cast이 const로 선언된 상수의 값을 변경해주는 것이라고 착각한다.하지만 밑의 코드와 같이 실제로 const의 값을 변경해보려고 하면 오류가 난다.

 

int main(void) {
	const int a = 4;
	a = *const_cast<int *>(a);
}

const_cast는 상수의 값을 변경하는 것이 아니라 const로 선언하지 않는 변수를 가리키는 pointer나 reference에 대해 const의 속성을 없애 값을 대입할 수 있게 해 준다.

 

밑의 코드에서 주석 처리된 코드는 오류가 난다. const int *의 값을 int * 값에 대입하려고 했기 때문이다.

const_cast를 통해 const int *의 값을 int *의 값으로 변경해서 b에 대입해줬다.

 

int main(void) {
	const int a = 4;
	int *b;
	// b = &a;
	b = const_cast<int *>(&a);
	std::cout << &a << ", " << b << std::endl;
}

 

4. reinterpret_cast (value ot pointer, pointer to value O!)

static_cast는 value to pointer는 가능했지만, pointer to value는 막아놨었다.

둘 다 사용할 수 있게 해주는 타입 변환 연산자가 reunterpret_cast이다.

 

int serialize(Data* ptr) {
    return reinterpret_cast<int>(ptr);
}

Data* deserialize(int raw) {
    return reinterpret_cast<Data*>(raw);
}

 

728x90

댓글