반응형

범용성을 위해 Color클래스를 만들어봤다.

class ColorF
{
private:
	float rgba[4];
public:
	ColorF(float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 1.0f)
		: r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) // 참조 초기화
	{
		rgba[0] = r; rgba[1] = g; rgba[2] = b; rgba[3] = a;
	}
	// 아니 참조도 메모리 차지하는거 처음암 ㅁㅊ
	const float& r;
	const float& g;
	const float& b;
	const float& a;

	UINT32 GetRGB()
	{
		UINT8 r = static_cast<UINT8>(rgba[0] * 255.0f);
		UINT8 g = static_cast<UINT8>(rgba[1] * 255.0f);
		UINT8 b = static_cast<UINT8>(rgba[2] * 255.0f);
		return (r << 16) | (g << 8) | (b);
	}

	operator const float* () const { return rgba; }
	operator const Vector4() const { return Vector4(r, g, b, a); }
	ColorF& operator=(const ColorF& _val)
	{
		rgba[0] = _val.rgba[0];
		rgba[1] = _val.rgba[1];
		rgba[2] = _val.rgba[2];
		rgba[3] = _val.rgba[3];
		return *this;
	}
#ifdef _D2D1_H_
	operator D3DCOLORVALUE()
	{
		return D3DCOLORVALUE(r, g, b, a);
	}
#endif
};

 

처음 만들때 정말 바보같은 생각을 함. 답답해도 들어보삼

처음만들때 한생각 : float4개를 배열로 만든다음(배열은 연속적인 메모리니까?) r,g,b,a가 각각 배열을 가리키게 하면 메모리도 4X4 16바이트 쓸 수 있겠지? (참조는 메모리 안먹는줄 암)

 

근데 다 만들고 보니까 구조체가 48바이트를 잡아먹는거임. 뭐지? 하고 보니까 참조도 메모리를 차지함. 당연한건데 왜 그 생각을 못했지? (심지어 참조는 주소를 가지고있어야 해서 8바이트임ㅋㅋ)

 

그리고 어차피 float 배열로 안해도 구조체는 연속적인 메모리로 정리되기 때문에 그냥 r, g, b, a 각각 float로 만들면 됨.... 이러면 더 명확하고 간단한데.... 자꾸 생각에 잡아먹혀서 간단하게 못짠듯

 

다시 짜본 Color클래스

#pragma once

class ColorF
{
public:
	ColorF(float _r = 0.0f, float _g = 0.0f, float _b = 0.0f, float _a = 1.0f)
		: r(_r), g(_g), b(_b), a(_a) {}
public:
	float r, g, b, a;

	UINT32 GetRGB()
	{
		UINT8 r_ = static_cast<UINT8>(r * 255.0f);
		UINT8 g_ = static_cast<UINT8>(g * 255.0f);
		UINT8 b_ = static_cast<UINT8>(b * 255.0f);
		return (r_ << 16) | (g_ << 8) | (b_);
	}

	operator const float* () const { return &r; }
	operator const Vector4() const { return Vector4(r, g, b, a); }
#ifdef _D2D1_H_
	operator D3DCOLORVALUE()
	{
		return D3DCOLORVALUE(r, g, b, a);
	}
#endif
};

초 - 간 - 단

r주소 반환해주면 구조체는 연속적인 메모리로 정리되어있기때문에 rgba에 접근할 수 있다.

의도대로 16바이트로 나옴

굿ㅋㅋ

 

생각에 잡아먹히지 말자...ㅇㅅㅇ

반응형
반응형

C++11 에서의 타입추론은 컴파일 시간에 변수의 자료형을 자동으로 추론해준다. 사실 코딩 표준에선 auto보다 실제 자료형을 명시하는것을 선호하지만, 잘만 쓰면 매우 유용한 기능인건 틀림이 없다 ㅇㅅㅇ

