KoreanFoodie's Study
[C++ 게임 서버] 2-3. Allocator 본문
[C++ 게임 서버] 2-3. Allocator
핵심 :
1. C++ 에서는 new 와 delete 도 오버로딩할 수 있는데, Allocator 를 만들어 커스텀화된 new 와 delete 를 사용해 보자!
2. placement new 기법을 이용하면, 내가 지정한 메모리에 객체를 초기화할 수 있다!
놀랍게도(?) C++ 에서는 new 와 delete 를 오버로딩하여, 메모리 관리를 섬세하게 커스터마이징할 수 있다.
static void* operator new(size_t size)
{
cout << "Knight new! " << size << endl;
void* ptr = ::malloc(size);
return ptr;
}
static void operator delete(void* ptr)
{
cout << "Knight delete!" << endl;
::free(ptr);
}
위 함수는 Knight 클래스의 new 와 delete 를 오버로딩 한 예시이다!
그런데, 사실 위와 같은 방식으로 new 와 delete 를 매번 오버로딩하기 보다는, 할당자(Allocator) 의 역할을 하는 클래스를 따로 빼서 관리하는 게 더 낫다. 바로 다음과 같이 말이다...
BaseAllocator 클래스 정의 :
/*-------------------
BaseAllocator
-------------------*/
class BaseAllocator
{
public:
static void* Alloc(int32 size);
static void Release(void* ptr);
};
void* BaseAllocator::Alloc(int32 size)
{
return ::malloc(size);
}
void BaseAllocator::Release(void* ptr)
{
::free(ptr);
}
음..? 일단 겉보기에는, 딱히 하는 역할이 없어 보인다.
일단 더 간편하게 사용할 수 있도록 매크로도 정의할 것인데...
/*----------------
Memory
-----------------*/
#ifdef _DEBUG
#define xalloc(size) BaseAllocator::Alloc(size)
#define xrelease(ptr) BaseAllocator::Release(ptr)
#else
#define xalloc(size) BaseAllocator::Alloc(size)
#define xrelease(ptr) BaseAllocator::Release(ptr)
#endif
사실 Allocator 를 따로 두는 이유는, 보통 메모리를 더 효율적으로 사용하기 위해서 이다.
우리는 new 와 delete 를 빈번하게 하게 되면 '메모리 파편화' 현상이 일어나는 것을 알고 있는데, 추후 커스터마이징된 Allocator 를 이용해서 그러한 현상을 해결할 것이다. 😊
일단 메모리 할당 및 해제를 간편하게 할 수 있는, 아래 핵심 코드를 보자.
template<typename Type, typename... Args>
Type* xnew(Args&&... args)
{
Type* memory = static_cast<Type*>(xalloc(sizeof(Type)));
new(memory)Type(forward<Args>(args)...); // placement new
return memory;
}
template<typename Type>
void xdelete(Type* obj)
{
obj->~Type();
xrelease(obj);
}
위의 xnew 를 보면... 먼저 메모리를 할당하고, 생성자를 호출한 다음, 할당했던 메모리를 리턴한다.
그런데 신기한 코드가 있다. 바로...
new(memory)Type(forward<Args>(args)...); // placement new
위 코드인데, 이는 placement new 라는 기능으로, 내가 원하는 메모리에 객체를 초기화해 준다! (좀 더 자세한 내용은 이 글 참고)
즉, 위의 xnew 는 객체의 크기만큼 메모리를 할당하고, 가변 길이 템플릿을 받아서 생성자를 호출해 준다. 😉 (소멸자도 동작 원리는 비슷하다)
앞으로 위의 xnew 와 xdelete 를 다음과 같이 유용하게 사용할 것이다 🙂
Knight* knight = xnew<Knight>(100);
xdelete(knight);
음... 혹시 위처럼 메모리를 할당하고, 생성자를 호출하는 프로세스를 나눠서 해도 되는지 의문이 들 수도 있는데...
사실 그냥 new 를 호출해도, 할당과 생성자 호출을 한번에 하지 않고 나눠서 한다 😁
잘 보면, new Knight() 를 할 때, 메모리를 먼저 할당하고(빨간색), 그 다음 생성자를 호출하여 이것 저것 작업(파란색)을 하는 것을 확인할 수 있다 🤣
'Game Dev > Game Server' 카테고리의 다른 글
[C++ 게임 서버] 2-5. STL Allocator (0) | 2023.08.25 |
---|---|
[C++ 게임 서버] 2-4. StompAllocator (0) | 2023.08.24 |
[C++ 게임 서버] 2-2. 스마트 포인터 (0) | 2023.08.23 |
[C++ 게임 서버] 2-1. Reference Counting (0) | 2023.08.21 |
[C++ 게임 서버] 1-24. 연습문제 (소수의 갯수 구하기) (0) | 2023.08.08 |