C++에서 동적 객체를 생성하기 위해 일반적으로 new 연산자를 사용하는데, new 연산자는 2가지의 동작단계를 지니고 있습니다.
- 요청한 타입의 객체를 담을수 있는 크기의 메모리를 할당한다.
- 객체의 생성자를 호출하여 할당된 메모리에 객체의 초기화를 수행한다.
new 연산자의 첫번째 동작단계, 즉 요청한 타입의 객체를 담을수 있는 크기의 메모리 할당은 내부적으로 operator new 라는 함수를 호출하여 이루어집니다.
void * operator new(size_t size) |
operator new함수의 반환타입은 void * 입니다. 즉, operator new는 malloc함수와 마찬가지로 메모리만 할당하는 것뿐입니다. 객체니 생성자니 하는것은 operator new와는 전혀 관계없는 것들일뿐이며, 할당된 메모리를 받아 온전한 객체를 생성하는 것은 new 연산자의 몫입니다.
우리는 new 연산자에 대해 재작성이나 오버로딩을 할 수 없지만, operator new 연산자는 재작성, 오버로딩을 할 수 있는데 이를 통하여 자신만의 커스터마이징된 메모리 관리 시스템을 개발할 수 있습니다.(operator new를 오버로딩 할 때에는 size_t이외의 매개변수를 추가할 수 있는데, 이럴때도 첫번째 매개변수는 언제나 size_t 타입이여야 합니다.)
1. placement new
operator new(또는 malloc)을 통해 할당은 받앗지만 초기화되지않은 메모리가 있을때, 우리는 바로 이 메모리 위해 객체를 초기화시킬 수 있습니다. 이를 placement new라 합니다. placement new를 실행하기 위해 준비해야할것은 #include<new>를 추가하는 것이 전부입니다.
- MyClass *mem = static_cast<MyClass*>(operator new(sizeof MyClass));
- new (mem) MyClass();
위 코드에서 new (mem) MyClass() 부분이 placement new 연산자 입니다. 문법이 조금 이상하지만 operator new 연산자에 매개변수가 1개 더 추가 되었다고 보면 됩니다.
void * operator new(size_t, void *location)
|
new, operator new, placment new에 대해 정리하면 다음과 같습니다.
- 어떤 객체를 heap에 생성하기 위해서는 new 연산자를 사용합니다.
- 메모리만을 할당하고 싶을 경우에는 operator new 함수를 사용합니다. 즉, 만약에 heap 객체의 생성을 도맡는 메모리 할당 매커니즘을 손보고 싶다면, operator new를 직접 작성하면 됩니다. new 연산자는 operator new 함수를 호출하게될 것이며 호출된 operator new함수는 개발자가 새롭게 작성한 함수입니다.
- heap에서 직접 메모리를 가져오지 않고, 개발자가 지정한(이미 가지고 있는) 메모리를 사용하게 하려면 placement new를 사용합니다.
2. delete, operator delete
메모리 누수를 막기 위해서는, 할당한 메모리는 반드시 그에 대응되는 메모리 해제를 통해 운영체제에 돌려줘야 합니다. C++에서는 기본적으로 제공되는 delete 연산자를 사용하게 됩니다. 그런데 delete 연산자도 new 연산자와 마찬가지로 내부적으로 어떤 함수를 호출하게 되있습니다.
void operator delete(void *dealloc) |
new 연산자를 통해 할당된 메모리는 delete 연산자를 통해 해제해야합니다. delete 연산자는 객체의 소멸자를 호출하고 객체가 차지하고 있던 메모리를 운영체제에 반환하게 됩니다.
여기서 주의해야할점은, 미초기화된(객체가 생성되지 않은) 메모리를 가지고 어떤 일을 할때에는 new와 delete 연산자를 사용하지 않아야 합니다. 그 대신 메모리를 얻을때에는 operator new를 메모리를 해제할때는 operator delete를 사용합니다.(마치 malloc함수를 통해 할당된 메모리를 free함수를 통해 해제하는것처럼)
placement new를 사용한 경우에는 절대로 delete 연산자를 호출하면 안됩니다. 이유는 delete 연산자는 operator delete를 호출하여 메모리를 해제하지만, placement new에서 사용된 메모리는 원천적으로 operator new를 통해 할당된 것이 아니기 때문입니다. 대신 객체의 소멸자를 직접 호출해주고 메모리도 역시 직접 할당한 방법에 따라 해제해줘야 합니다.
- MyClass->~MyClass();
- operator delete MyClass;
이와 같이 C++에서 기본 제공하는 new, delete 연산자의 동작방식을 직접 손댈수는 없지만 이들 연산자가 내부적으로 호출하는 operator new, operator delete 연산자는 개발자가 마음먹은대로 고칠 수 있으며, 이는 프로그램의 전첵 메모리 할당 매커니즘을 총괄하는 강력한 메모리 매니지먼트 시스템을 개발할 수 잇음을 의미합니다.