프로그래머라면 응당 어떻게 동작하는지 알아야 적재적소에 잘 써먹을  수 있다는 사실..... 타입추론에 관해서 주의할 점도 많으니까 오늘 알아봅시다 ㅋㅅㅋ


타입 추론이 무엇인가?

타입 추론은 컴파일러가 코드 내에서 변수나 표현식의 타입을 "추론"해서 직접 할당해주는 것을 의미하는데요?

이 덕분에 개발자가 직접 타입을 명시하지 않아도 되고, 코드가 간결해질 수 있읍니다.

그렇다면? 타입추론을 자기가 직접 할리는 없고, 해주는 친구들이 있지 않겠습니까?

그 친구들이 바로 auto키워드와 template문법입니다. 오늘은 그중에서 auto에 대하여 알아봅시다 ㅇㅅㅇ


auto가 뭔데 씹덕아

auto는 변수의 타입 (int, char 등)을 지정해줄때의 자료형 대신 auto를 붙여주면 컴파일러가 컴파일시간에 알아서 타입을 추론해주는 친구인데요?

어떤식으로 사용하는지 한번 볼까용

// 사용 예시 ()
double	x1 = 5.0;  // double 자료형
float	x2 = 5.0;  // float 자료형
auto	x3 = 5.0;  // auto의 추론 값 : double
auto	x4 = 5.f;  // auto의 추론 값 : float

이런식으로 사용할 수 있는 친구입니다 ㅇㅅㅇ 쉽지 아니한가

근데 여기서 촉이 좋으신 분들은 이미 느꼈을텐데요?

5.0을 float에 넣을 수는 있지만 auto에 5.0을 넣으면 double이 추론됩니다. 물론 float를 사용할 때는 숫자 뒤에 "f"를 명시적으로 붙여야 되는건 맞습니다.. 그만큼 auto를 쓸 때는 사용자가 컴파일러에게 타입 추론에 대한 단서를 명확히 줘야 된다는 점입니다. 아래에서 자세히 다뤄볼게여 ㅇㅅㅇ


본격적으로 auto에 대하여 알아보자

여러 상황에 대하여 다뤄볼건데여 통수맞는 상황이 생각보다 많아서 글이 길어질 것 같습니다. ㅠ

같이 추론 값을 알아볼까요?


1. 타입이 다른 연산에 대하여

다음 타입의 추론 값을 예상해보시죠 ㅇㅅㅇ

auto	y1 = 1 + 1.0f;	  // ??
auto    y2 = 1.0 + 1.0f;  // ??

 

정답 및 설명

더보기

y1 = float

y2 = double

 

컴파일러가 타입을 추론할 때 축소 변환을 하지 않는 것을 원칙으로 연산하기 때문인데요?

축소 변환이 무엇인지 대강 말씀드리자면,

(int)1 + (float)1.5f 를 더할 때 컴파일러는 자료형을 암시적으로 통일해서 연산합니다, float를 int로 바꾼다면, 1.5는 1이 되어버립니다. 이 과정에서 원래 수인 1.5를 잃어버리게 되어서 결과적으로 1 + 1 = 2가 되어버리는데요, 반대로 int를 float로 바꾸면 1.0 + 1.5를 연산하여 2.5를 정상적으로 출력할 수 있습니다. float가 int로 변환하면서 값을 잃어버릴 수도 있을 때, 이를 축소 변환한다고 말합니다. 동일하게 float + double 연산도 double이 float에 비해 더 많은 소수점을 나타낼 수 있기 때문에 double로 캐스팅이 됩니다.

똑똑한 컴파일러는 축소 변환으로 인한 연산 미스를 미연에 방지하기 위해 확대 변환을 하여 연산을 합니다. 그래서 auto로 타입 추론시 저런 결과가 나오는 겁니다. ㅇㅅㅇ


2. 상수(const), 참조(&) 추론에 대하여

그럼 이건 어떨까요?

