공부 중 메모/수업 예제 (KGCA)

Cpp cast연산자들 예제

라이피 (Lypi) 2018. 7. 17. 22:08
반응형

출처 : KGCA 게임 아카데미(http://www.kgcaschool.com/). 수업 예제 파일


참고 : 


1) bad_cast 예외

2) using과 typedef

3) using namespace ###; 는 취소할 수 없다. 그래서 using문이 작동하는 범위를 정해버림(..)

4) __int64 == long long  참고



header.h

#pragma once


#include <iostream>
using std::cout;
using std::endl;

dynamic_cast.h

#pragma once
#include "header.h"

namespace dyn
{
	//dynamic_cast는 vtable을 사용하기 떄문에 반드시 virtual 함수가 있어야 작동한다.

	class dA {
	public:
		virtual void test() {
			cout << "test in A" << endl;
		}
	};

	//상속시 접근지정자를 생략하면 class는 private로, struct와 union은 public으로 상속된다.
	class dB : public dA {
	public:
		virtual void test() {
			cout << "test in B" << endl;
		}

		void test2() {
			cout << "test2 in B" << endl;
		}

	};


	class dC : public dB {
	public:
		virtual void test() {
			cout << "test in C" << endl;
		}

		void test2() {
			cout << "test2 in C" << endl;
		}

	};

	//부모 클래스의 레퍼런스를 매개변수로 받는 함수
	void Globaltest(dA& da)
	{
		try {
			//매개변수로 받은 a를 B&로 캐스팅
			dB& b = dynamic_cast<dB&>(da);
			cout << "Success cast to B " << endl;
		}

		//캐스팅 실패시 dynamic_cast연산자는 예외 클래스 std::bad_cast를 던진다.
		catch (std::bad_cast) {
			cout << "Can't cast to B" << endl;
		}
	}
}

static_cast.h

#pragma once

#include "header.h"

namespace sta
{

	using BYTE = unsigned char;
	// == typedef unsigned char BYTE ;

	//static_cast : 의미없는 형변환 방지



	class sA
	{
	public:
		virtual void Test() {}
	};

	class sB : public sA 
	{
		//내용이 없다(..)
	};
	
	class sD
	{
	public:
		float m_fValue;


		//operator sC()
		//{
		//	//멤버 초기화
		//	sD fRet = { static_cast<int>(m_fValue) };
		//	return fRet;
		//}
	};

	class sC
	{
	public:
		int m_iValue;

		operator sD()
		{
			//멤버 초기화
			sD fRet = { static_cast<float>(m_iValue) };
			return fRet;
		}
	};


	//함수들
	void func() {

		BYTE ch = 'a';
		int i = 75;
		float f = 2.5f;
		double db = 3.14;

		cout << ch << " " << i << " " << f << " " << db << endl;

		BYTE chi = static_cast<BYTE>(i);    //int to BYTE(unsigned char);
		int ich = static_cast<int>(ch);    //BYTE(unsigned char) to int
		float fdb = static_cast<float>(db);   //double to float;
		double dbf = static_cast<double>(f); //float to double;

		cout << chi << " " << ich << " " << fdb << " " << dbf << endl;

		//이런식의 형변환은 컴파일러가 방법을 모르므로 불가능하다. 
		//sA aaa = static_cast<sA>(ch);
	}

	void func(sA* pa, sB* pb)
	{
		//sA(부모)의 포인터를 sB(자식)의 포인터로 형변환 시도
		//안전하지 않다. (unsafe conversion)
		sB* pb1 = dynamic_cast<sB*>(pa);
		sB* pb2 = static_cast<sB*>(pa);

		if (pb1 == nullptr) {
			cout << "pb1 == nullptr 캐스팅 실패" << endl;
		}
		else {
			cout << "pb1 != nullptr 캐스팅 성공" << endl;
		}

		if (pb2) {
			cout << "pb2 != nullptr 캐스팅 성공" << endl;
		}
		else {
			cout << "pb2 == nullptr 캐스팅 실패" << endl;
		}

		//sB(자식)의 포인터를 sA(부모)의 포인터로 형변환 시도
		//안전하다.      (safe conversion);
		sA* pa1 = dynamic_cast<sA*>(pb);
		sA* pa2 = static_cast<sA*>(pb);

		if (pa1 == nullptr) {
			cout << "pa1 == nullptr 캐스팅 실패" << endl;
		}
		else {
			cout << "pa1 != nullptr 캐스팅 성공" << endl;
		}

		if (pa2) {
			cout << "pa2 != nullptr 캐스팅 성공" << endl;
		}
		else {
			cout << "pa2 == nullptr 캐스팅 실패" << endl;
		}
	}

