Win32 API/기초

Win32 API 기초 : Resource Manager

시플마 2024. 7. 19. 21:35

플레이어 오브젝트에 텍스처를 적용하고 싶다면

플레이어 오브젝트 내에서 텍스처를 저장하는 멤버를

만들고 그 멤버를 통해 직접 텍스처를 불러왔습니다.

 

그런데 이런 구조로 텍스처를 적용하면 문제가 있습니다.

 

만약 똑같은 모습을 한 몬스터를 여러 마리 출력하게 되면

여러 텍스처가 그려지고 각 오브젝트마다 해당하는 텍스처를

가리키게 되죠.

 

이렇게 할 필요 없이 어차피 모두 같은 텍스처를 적용할 것이라면

텍스처 하나만 불러와서 모든 몬스터 오브젝트가 하나의

텍스처만을 가리키게 하면 되겠죠.

 

 

이를 위해서 리소스를 관리하는 매니저 역할을 하는

CResMgr 클래스를 만들었습니다.

 

매니저이기 때문에 프로그램 내에서 

하나만 존재해야 하므로 싱글톤 객체로 생성하게 됩니다.

 

리로스 매니저는 멤버로 map형 m_mapTexture를 갖고 있습니다.

 

map 자료형은 균형 이진 탐색 트리로, 노드로 구성되어 있으며 

각 노드에는 키-값과 특정 데이터가 들어가 있습니다.

 

m_mapTexture 같은 경우에는 키-값의 자료형으로

문자열을 채택하였고, 각 노드에는 CTexture형 포인터가

들어가 있습니다.

 

멤버 함수 LordTexture와 FindTexture를 갖고 있습니다.

 

먼저 LordTexture 함수부터 살펴보겠습니다.

 

해당 함수는 텍스처의 키-값 역할을 해 줄 문자열 _strKey와

텍스처의 상대 경로를 담고 있는 _strRelativePath를 인자로 받습니다.

 

27번째 줄에서 FindTexture 함수의 반환값을

포인터 textureCheck에 저장하는 모습이 보이죠?

 

FindTexture 함수는 멤버 m_mapTexture와

같은 형태의 iterator를 생성합니다.

 

인자로 받은 텍스처의 키-값 역할을 해 줄 문자열 

_strKey를 통해서 m_mapTexture이 갖고 있는 노드 중

해당 문자열과 키-값이 일치하는 노드를 가리키는

iterator를 반환합니다.

 

이때 find 함수를 통해 트리 내에서

일치하는 노드를 발견하지 못하면 

end iterator를 반환하죠.

 

그래서 만약 iter에 대입된 iterator가

end iterator라면 nullptr을 반환하고,

그것이 아니라면 트리 내에서 일치하는

노드를 발견했다는 의미이므로 

iter의 second를 반환합니다.

( first는 키-값이고 second는 노드가

지닌 실질적인 데이터입니다. )

 

 

다시 LordTexture 함수로 돌아와서 

 

FindTexture 함수의 반환값을 

textureCheck에 대입하고 

textureCheck의 값이 nullptr이 아닌 경우,

즉 멤버 m_mapTexture에 LordTexture 함수가

인자로 받은 키-값 문자열과 키-값이 일치하는

노드( 텍스처 )가 존재한다는 의미이므로

해당 텍스처를 반환하면 되겠죠.

 

 

멤버 m_mapTexture에 찾는 텍스처가 없다면

경로 매니저인 CPathMgr가 갖고 있는

리소스 파일까지의 절대 경로를 strFilepath에 저장합니다.

 

그리고 LordTexture 함수의 두 번째 인자( 상대 경로 )를

절대 경로가 저장된 strFilepath에 이어 붙입니다.

 

CTexture형 포인터 pTex를 생성하고 

pTex가 동적 할당한 공간을 가리키도록 합니다.

 

동적 할당한 공간을 CTexture 객체로 보고

Lord 함수를 호출하여 strFilepath 경로에 있는

파일을 불러옵니다.

 

그리고 텍스처 객체는 