const int z1 = 1;	// const int 자료형을 선언
auto	  z2 = z1;	// 그럼 여기서 auto는 const int가 맞겠지?
z2 += 1;			// const값을 변경하는데 컴파일 에러가 안난다??
const int z3 = 1;	// const int 자료형을 선언
auto&	  z4 = z3;	// 무슨 자료형일까?
z4 += 1;			// 이 상황은 에러가 난다

이미 주석으로 결과는 말해드렸는데요?

z2와 z4가 주석의 의도와 다르게 타입 추론이 되었기 때문입니다.

z2와 z4의 타입은 어떻게 추론되었을까요?

 

정답 및 설명

더보기

z2 = int

z4 = const int&

 

z2는 const int, 즉 상수 값 1을 대입받아 z2 = 1과 같은 대입 연산이 이루어 졌습니다. 그 결과 int로 추론이 되었습니다. 때문에 z2 += 1 연산이 컴파일 에러없이 잘 동작합니다.

z4는 auto&, 즉 z3의 원본에 대한 값을 참조하므로 타입을 그대로 받아올 수 있었습니다. 그 결과 const int를 참조하기 때문에 const int&로 추론이 되었습니다. 때문에 z4 += 1 연산은 상수연산으로 인식되어 컴파일 에러가 납니다.


3. 포인터(*) 추론에 대하여

C++인데 포인터가 빠지면 섭섭하죠잉? 해당 코드의 추론값도 예상해 보시져 ㅇㅅㅇ

int w1 = 1;
auto w2  = &w1;  // w2의 자료형은?
auto* w3 = &w1;  // w3의 자료형은?

 

정답 및 설명

더보기

w2 = int*

w3 = int*

 

주소를 받았으니 당연히 int*인건 맞습니다. 하지만 w2, w3는 각각 auto, auto*인데 결과가 똑같이 나왔습니다.

이를 통해 컴파일러가 auto추론에서 포인터(*)를 자동으로 추론해주지만, 개발자가 명시적으로 포인터를 정의해줘도 된다는 것을 알았습니다!!

무조건 포인터를 쓸거라는 확신이 있으면 auto에 포인터를 명시해주는 습관을 들이는게 좋겠네용


4. 그외 잡다한 추론

이제 기본적인 추론 상황들은 다 본 것 같습니다...

좀 더 심화적으로 들어가보죠. 다음 상황들도 추론해 보세여 ㅇㅅㅇ

// 다음 변수의 타입을 직접 추론해보자 (안되는 것도 있다)
auto a1 { 1 };
auto a2 = { 1 };

auto b1 = 'abc';
auto b2 = "abc";

auto c1 = { 1,2,3,4 };
auto c2[4] = { 1,2,3,4 };

const int* d1 = 0;
auto       d2 = d1;

std::string e1 = "abc";
auto		e2 = e1.begin();

const int f1 = 1;
auto&&	  f2 = f1;

 

 

정답 및 간략한 설명

더보기

a1 = int

a2 = std::initializer_list<int>

 

b1 = int (잘못된 문자형 리터럴 사용. 멀티 캐릭터 리터럴(문자열 및 문자 리터럴(C++) | Microsoft Learn)로 간주해 int로 추론. but 옳지 않은 방식)

b2 = const char*

 

c1 =  std::initializer_list<int>

c2 = 컴파일 에러 (auto는 배열에 대해선 std::initializer_list<>로만 추론한다고 해요. 따라서 []배열선언이 불가능하다고 합니다?)

 

d2 = const int*

 

e2 = std::string::iterator

 

f2 = const int&


번외. 함수 포인터에 대한 추론 

void Func1(const char* _str)
{
	std::cout << _str << "ㅇㅅㅇ" << '\n';
}
int main()
{	
    // 함수 포인터를 쓸때 편하다!
	void (*funcPtr1)(const char*);
	auto funcPtr2 = Func1;  // = void (*funcPtr2)(const char*);
}

 