	void func(sC& pc, sD& pd)
	{
		//상속 관계가 아닌 클래스끼리의 형변환은 Dynamic_cast가 허락하지 않는다.	
		// sC* pc1 = dynamic_cast<sC*>(pd);
		// sD* pd1 = dynamic_cast<sD*>(pc);

		//static_cast는 컴파일러가 형변환 방법을 알면 변환해준다.
		//그러나 컴파일러가 형변환 방법을 모르면 알려줘야 한다.
		//sC pc2 = static_cast<sC>(pd); //형변환 방법 지정 안함
		sD pd2 = static_cast<sD>(pc); //형변환 방법 지정 함

		cout << "pc.m_iValue : " << pc.m_iValue << endl;
		cout << "pd.m_fValue : " << pd.m_fValue << endl;
		cout << "pd2.m_fValue : " << pd2.m_fValue << endl;
		
	}
}

const_cast.h

#pragma once
#include "header.h"

namespace con
{
	class cc_test {
		      int number;
		const int cn;

	public:
		void       setNumber(int n);
		void       printNumber() const;
		const int* GetConst();
		int*       GetNumberPointer();

		//const 멤버 변수가 있으면 기본 생성자가 추가되지 않는다.
		//생성자에서 const 멤버 변수를 초기화해야하기 때문이다.
		cc_test() : cn(0) {}
		~cc_test() {}
	};

	void       cc_test::setNumber(int n)
	{
		
		//const_cast<int>cn = n;
		//const_cast의 형식은 개체 형식에 대한 포인터, 참조 또는 멤버 포인터여야 합니다.

		number = n;
	}

	//멤버함수에 const가 붙으면 그 함수 내에서는 클래스 자체가 const class로 취급된다.
	void       cc_test::printNumber() const
	{
		//const 함수에서 멤버 변수의 값을 변경하기(..)
		cout << "\nBefore : " << number;
		const_cast<cc_test *>(this)->number--;
		cout << "\nAfter : " << number;
	}

	const int* cc_test::GetConst()
	{
		return &number;
	}

	int*       cc_test::GetNumberPointer()
	{
		return &number;
	}

}

reinterpret_cast.h

#pragma once
#include "header.h"

namespace rei
{
	//reinterpret_cast 강제 형변환에 사용.
	//보통 포인터->데이터, 포인터->포인터 형변환에 사용된다.

	unsigned short hash_1(void* p)
	{
		//주소를 정수로 변환.
		unsigned int val = reinterpret_cast<unsigned int>(p);
		return (unsigned short) (val ^ (val >> 16));
	}

	int g_iArray[10][10];

	unsigned short hash_2(void* p)
	{
		unsigned short val = reinterpret_cast<unsigned short>(p);
		return val % 10;
	}

	void set(int iRow, int iValue)
	{
		for (int iCnt = 0; iCnt < 10; iCnt++) {
			if (g_iArray[iRow][iCnt] <= 0) {
				g_iArray[iRow][iCnt] = iValue;
				break;
			}
		}
	}

	int get(int iRow, int iValue)
	{
		for (int iCnt = 0; iCnt < 10; iCnt++) {
			if (g_iArray[iRow][iCnt] == iValue) {
				return g_iArray[iRow][iCnt];	
			}
		}
		return -1;
	}

}

cast_main.cpp

#include "dynamic_cast.h"
#include "static_cast.h"
#include "const_cast.h"
#include "reinterpret_cast.h"

