메모리를 할당하거나 반납할 때, 기본 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)
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 |
댓글 영역