함수 포인터를 쓸때 정말 ㄹㅇ로 편해집니다? 


auto를 쓸 때 주의할 점

auto는 개발자가 제대로 모르고 남발하면 의도된 타입과 다르게 추론될 수 있다는 것을 알았는데요 ㅇㅅㅇ. 때문에 추론 규칙을 완벽히는 아니더라도 잘 숙지해서 쓰는 것도 중요합니다. 변수 위에 마우스 커서를 올리면 추론 값이 명시적으로 나오기 때문에 그걸 확인하는 것도 좋은 습관이 되겠습니다!

또한 auto를 쓰게 되면 코드 간결성은 늘어날 수 있지만, 코드 가독성을 해칠 수 있는데요 ㅇㅅㅇ. 명확한 자료형을 제시함으로써 코드를 읽을 때 어떤 값을 받는지 대충 예상할 수 있지만 auto를 쓰게 되면 확인하기가 힘들기 때문에 코드 가독성면에서 안좋다고 볼 수 있습니다...

마지막으로 auto는 컴파일 시간에 영향을 줍니다. 사실 뭐 엄청나게 컴파일 타임이 늘어나진 않지만, 타입이 복잡해질 경우 조금 영향을 미칠 수 있다고 하네요.


하 알아볼게 왤캐 많아 ㅇㅅㅇ

저는 개인적으로 auto를 비선호합니다(iterator정도는 가끔 씀). 사실 프로그래머라면 auto를 쓰지 않고도 어떤 타입을 써야되는지 알아야 할 필요가 있죠? 그럼에도 코드 간결성은 프로그래머에게 있어서 코스트라고 생각하기 때문에 나쁘게 생각하지는 않습니다. 적재적소에 잘 쓰면 좋은 키워드인건 맞다고 생각합니다 ㅇㅅㅇ

C++14에선 함수의 반환 값 추론도 있다고 하는데요? 그건 그만 알아보자.araboza

 

 

반응형
반응형

오늘은 복사생성자에 대해 알아보겠습니닷
이 글은 포인터를 잘 모르시면 이해가 안될 수도 있습니다.
바로 ㄱㄱ싱


해당 코드를 실행하면 에러가 납니다. 왜 그런지 한번 생각해보시죠 ㅇㅅㅇ
 

#include <iostream>

class MyClass
{
public:
	// 기본 생성자
	MyClass() : m_data(nullptr) { std::cout << "기본 생성자 호출 ㅇㅅㅇ" << '\n'; }
	// 파라미터 생성자
	MyClass(const char* _str) {
		int len = strlen(_str);
		m_data = new char[len + 1];
		strcpy_s(m_data, len + 1, _str);
		std::cout << "파라미터 생성자 호출 ㅇㅅㅇ" << '\n';
	}
	// 소멸자
	~MyClass()
	{
		delete[] m_data;
		std::cout << "소멸자 호출 ㅇㅅㅇ" << '\n';
	}
	void Show() { std::cout << m_data << '\n'; }
private:
	char* m_data;
};

int main()
{
	MyClass mc1("Hello");
	MyClass mc2 = mc1;
	MyClass mc3;
	mc3 = mc1;

	mc1.Show();
	mc2.Show();
	mc3.Show();

	return 0;
}

 
해당 코드의 문제점을 찾으셨나요?
문제를 30초만에 바로 찾으셨다면 당신은 코딩고수....
 


뭐가 문제인데?

해당 코드의 문제는 mc1, mc2, mc3 총 3개의 객체들의 멤버변수인 m_data가 같은 메모리를 참조하고 있다는 것입니다. 즉 객체만 다르고 m_data는 같은 포인터를 공유하고 있다는 것임니다.... 

이런 느낌 ㅇㅇ...(ㅋㅋ)

"니가 뭔데? 증거있음?" 이라고 하실까봐 위의 코드에서 m_data의 주소를 출력해 보겠읍니다..

