| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
Tags
- SCC
- 2-SAT
- Behavior Design Pattern
- DP
- Delete
- Github
- trie
- 벨만-포드
- Prisma
- Strongly Connected Component
- 이분 탐색
- 강한 연결 요소
- 비트필드를 이용한 dp
- 그래프 탐색
- Lock-free Stack
- 최소 공통 조상
- JavaScript
- 게임 서버 아키텍처
- map
- Binary Lifting
- Express.js
- 트라이
- localstorage
- 비트마스킹
- Spin Lock
- ccw 알고리즘
- reference counting
- PROJECT
- 자바스크립트
- R 그래프
Archives
- Today
- Total
dh_0e
[C++/Game Server] Allocator (New, Delete Operator Overloading) + std::forward, Placement New 본문
C++/Game Server
[C++/Game Server] Allocator (New, Delete Operator Overloading) + std::forward, Placement New
dh_0e 2026. 3. 10. 12:26New, Delete Operator
- 다음과 같이 new와 delete도 operator로 재정의 가능
- operator로 흐름을 가로채서 로그를 찍거나 무언가를 할 수 있음
class Knight {
public:
Knight() { cout << "Knight()" << endl; }
~Knight() { cout << "~Knight()" << endl; }
int32 _hp = 100;
int32 _mp = 100;
};
// new operator overloading (Global)
void* operator new(size_t size) {
cout << "operator new: " << size << endl;
void* ptr = malloc(size);
return ptr;
}
void operator delete(void* ptr) {
cout << "operator delete: " << ptr << endl;
::free(ptr);
}
void* operator new[](size_t size) {
cout << "operator new: " << size << endl;
void* ptr = malloc(size);
return ptr;
}
void operator delete[](void* ptr) {
cout << "operator delete: " << ptr << endl;
::free(ptr);
}
int main() {
Knight* knight = new Knight();
delete knight;
return 0;
}

- 다음과 같이 전역으로 선언하면 Global 하게(모든 new, delete에) 적용돼서 위험할 수 있음
- 아래와 같이 클래스에 넣어서 해당 클래스에만 적용되게끔 사용이 가능함
class Knight {
public:
Knight() { cout << "Knight()" << endl; }
~Knight() { cout << "~Knight()" << endl; }
void* operator new(size_t size) {
// == static void* operator new() {}
// 클래스 멤버로 선언 시 자동으로 static 함수가 됨
cout << "operator new: " << size << endl;
void* ptr = malloc(size);
return ptr;
}
void operator delete(void* ptr) {
cout << "operator delete: " << ptr << endl;
::free(ptr);
}
int32 _hp = 100;
int32 _mp = 100;
};
Allocator.h / Allocator.cpp

