Coding Memo

메모리 할당 - STL Allocator 본문

Game Server (C++)

메모리 할당 - STL Allocator

minttea25 2022. 10. 9. 13:12

본 포스팅은 인프런에 등록되어 있는 Rockiss 님의 강의를 보고 간단하게 정리한 글입니다.


STL에 있는 많은 Containers 들은 선언할 때 추가적으로 allocator를 따로 지정해줄 수 있다. 이 allocator을 사용자 정의하여 각 container들의 메모리 사용하는 정책을 지정해 줄 수 있는 것이다.

 

예를 들어 vector 같은 경우 다음과 같은 생성자가 구현이 되어 있다.

template <class _Ty, class _Alloc = allocator<_Ty>>
class vector

...

 

그렇다면 이 allocator에는 어떤 타입이 들어가야 할까?

 

main 함수에서 다음의 코드를 실행해 본다면 무엇이 필요한지 대강 알 수 있다.

(BaseAllocator는 전에 작성했던 클래스 이다.)

int main() 
{
	vector<int, BaseAllocator> v;
}

 

실행하면 오류가 쭉 나오게 된다.

 

여기서 필요한 것들을 하나씩 구현해나가면 될 것이다.

 

먼저 식별자부터 하나 추가해보자: value_type을 typename으로 지정

template<typename T>
class StlAllocator
{
public:
	using value_type = T;

};

오류가 상당히 줄어든 것을 알 수 있다.

 

메모리를 할당할 함수와 해제할 함수도 추가 한다. (vector에서 allocator 타입을 그대로 사용하는 것이므로 함수이름은 반드시 똑같이 해야 한다!)

 

"Allocator.h"

template<typename T>
class StlAllocator
{
public:
	using value_type = T;

	StlAllocator() {}
	template<typename Other>
	StlAllocator(const StlAllocator<Other>&) {}

	T* allocate(size_t count)
	{
		const int size = static_cast<int>(count * sizeof(T));
		return static_cast<T*>(xxalloc(size));
	}

	void deallocate(T* ptr, size_t count)
	{
		xxrelease(ptr);
	}
};

이렇게 StlAllocator를 작성하면 빌드가 오류없이 된다.

 

참고: xxalloc과 xxrelease는 전에 작성했던 DEBUG 상태에 따라서 StompAllocator나 BaseAllocator로 메모리를 할당하는 매크로이다.

#ifdef _DEBUG
#define xxalloc(size)		StompAllocator::Alloc(size)
#define xxrelease(ptr)		StompAllocator::Release(ptr)
#else
#define xxalloc(size)		BaseAllocator::Alloc(size)
#define xxrelease(ptr)		BaseAllocator::Release(ptr)
#endif // _DEBUG

 


이제 사용할 Container들에 대해서 우리가 원하는 방식의 메모리 할당 방법을 사용하도록 바꿔 줄 수 있다.

vector 뿐만 아니라, list, map, set, queue, stack, deque 등에 사용할 container에 방금 작성한 StlAllocator 방식을 이용하도록 타입을 새롭게 정의해보자.

(메모리 관련 타입만 지정하고 나머지-hash, 비교 방법 등은 기존 방식 그대로 사용하면 된다.)

 

"Container.h"

template<typename Type>
using Vector = vector<Type, StlAllocator<Type>>;

template<typename Type>
using List = list<Type, StlAllocator<Type>>;

template<typename Key, typename Type, typename Pred = less<Key>>
using Map = map<Key, Type, Pred, StlAllocator<pair<const Key, Type>>>;

template<typename Key, typename Pred = less<Key>>
using Set = set<Key, Pred, StlAllocator<Key>>;

template<typename Type>
using Deque = deque<Type, StlAllocator<Type>>;

template<typename Type, typename Container = Deque<Type>>
using Queue = queue<Type, Container>;

template<typename Type, typename Container = Deque<Type>>
using Stack = stack<Type, Container>;

template<typename Type, typename Container = Vector<Type>, typename Pred = less<typename Container::value_type>>
using PriorityQueue = priority_queue<Type, Container, Pred>;

using String = basic_string<char, char_traits<char>, StlAllocator<char>>;

using WString = basic_string<wchar_t, char_traits<wchar_t>, StlAllocator<wchar_t>>;

template<typename Key, typename Type, typename Hasher = hash<Key>, typename KeyEq = equal_to<Key>>
using HashMap = unordered_map<Key, Type, Hasher, KeyEq, StlAllocator<pair<const Key, Type>>>;

template<typename Key, typename Hasher = hash<Key>, typename KeyEq = equal_to<Key>>
using HashSet = unordered_set<Key, Hasher, KeyEq, StlAllocator<const Key>>;

테스트로 생성자와 소멸자가 제대로 호출되는지 확인해보자.

class Unit
{
public:
	Unit()
	{
		cout << "Unit()\n";
	}
	Unit(int hp) : _hp(hp)
	{
		cout << "Unit(_hp)\n";
	}
	~Unit()
	{
		cout << "~Unit(): " << _hp << "\n";
	}

	int _hp = 1;
};

int main()
{
	Vector<Unit> v(5);

	Queue<Unit, Deque<Unit>> q;
	q.push(Unit(100));

	Map<int, Unit> m;
	m[10] = Unit(1000);
}

 

결과

생성자와 소멸자가 제대로 호출 되는 것을 확인 할 수 있다.

 

 

* Map에 대해 값을 줄 때, m[10] = Unit(1000) 부분을 순서대로 실행시켜 보았더니 m[10] = Unit(1000)이 좀 특이하게 작동하는 것을 알 수 있었다.

 

Unit(1000) 에 대한 생성자 Unit(hp)가 호출이 되고,

m[10]에 대한 생성자 Unit()이 호출이 되고,

m[10]에 Unit(1000)을 넣은 후,

Unit(1000)에 대한 소멸자가 호출이 된다. (~Unit(1000))

 

이후 main 함수가 끝나면서 Map에 있는 hp가 1000인 Unit이 소멸되면서 ~Unit(1000)이 한번 더 호출 된다.

이후 차례대로 소멸자 호출...

'Game Server (C++)' 카테고리의 다른 글

Object Pool  (0) 2022.10.11
Memory Pool  (0) 2022.10.09
메모리 할당 - Stomp Allocator  (0) 2022.10.06
메모리 할당 - Allocator  (0) 2022.10.06
Reference Counting  (0) 2022.09.30