// 출력 결과
파라미터 생성자 호출 ㅇㅅㅇ
기본 생성자 호출 ㅇㅅㅇ
Data : Hello, Address : 000001696D8337F0
Data : Hello, Address : 000001696D8337F0
Data : Hello, Address : 000001696D8337F0
소멸자 호출 ㅇㅅㅇ

말했듯이 같은 주소를 가리키고 있고, 소멸자도 전부 호출하지 못하며, 에러가 나고 꺼진 것을 볼 수 있습니다...
따라서 프로그램이 종료되기 직전 소멸자가 호출할 때, mc1의 소멸자가 호출되면서 m_data를 delete해주지만, mc2, mc3의 m_data도 같은 주소를 가리키기 때문에? mc1이 삭제된 직후 mc2, mc3가 삭제될때 이미 삭제된 메모리 삭제를 시도하기 때문에 터진답니다? ㅇㅅㅇ


어째서 같은 주소를 가리키는 것임?

그건 복사 생성자와 복사 대입 연산자의 결함때문인데요?
그것은 바로 사용자가 복사생성자를 명시적으로 정의하지 않으면 내부의 기본적으로 정의되어 있는 복사생성자를 호출하게 된답니다?

기본적으로 구현되어있는 생성자와 연산자들은 멤버를 각각 복사 대입하도록 구현되어 있기 때문인데요...

 

대체 어떻게 생겼길래?

복사 생성자의 내부 구현은 어떻게 되어있을까용

class MyClass {
    char* m_data;
};

// 기본 복사 생성자의 내부 동작
MyClass::MyClass(const MyClass& _other) 
{
    // 이러면 m_data의 주소 값이 대입되므로 같은 주소를 가리킨다.
    this->m_data = _other.m_data;
}

이런식으로 구현되어 있을거임(?) ㅇㅅㅇ

 

복사 대입 연산자도 이와 비슷한 맥락으로 이렇게 구현되어 있답니다?

class MyClass {
    char* m_data;
};

// 기본 복사 생성자의 내부 동작
MyClass& MyClass::operator=(const MyClass& _other) 
{
    if (this != &other) // 자기 자신을 대입하는지 검사
    {
         // 위와 동일. 이러면 m_data의 주소 값이 대입되므로 같은 주소를 가리킨다.
         this->m_data = _other.m_data;
    }
    return *this;
}

복사 생성자와 복사 대입 연산자는 대체 언제 호출됨?

바로 이때!

둘의 차이는 생성 당시에 (=)연산자를 붙였는가 안붙였는가의 차이인듯 합니다람쥐


그렇다면 이런 현상을 어떻게 방지할 수 있는데?

그 해답은 바로 복사 생성자와 복사 대입 연산자의 재정의입니닷

	// 복사 생성자 재정의
	MyClass(const MyClass& _other)
	{
		CopyData(_other);
	}
	// 복사 대입 연산자 재정의
	MyClass& operator=(const MyClass& _other)
	{
		// 복사 대상이 같으면 복사 ㄴㄴ
		if (this != &_other)
		{
			CopyData(_other);
		}
		return *this;
	}
	// 멤버의 메모리 할당 후에 대상 카피
	void CopyData(const MyClass& _other)
	{
		int len = strlen(_other.m_data);
		m_data = new char[len + 1];
		strcpy_s(m_data, len + 1, _other.m_data);
	}

저같은 경우에는 이런 식으로 재정의해봤습니다 ㅇㅅㅇ
 
그럼 이제 제대로 출력되겠지?
바로 출력을 해보자.

// 출력 결과
파라미터 생성자 호출 ㅇㅅㅇ
복사 생성자 호출 ㅇㅅㅇ
기본 생성자 호출 ㅇㅅㅇ
복사 대입 연산자 호출 ㅇㅅㅇ
Data : Hello, Address : 00000177881C3700
Data : Hello, Address : 00000177881C40B0
Data : Hello, Address : 00000177881C3750
소멸자 호출 ㅇㅅㅇ
소멸자 호출 ㅇㅅㅇ
소멸자 호출 ㅇㅅㅇ