- Alloc(): 메모리 할당만 하고 생성자는 직접 호출해주지 않음
- Release(): 메모리 해제만 담당하고 소멸자는 직접 호출해주지 않음
Memory.h
#pragma once
#include "Allocator.h"
template <typename Type, typename... Args>
Type* xnew(Args&&... args) { // 새로운 new 연산자 오버로딩
Type* memory = static_cast<Type*>(xxalloc(sizeof(Type)));
// placement new: 어떤 객체의 생성자를 호출하는 또다른 문법
// new (memory) Type();
new (memory) Type(std::forward<Args>(args)...);
// forward: 가변인자를 통해 생성자를 호출
return memory;
}
template <typename Type>
void xdelete(Type* obj) {
obj->~Type(); // 소멸자 직접 호출
xxrelease(obj);
}
Memory.cpp
#pragma once
#include "Allocator.h"
template <typename Type, typename... Args>
Type* xnew(Args&&... args) { // 새로운 new 연산자 오버로딩
Type* memory = static_cast<Type*>(xxalloc(sizeof(Type))); // xxalloc == BaseAllocator::Alloc
// placement new: 어떤 객체의 생성자를 호출하는 또다른 문법
// new (memory) Type();
new (memory) Type(std::forward<Args>(args)...);
// forward: 가변인자를 통해 생성자를 호출
return memory;
}
template <typename Type>
void xdelete(Type* obj) {
obj->~Type(); // 소멸자 직접 호출
xxrelease(obj); // xxrelease == BaseAllocator::Release
}
- Allocator.h/.cpp로 다음과 같이 메모리 할당, 해제 로직을 만들고 Memory.h/.cpp에 새로운 xnew, xdelete를 제작
- xnew()
- xnew(Args&&... args)에서 &&는 전달 참조(Forwarding Reference)로 Rvalue(임시 객체)를 넣든, Lvalue(변수)를 넣든 다 받아낼 수 있음
- 하지만 함수 내부로 들어오는 순간, 이름(args)이 생기기 때문에 이 변수는 무조건 Lvalue가 됨
- 가변 인자를 받아 다양한 생성자 종류에 대응할 수 있도록 std::forward 사용
- std::forward: 전달받은 인자의 원래 성질(Lvalue인지 Rvalue인지)을 그대로 유지해서 다음 함수로 넘겨주는 도구
- Lvalue가 들어올 때: Args가 int&가 되고, 참조 축약 규칙에 의해 int&를 반환(복사 생성자 호출)
- Rvalue가 들어올 때: Args가 int가 되고, std::forward는 내부적으로 static_cast<int&&>(args)를 수행 (이동 생성자 호출)
- 생성자에 필요한 인자가 몇 개든 상관없이 원래 형태 그대로 생성자에게 배달해 주는 역할
- 이게 없으면 모든 인자가 복사(Copy)되거나 Lvalue로 전달되어, 성능 최적화(Move)가 불가능해짐
- std::move: 무조건 Rvalue로 바꿈 (강제 약탈)
- std::forward: 조건부로 Rvalue로 바꿈 (원래 성격 유지)
- std::forward: 전달받은 인자의 원래 성질(Lvalue인지 Rvalue인지)을 그대로 유지해서 다음 함수로 넘겨주는 도구
- Allocator(Alloc)에서 생성자를 호출하지는 않기 때문에 placement new를 사용하여 생성자 호출 (with 가변 인자)
- Placement New: 이미 확보된 메모리 주소에 객체의 생성자만 호출해서 앉히는(placement) 기법
- 일반 new: [메모리 할당 + 생성자 호출]
- Plcement new: [생성자 호출]만 담당
- 해제할 때 소멸자 직접 호출해줘야 함
- !Placement new로 생성한 객체는 절대 delete로 지우면 안 됨!
- Placement New: 이미 확보된 메모리 주소에 객체의 생성자만 호출해서 앉히는(placement) 기법
- xnew(Args&&... args)에서 &&는 전달 참조(Forwarding Reference)로 Rvalue(임시 객체)를 넣든, Lvalue(변수)를 넣든 다 받아낼 수 있음
Placement New 문법
#include <new> // 반드시 이 헤더를 포함해야 함
// 1. 메모리 준비 (보통 할당이나 배열로 준비)
void* ptr = malloc(sizeof(Knight));
// 2. Placement New 호출
// new (주소) 타입(생성자 인자);
Knight* knight = new (ptr) Knight();
- xdelete()
- Allocator(Release)에서 소멸자를 호출하지 않기 때문에 메모리 할당을 해제하기 전에 소멸자 호출
- xxalloc, xxrelease는 coreMacro에 정의
coreMacro.h
/*----------------
Memory
----------------*/
#ifdef _DEBUG
#define xxalloc(size) BaseAllocator::Alloc(size)
#define xxrelease(ptr) BaseAllocator::Release(ptr)
#else
#define xxalloc(size) BaseAllocator::Alloc(size)
#define xxrelease(ptr) BaseAllocator::Release(ptr)
#endif
- Debug 상황, Release 상황에 맞게 다양한 alloc, release 버전을 만들어 사용 가능하게끔 매크로 생성
GameServer.cpp
class Knight {
public:
Knight() { cout << "Knight()" << endl; }
Knight(int32 hp) : _hp(hp) { cout << "Knight(int32 hp): " << hp << endl; }
~Knight() { cout << "~Knight()" << endl; }
int32 _hp = 100;
int32 _mp = 100;
};
int main() {
Knight* knight = xnew<Knight>(100);
xdelete(knight);
return 0;
}

- 다음과 같이 인수가 잘 전달되어 출력되는 것을 확인할 수 있음
'C++ > Game Server' 카테고리의 다른 글
| [C++/Game Server] STL Allocator (0) | 2026.03.11 |
|---|---|
| [C++/Game Server] Stomp Allocator (Use-after-free & Overflow 오류 검증) (1) | 2026.03.10 |
| [C++/Game Server] Reference Counting (in Shared Pointer) (1) | 2026.03.07 |
| [C++/Game Server] DeadLock Profiler (+ release, debug 모드) (0) | 2026.02.19 |
| [C++/Game Server] Reader-Writer Lock (0) | 2026.02.12 |