int main()
{

#pragma region dynamic_cast
	{
#define define_dynamic
#ifdef define_dynamic
		using namespace dyn;
#endif
		cout << "dynamic_cast Start" << endl;

		// 부모 => 자식 (상속관계)
		// dA => dB => dC

		//자식의 자식을 동적할당
		dA* pa1 = new dC;  
		//가상 함수이므로 실제 자식의 것으로 실행됨.
		pa1->test(); 

		//자식을 동적할당
		dA* pa2 = new dB;  
		//부모에는 test2 함수가 없으므로 호출할 수 없다.
		//pa2->test2();


		//pa1에 들어있는 메모리는 C
		//그러므로 B(부모)의 포인터로 캐스팅 가능
		dB* pb = dynamic_cast<dB*>(pa1); 
		if (pb) { // == if(pb != nullptr) 
			pb->test();  //가상함수인 경우      : 메모리의 함수(class C의 함수) 호출
			pb->test2(); //가상함수가 아닌 경우 : 포인터의 함수(class B의 함수) 호출
		}

		//pa2에 들어있는 메모리는 B
		//그러므로 C(자식)의 포인터로 캐스팅 불가능
		dC* pc = dynamic_cast<dC*>(pa2); 
		if (pc != nullptr) { // == if(pc)  
			pc->test();
			pc->test2();
		}

		//A(부모)는 B(자식)을 알고 있지 않기 때문에 실패.
		dA AonStack;
		Globaltest(AonStack);

		//B(자신)은 B(자신)을 알고 있으므로 당연히 가능.
		dB BonStack;
		Globaltest(BonStack);

		//C(자식)은 B(부모)를 알고 있으므로 가능.
		dC ConStack;
		Globaltest(ConStack);

		cout << "dynamic_cast End" << endl << endl;

#undef define_dynamic
	}
#pragma endregion

#pragma region static_cast
	{
#define define_static
#ifdef define_static
		using namespace sta;
#endif
		cout << "static_cast Start" << endl;

		func();

		sA a;
		sB b;
		func(&a, &b);


		sC c;
		sD d;
		c.m_iValue = 99;
		d.m_fValue = 0.99f;

		func(c, d);

		cout << "static_cast End" << endl << endl;

#undef define_static
	}
#pragma endregion

#pragma region const_cast
	{
#define define_const
#ifdef define_const
	using namespace con;
#endif

	cout << "const_cast Start" << endl;

	cc_test X;
	X.setNumber(8);
	X.printNumber();

	//1 
	//int* iValue = X.GetConst();
	//반환값이 const인 함수는 const 변수로 받아야 함.
	const int* iValue = X.GetConst();
	//*iValue = 20; //상수 에러

	//2
	int* iValue2 = const_cast<int*>(X.GetConst());
	*iValue2 = 20;
	X.printNumber();

	//이렇게 멤버 변수를 바꿀수도 있구나(...)
	int* iValue3 = X.GetNumberPointer();
	*iValue3 = 30;
	X.printNumber();

	cout << "const_cast End" << endl << endl;


#undef define_const
	}
#pragma endregion

#pragma region reinterpret_cast
	{
#define define_reinterpret
#ifdef define_reinterpret
		using namespace rei;
#endif
		cout << "reinterpret_cast Start" << endl;

		//두 개의 값이 동일한 경우 주소를 사용하여 해시값을 얻을 수 있다.
		//주소를 인덱스에 매핑하는 해시함수에서 reinterpret_cast를 유용하게 사용할 수 있다.
		int a[3] = { 1,2,3 };
		for (int iCnt = 0; iCnt < 3; iCnt++) {
			cout << a[iCnt] << " " << hash_1(a + iCnt) << endl;
		}

		int iArray[100];

		//이부분을 왜 이렇게 해놨는지 모르겠음...(...)
		//여기서부터
		for (int iCnt = 0; iCnt < 10; iCnt++) {
			iArray[iCnt] = iCnt + 1;
			unsigned short iHash = hash_2(&iArray[iCnt]);
			set(iHash, iArray[iCnt]);
		}

		for (int iCnt = 0; iCnt < 10; iCnt++) {
			unsigned short iHash = hash_2(&iArray[iCnt]);
			cout << get(iHash, iArray[iCnt]) << " " << endl;
		}
		//여기까지
		

		//주소값을 수치로 바꿔서 주소에 접근하기.
		__int64 aa = 7;

		__int64 aaa = reinterpret_cast<__int64>(&aa);
		__int64* pAAA;

		cout << aa << endl;

		pAAA = reinterpret_cast<__int64*>(aaa);
		*pAAA = 111;

		cout << aa << endl;

		cout << "reinterpret_cast End" << endl << endl;


#undef define_reinterpret
	}
#pragma endregion
}


반응형