3개의 데이터가 의도대로 다른 주소를 가리키고, 에러 없이 소멸자도 정상적으로 잘 호출되는 모습이쥬?


오늘도 제가 꿀팁을 알려드렸는데요?
좋으면 구독과 좋아요 알림설정까지 꾸욱 ㅇㅅㅇ

반응형
반응형

잠깐 알아가보기 전에 잡설을 해보겠다....

보고싶은 분은 접은글을 봐주시길....★

더보기

불과 몇달 전에 한 프로젝트 중에 이런 난잡한 코드를 썼다.

 

void InGameHumanBox::Update()
{
	if (human)
	{
		if (Utillity.CheckBaseClass<TutorialHuman>(human))
		{
			m_name->SetText(human->gameObject->GetName());
			m_profile->index = (int)human->GetHorrorLV();
			m_gauge->index = ((int)human->GetHorrorScore() / 10) + 25;
		}
		if (Utillity.CheckBaseClass<Woman01>(human))
		{
			m_name->SetText(human->gameObject->GetName());
			m_profile->index = (int)human->GetHorrorLV();
			m_gauge->index = ((int)human->GetHorrorScore() / 10) + 25;
		}
		if (Utillity.CheckBaseClass<Woman01>(human))
		{
			m_name->SetText(human->gameObject->GetName());
			m_profile->index = (int)human->GetHorrorLV();
			m_gauge->index = ((int)human->GetHorrorScore()/10) + 25;
		}
		if (Utillity.CheckBaseClass<Woman02>(human))
		{
			m_name->SetText(human->gameObject->GetName());
			m_profile->index = (int)human->GetHorrorLV() + 8;
			m_gauge->index = ((int)human->GetHorrorScore() / 10) + 25;
		}
		if (Utillity.CheckBaseClass<Woman03>(human))
		{
			m_name->SetText(human->gameObject->GetName());
			m_profile->index = (int)human->GetHorrorLV() + 16;
			m_gauge->index = ((int)human->GetHorrorScore() / 10) + 25;
		}
		if (Utillity.CheckBaseClass<Man01>(human))
		{
			m_name->SetText(human->gameObject->GetName());
			m_profile->index = (int)human->GetHorrorLV() + 4;
			m_gauge->index = ((int)human->GetHorrorScore() / 10) + 25;
		}
		if (Utillity.CheckBaseClass<Man02>(human))
		{
			m_name->SetText(human->gameObject->GetName());
			m_profile->index = (int)human->GetHorrorLV() + 12;
			m_gauge->index = ((int)human->GetHorrorScore() / 10) + 25;
		}
		if (Utillity.CheckBaseClass<Man03>(human))
		{
			m_name->SetText(human->gameObject->GetName());
			m_profile->index = (int)human->GetHorrorLV() + 20;
			m_gauge->index = ((int)human->GetHorrorScore() / 10) + 25;
		}
	}
}

 

CheckBaseClass()가 무슨 메소드냐면....

대상이 P클래스를 상속받았으면 true를 반환

그렇다 그냥 인자로 받은 human친구가 템플릿 인자로 받은 클래스를 상속받았으면 true를 반환해주는 클래스다.

하지만 이런 삽질을 간단하게 만들어주는 편한 메소드를 찾아냈다...!

is_base_of 클래스 | Microsoft Learn

 

is_base_of 클래스

자세한 정보: is_base_of 클래스

learn.microsoft.com

오늘은 이 클래스에 대해 소개해보겠슴덩

template <class Base, class Derived>
struct is_base_of;

말그대로 Derived클래스가 Base클래스를 상속받은건지 알려주는 클래스인데요?

이걸로 편하게 상속받은 클래스를 확인해보시죠 ㅇㅅㅇ


