상세 컨텐츠

본문 제목

Allocator

똑똑한 개발/C++ 게임개발

by 성댕쓰 2021. 9. 11. 15:24

본문

메모리를 할당하거나 반납할 때, 기본 new, delete말고 custom하여 쓸 수 있다. 그러면 memory 할당, 반납 전 카운팅을 하여 메모리 누수 포인트를 알아낸다거나 하는 용도외 다양한 기능을 추가할 수 있다.

 

3가지 방법을 알아볼건데, 첫 번째 방법은 Global한 new, delete operator overloading 하는 방법이다.

// new operator overloading (Global)
void* operator new(size_t size)
{
	cout << "new! " << size << endl;
	void* ptr = ::malloc(size);
	return ptr;
}

void operator delete(void* ptr)
{
	cout << "delete!" << endl;
	::free(ptr);
}

// 배열
void* operator new[](size_t size)
{
	cout << "new[]! " << size << endl;
	void* ptr = ::malloc(size);
	return ptr;
}

void operator delete[](void* ptr)
{
	cout << "delete[]!" << endl;
	::free(ptr);
}

외부 라이브러리 코드의 new, delete에도 적용되고 기존 기본 연산자를 더 이상 쓸 수 없다는 단점이 있다.

 

new 동작을 좀더 자세히 보면 먼저 메모리를 할당하고 사용하고자 하는 객체의 생성자를 호출한다.

delete는 사용한 객체의 소멸자를 호출하고 메모리를 반납한다.

 

다른 방법은 클래스에 추가하는 것이다.

class Knight
{
public:
	Knight()
	{
		cout << "Knight()" << endl;
	}

	Knight(int32 hp) : _hp(hp)
	{
		cout << "Knight(hp)" << endl;
	}

	~Knight()
	{
		cout << "~Knight()" << endl;
	}

	// new operator overloading
	static void* operator new(size_t size) // static 붙이지 않아도 붙음(operator한정)
	{
		cout << "new! " << size << endl;
		void* ptr = ::malloc(size);
		return ptr;
	}

	static void operator delete(void* ptr)
	{
		cout << "delete!" << endl;
		::free(ptr);
	}

	int32 _hp = 100;
	int32 _mp = false;
};

 

단점은 사용하고자 하는 모든 class에 new, delete operator overloading을 해야한다.

 

마지막 방법은 새로운 메모리 관리 클래스를 만들어 사용하는 것이다.

Allocator.h, Allocator.cpp

#pragma once

/*---------------------
	BaseAllocator
---------------------*/

class BaseAllocator
{
public:
	static void* Alloc(int32 size);
	static void Release(void* ptr);
};
#include "pch.h"
#include "Allocator.h"

void* BaseAllocator::Alloc(int32 size)
{
	return ::malloc(size);
}

void BaseAllocator::Release(void* ptr)
{
	::free(ptr);
}

상황에 따라 다른 Allocator를 사용하게 하고, 편하게 사용하기 위해서 위 메서드를 macro로 정의한다.

CoreMacro.h

	/*----------------------
			Memory
	----------------------*/
#ifdef _DEBUG
	#define x_alloc(size) BaseAllocator::Alloc(size)
	#define x_release(ptr) BaseAllocator::Release(ptr)
#else
	#define x_alloc(size) BaseAllocator::Alloc(size)
	#define x_release(ptr) BaseAllocator::Release(ptr)
#endif

현재는 1가지 버전밖에 없으므로 if와 else 코드가 같다.

 

새로 만든 클래스를 사용할 함수를 Global영역에 정의한다.

Memory.h

#pragma once
#include "Allocator.h"

template<typename Type, typename... Args>
Type* xnew(Args&&... args)
{
	Type* memory = static_cast<Type*>(x_alloc(sizeof(Type)));

	// placement new
	new(memory)Type(std::forward<Args>(args)...);
	return memory;
};

template<typename Type>
void xdelete(Type* obj)
{
	obj->~Type();
	x_release(obj);
}

 

placement new 문법을 이용하여 할당한 메모리에 객체를 생성한다. 가변길이 템플릿 파라미터 ... Args와 가변길이 함수 파라미터 Args&&... args를 이용하여 생성자 호출할 때 필요한 파라미터를 개수에 상관없이 받을 수 있다.

파라미터로 넘어온 args를 lvalue, rvalue 그대로 넘기기 위해 std::forward문법을 이용한다.

 

메모리 반납하기 전에는 객체의 소멸자를 먼저 호출한다.

 

사용은 아래처럼 한다.

int main()
{
	Knight* knight = xnew<Knight>();

	xdelete(knight);
}

 

참조 : [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 - 인프런 | 강의 (inflearn.com)

'똑똑한 개발 > C++ 게임개발' 카테고리의 다른 글

STL allocator  (0) 2021.09.12
Stomp allocator  (0) 2021.09.11
스마트 포인터  (0) 2021.09.10
Reference Counting  (0) 2021.09.08
Deadlock 탐지  (0) 2021.09.06

관련글 더보기

댓글 영역