본문 바로가기
프로그래밍/More Effective C++

항목2. 가능한 C++ 스타일의 캐스트를 즐겨 쓰자.

by 리뷰하는 (게임)프로그래머_리프TV 2011. 4. 22.




먼저 static_cast, const_cast 부터 알아 보자.

#include <iostream>

using namespace std;

void main()
{
	int scast_number1 = 3;
	int scast_number2 = 2;

	// static_cast
	// 가장 많이 사용하는 cast라고 보면 되겠다. 
	// 가장 기본적인 형변환
	// 기존의 단순한 (float)number1 대신
	float fnumber = static_cast<float>(scast_number1) / scast_number2;
	// 다음 처럼 변형되었다고 보면 되겠다.
	cout << fnumber << endl;	// 1.5출력

	// const_cast
	// 쉽게 보면 const로 선언된 변수를 때어 버리고 싶을때 쓴다고 할 수 있다.
	const int ccast_number1 = 10;
	int* pi;

//	pi = ccast_number1;		// const이기 때문에 안된다.
//	pi = (int*)&ccast_number1;		// 기존의 c스타일
	pi = const_cast<int*>(&ccast_number1);	// c++ 스타일

	*pi = 100;

	// pi를 출력하면 100이 나오지만
	cout << *pi << endl;
	// ccast_number1 을 출력하면 그대로 10이 나온다.
	cout << ccast_number1 << endl;

	// 하지만 주소값은 둘다 똑같은데..?
	cout << pi << endl;
	cout << &ccast_number1 << endl;
	
	// 여기서 신기한 점은, 출력시 기존의 값(10)이 나온다는 것이다.
	// 다시 말해 cast를 통해 const를 제거 하더라도, 
	// 기존의 값을 변경했을 때 실질적으로 변경이 되지 않았다는 점이다.
	// 당장은 항목2에서 이 부분을 언급하지는 않았지만, 
	// 추후 좀더 자세히 알아보아야 할 필요는 있다.

	// 하지만 가장 좋은 방법은 const를 사용하는 것은
	// 말 그대로 상수화 되있다고 생각하고 저런 const_cast를 지양 하는것이 가장 좋은 방법이 아닐까?
}​



다음은, dynamic_cast!!

#include <iostream>

using namespace std;

class AAA
{
public:
	virtual void abc(){}
};

class BBB : public AAA
{
};

void updateAAA( AAA* aaa )
{
	cout << "AAA::update" << endl;
}

void updateBBB( BBB* bbb )
{
	cout << "BBB::update" << endl;
}

void main()
{
	AAA* paaa = new AAA;
	BBB* pbbb = new BBB;
	// dynamic_cast
	// 기본 클래스의 객체에 대한 포인터나 참조자의 타입을 파생 클래스, 
	// 형제 클래스의 타입으로 변환해 준다.

	// 여기까지는 그냥 기본이다.
	updateAAA( paaa );
	updateBBB( pbbb );

	// 이 부분이 고민해 보아야 할 부분인데
	updateAAA( pbbb );	// 컴파일상 문제는 없다.
	//updateBBB( a );	// 문제 발생
	updateBBB( dynamic_cast<BBB*>(paaa) );	// dynamic_cast를 사용

	// 사실 예제를 위한 억지 cast사용이라 추천하지는 않지만
	// 구지 이유를 달자면, 다형성에 따라
	// AAA*에 pbbb를 넣어도 사실상 문제가 생기진 않는다.
	// 디자인 패턴적인 부분을 보아도 자주 사용하기도 하고..
	// 하지만 BBB*에 paaa를 넣는다는것은 가능하지 않는데,
	// 이처럼 캐스팅을 해주어 넣어 줄 수는 있다.
	// 이 부분에 대해서도 좀더 자세히 파고 들어가긴 이른거 같다.
	// 사용을 어떤식으로 하는 것에 대해서만 기억하는 것으로 하자.

	// 출력은 당연히
	// AAA::update
	// BBB::update
	// AAA::update
	// BBB::update
}​


마지막으로 reinterpret_cast이다.

#include <iostream>

using namespace std;

// 함수 포인터 생성
typedef void (*FuncPtr)();

// 함수 포인터 10개를 담기 위한 함수 포인터 배열
FuncPtr funcPtrArray[10];

// int형 반환을 하는 함수에 포인터를
// void형 함수 포인터에 담고 싶은것이다.
int doSomething(){ return 0; }

void main()
{
	// reinterpret_cast
	// 마지막 캐스팅은 함수 포인터에 대한 캐스팅이다.
	// 먼저 예제에는 이런식으로 나와있다.

	// 당연히 void형 함수 포인터이기 때문에
	// int형을 반환하는 함수 포인터를 넣어 싶어도
	// funcPtrArray는 버틸 수가 없다.

	//funcPtrArray[0] = &doSomething;	// 당연히 안된다.
	
	funcPtrArray[0] = reinterpret_cast<FuncPtr>(&doSomething);

	// 하지만 함수 포인터를 사용하는 부분에 대해서 조금더 고급적인 기법은 
	// 후반에 나온다고 하니, 일단은 저렇게 "변환 할 수 있다는 것만"알아 두는것이
	// 현재로써는 가장 나을 것이다. 
}​


사실 별거 아닌 부분들일 지도 모른다.
쉽게 생각해서 자신없으면 cast하지 않고, 있는 그대로만 잘 사용해도 왠만한 코드는 다 짤 수 있을 것이다.
하지만 알고 있는것과 모르는 것,
모르기에 사용하지 못하는것과, 알면서 사용하지 않는것은, 분명 차이가 있을 것이다.
cast부분에 대해서는 좀더 활용성을 찾아 보아야 할 것 같고,
이런 부분들은 추후 디자인 패턴에 대해 포스팅 할때 좀더 명확하게 집고 넘어 가려고 한다.
마지막으로 항목2 초반에 저자가 말하길, goto문과 같이 써서는 안되는 1급 기피대상이 cast(형변환) 이라고 한다.
하지만 무작정 안쓰는게 좋다기 보단, 적절한 상황에 적절하게...; 사용한다면 분명 안쓰는것 보단 나을 것이라 생각한다.
(애초에 사용해서는 안되는 것이라면 지원해 주는것 자체가 이상하지 않은가?)
이제 막 포스팅을 시작한 만큼, 더 많은 준비를 통해 좋은 발전의 결과를 내기 위해 노력할 생각이다.