근데 이렇게만 쓰면 섭섭하죠?

특정 객체와 클래스를 인자로 넘겨주면 해당 클래스를 상속받은건지 확인해주는 메소드를 작성해보겠습니다.

#include "pch.h"
#include <type_traits>

struct Human{};
struct Human01 : public Human{};
struct Human02 : public Human{};
struct Human03 : public Human{};

// P클래스를 상속받은 클래스면 true를 반환한다.
template <typename P, typename C>
bool CheckBaseClass(C _dest) {
    return std::is_base_of<P, C>::value;
}

int main()
{
    Human   _human;
    Human01 _human01;
    Human02 _human02;
    Human03 _human03;
    std::cout << std::boolalpha;
    std::cout << "human01 is base of Human : " << CheckBaseClass<Human>(_human01) << '\n';
    std::cout << "human02 is base of Human : " << CheckBaseClass<Human>(_human02) << '\n';
    std::cout << "human03 is base of Human : " << CheckBaseClass<Human>(_human03) << '\n';
    std::cout << "=================================" << '\n';
    std::cout << "human01 is base of Human01 : " << CheckBaseClass<Human01>(_human01) << '\n';
    std::cout << "human02 is base of Human01 : " << CheckBaseClass<Human01>(_human02) << '\n';
    std::cout << "human03 is base of Human01 : " << CheckBaseClass<Human01>(_human03) << '\n';

    return (0);
}

출력결과

human01 is base of Human : true
human02 is base of Human : true
human03 is base of Human : true
=================================
human01 is base of Human01 : true
human02 is base of Human01 : false
human03 is base of Human01 : false

그럼 여기서 궁금한 점 하나...

dynamic_cast와 is_base_of 와 성능차이는 얼마나 날까요? 성능차이가 나지 않으면 굳이 쓸 이유가 없는 코드죠?

그래서 코드로 확인을 해봤습니다...

더보기
#include "pch.h"
#include <iostream>
#include <type_traits>
#include <chrono>

struct Human {};
struct Human01 : public Human {};
struct Human02 : public Human {};
struct Human03 : public Human {};

template <typename P, typename C>
bool CheckBaseClass(C* _dest) {
    return std::is_base_of<P, C>::value;
}

template <typename P, typename C>
bool CheckDynamicCast(C* _dest) {
    P* temp = dynamic_cast<P*>(_dest);
    return temp != nullptr;
}

int main()
{
    Human01* _human01 = new Human01();
    Human02* _human02 = new Human02();
    Human03* _human03 = new Human03();

    const int iterations = 100000000;  // 반복 횟수 설정

    // std::is_base_of 시간 측정
    auto startIsBaseOf = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        CheckBaseClass<Human>(_human01);
    }
    auto endIsBaseOf = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsedIsBaseOf = endIsBaseOf - startIsBaseOf;
    std::cout << "std::is_base_of time: " << elapsedIsBaseOf.count() << " seconds\n";

    // dynamic_cast 시간 측정
    auto startDynamicCast = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        CheckDynamicCast<Human>(_human01);
    }
    auto endDynamicCast = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> elapsedDynamicCast = endDynamicCast - startDynamicCast;
    std::cout << "dynamic_cast time: " << elapsedDynamicCast.count() << " seconds\n";

    delete _human01;
    delete _human02;
    delete _human03;

    return 0;
}

결과 (4번 시행)

1트
2트
3트
4트

2트때 차이가 너무 안나서 엥? 했다가 여러번 해보니까 나름 격차가 좀 있네요 ㅇㅅㅇ


그래서 어따씀?

에 대한 의문이면 게임엔진으로 따져보면 AddComponent같은 곳에 쓰지 않을까요?

 

오늘도 개꿀팁(?)을 알려드렸는데요?

이상으로 글을 마쳐보겠습니다. 감사합니다?

 

반응형

+ Recent posts