LordTexture 함수가 인자로 받은 키-값을

SetKey 함수를 통해 자신의 키-값으로서 저장합니다.

 

LordTexture 함수가 인자로 받은 상대 경로를

SetPath 함수를 통해 자신의 경로로 저장합니다.

 

 

42번째 줄에 있는 insert 함수를 통해

LordTexture 함수가 인자로 받은 키-값을

키-값으로 하고 pTex가 가리키는 동적 할당 공간에 존재하는

텍스처를 실질적인 데이터로 하는 노드를 만들어 

멤버 m_mapTexture에 삽입합니다.

 

 

그리고 pTex를 반환합니다.

 

 


 

 

LordTexture 함수가 반환하는

텍스처를 가리키는 포인터를 

 

플레이어 오브젝트에 텍스처를 입히기 위해 

존재하는 멤버 m_pTexture에 저장합니다.

 

 

이렇게 되면 멤버 m_pTexture가 가리키는

텍스처를 통해 플레이어가 렌더링된 위치에

텍스처를 함께 그릴 수 있게 됩니다.

 

 


 

 

 

기존에는 플레이어가 직접 텍스처를 

불러와서 갖고 있었기 때문에 플레이어 오브젝트

소멸자 부분에서 메모리 해제를 진행하였지만,

이제는 리소스를 관리하는 매니저에 의해 

텍스처를 불러오고 관리하고 있으므로 

아래 코드처럼

 

CResMgr 객체가 소멸될 때

메모리 해제를 진행합니다.

 

각종 리소스를 트리 형식으로 저장하고 있는

멤버 m_mapTexture의 첫 노드를 iterator인 iter가

가리키도록 합니다.

 

그리고 for 문을 통해 iter가 end iterator에 도달할 때까지

트리에 존재하는 노드, 즉 리소스들에 하나씩 접근하여

노드가 실질적으로 가리키는 텍스처를 delete 키워드를 통해

제거합니다.

 

 

이제 텍스처를 관리하는 리소스 매니저가 따로 존재하니

텍스처 관련 객체가 리소스 매니저 클래스 외

다른 곳에서 생성할 수 있는 것을 막아야 합니다.

 

그래서 위 코드처럼

CTexture 클래스의 생성자와 소멸자를

private로 선언하고 friend 키워드를 통해

CResMgr 클래스에서만 CTexture의 생성과

소멸이 가능하도록 하였습니다.

 

 

 


 

 

 

 

동적 할당한 공간이 필요 없음에도 메모리를

해제해 주지 않으면 계속 메모리를 점유하게 되어

메모리가 낭비되는, 메모리 누수가 발생하죠.

 

이러한 메모리 누수를 체크할 수 있는 함수가 존재하는데,

 

29번째 줄에 있는 _CrtSetDbgFlag 함수입니다.

 

지금 현재 32번째 줄에서 의미 없는 동적 할당을 진행하고

해제를 해 주지 않고 있습니다.

 

이 상태에서 디버깅 모드로 실행하고 

프로그램을 종료해 보면,

( 이때 디버깅 중지가 아닌 윈도우 창을 꺼서

프로그램을 종료해야 합니다. )

 

출력창에 위와 같은 문구가 출력되는 것을

확인할 수가 있습니다.

 

4 bytes로 할당된 메모리의 누수가 발생했다고 뜨네요.

 

이때 아래와 같이

 

출력 문구 맨 앞에 있는 숫자를

_CrtSetBreakAlloc 함수의 인자로 넘기고 

다시 디버깅 모드로 실행하면,

바로 프로그램이 중단이 됩니다.

 

그 상태에서 호출 스택 창을 탐색해 보면

 

누수가 발생한 지점으로 화면이 이동하여

 

어디서 메모리 누수가 발생했는지 

알아낼 수 있습니다.

 

 

메모리 누수 체크가 끝났다면 반드시

_CrtSetBreakAlloc 함수를 주석 처리해 주어야

프로그램이 정상 작동합니다.

 

 


강의 출처 : https://www.youtube.com/watch?v=F1m0i2QfEcI&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=24