이번에는 Allocator에 대해 얘기해볼 것입니다. 말 그대로 할당자라는 뜻으로 동적 할당에 관련해서 볼 것입니다.
우리는 주로 new Knight() 등을 통해 동적할당을 합니다.
여기서 new는 Knight 클래스만큼의 용량을 할당을 해주고 생성자 또한 알아서 실행시켜줍니다.
이것만으로도 충분히 좋은 명령어이지만 하다보면 뭔가 추가옵션을 넣어주고 싶을 수 있습니다. 이를 통해 버그가 일어날 수 있는 부분을 보완하거나 메모리 단편화 문제를 해결할 수 있을 것입니다.
이번 포스팅에는 어떤 식으로 추가옵션을 줄 수 있는지에 대해 써볼 것입니다.
void* operator new(size_t size) {
cout << "new!" << endl;
void* ptr = malloc(size);
return ptr;
}
void operator delete(void* ptr) {
cout << "delete!" << endl;
free(ptr);
}
class Knight {
public:
Knight() {
cout << "Knight()" << endl;
}
Knight(int32 hp) : _hp(hp) {
cout << "Knight(hp)" << endl;
}
~Knight() {
cout << "~Knight()" << endl;
}
public:
int32 _hp;
};
int main() {
Knight* knight = new Knight();
delete knight;
//실행결과
//new!
//Knight()
//~Knight()
//delete!
}
이렇게 하면 new, delete를 사용할 때마다 우리가 원하는 명령어를 실행시켜줄 수 있습니다. 다만 이렇게 전역적으로 바꾸는 것은 원하지 않기 때문에 따로 함수로 만들어보겠습니다.
void* Alloc(int32 size) {
return malloc(size);
}
void Release(void* ptr) {
free(ptr);
}
template<typename Type, typename... Args>
Type* xxnew(Args&&... args) {
Type* memory = static_cast<Type*>(Alloc(sizeof(Type)));
return memory;
}
template<typename Type>
void xxdelete(Type* ptr) {
Release(ptr);
}
int main() {
Knight* knight = xxnew<Knight>();
xxdelete<Knight>(knight);
}
이렇게 하면 새로운 동적할당 함수를 만들어줄 수 있습니다. 다만 생성자와 소멸자가 실행되지 않습니다. 왜냐하면 new와 delete는 알아서 생성자와 소멸자를 실행시켜주는 기능이 있지만 malloc과 free는 아니기 때문입니다.
그래서 생성자와 소멸자를 실행시켜주기 위해 명령어를 추가해줍니다.
template<typename Type, typename... Args>
Type* xxnew(Args&&... args) {
Type* memory = static_cast<Type*>(Alloc(sizeof(Type)));
//new(memory)Type(::forward<Args>(args)...);
return memory;
}
template<typename Type>
void xxdelete(Type* ptr) {
//ptr->~Type();
Release(ptr);
}
이렇게 해주면 생성자와 소멸자도 잘 실행이 됩니다. 여기까지 새롭게 함수를 만들어 버그가 일어날 수 있는 부분 등을 보완할 수 있는 토대를 만들어보았습니다.