C++에 대한 문법을 공부하다가 Placement new라는 문법을 알게 되었다.
뭔가 어렵기도 하고 사용법이 다소 까다롭기도 하지만, 메모리 풀링을 할 때 중요하게 사용되는 문법이라고 한다.
먼저, Placement new를 이해하기 전에 malloc과 new의 차이점을 알고 있어야 한다
모른다면 아래 링크를 참고하도록 하자.
C++ 문법 - malloc와 new의 차이 (tistory.com)
먼저, new의 작동 방식을 알아보자.
1. 전역 함수인 operator new를 실행하여 메모리를 할당한다.
malloc과 동일하게 사이즈만큼의 메모리 영역을 할당한 뒤, void*를 반환한다.
2. 할당된 영역의 내부의 값을 초기화해준다.
new의 경우 malloc과 다르게 내부의 값을 초기화해준다고 하였다. 이 과정에서 초기화가 이루어진다.
3.사용자가 요청한 자료형으로 포인터를 반환해준다.
malloc처럼 void*가 아닌, int*, char*등의 지정한 자료형으로 주소를 반환받을 수 있는 이유는 내부에서 변환을 해주기 때문이다.
new는 이렇게 3개의 과정을 거친다.
이 중에서 2번의 과정에 사용되는 문법이 placement new 이다.
placement new는 메모리를 할당해주는 문법은 아니다. 이미 할당되어 있는 메모리 영역을 초기화해주는 문법이다.
operator new를 통해, 메모리 영역을 할당하였다고 가정해보자.
이런 형태로 내부엔 쓰레기 값이 담겨있을 것이다.
하지만, 우리가 사용하기 위해선 우리가 사용할 데이터를 저 곳에 집어넣어야 한다.
그 역할을 해주는 것이 placement new이다.
int main()
{
void* Pointer = operator new(sizeof(int));
int* I = new(Pointer) int;
return 0;
}
위와 같이 void*형으로 메모리만 할당받은 뒤에, 나중에 new(주소값) 의 형태로 초기화를 해준다.
괄호 안에 있는 주소가 가리키는 메모리 영역을 원하는 자료형으로 초기화하는 것이다.
아래와 같이 오버로딩된 생성자도 사용 가능하다.
class Test
{
public:
Test() {};
Test(int _X, int _Y) : X(_X), Y(_Y) {}
~Test() { std::cout << "Delete\n"; }
int GetX()
{
return X;
}
int GetY()
{
return Y;
}
private:
int X = 0;
int Y = 0;
};
int main()
{
void* Pointer = operator new(sizeof(Test));
Test* T = new(Pointer) Test(4, 5);
T->~Test();
delete Pointer;
return 0;
}
그런데 위 글을 보면 이상한 점이 한가지 있다.
바로 소멸자를 직접 호출해주고 있다는 점이다.
왜 그럴까?
먼저, 위의 코드를 보면 소멸자가 호출될 때 Delete라는 문자열을 출력하도록 해주었다.
소멸자를 호출하는 코드에 주석을 친 뒤에 프로그램을 실행해보면, Delete가 출력되지 않는다.
이렇게 아무것도 출력되지 않는다.
반면 소멸자를 직접 호출해주면?
위와 같이, Delete가 출력되는 것을 볼 수 있다.
왜 그럴까? 기본적으로 메모리 할당을 void* 형의 Pointer라는 변수에다가 할당을 해주었다.
그렇기 때문에 메모리 해제를 할 때에도 delete Pointer; 로 Pointer가 가리키는 주소의 메모리를 해제해 주었다.
하지만, Pointer는 void 타입이기 때문에 해당 영역의 소멸자를 호출해줄 수가 없다.
그렇다면, delete T;를 호출해준다면?
물론 소멸자가 잘 호출이 된다. 하지만, 문제가 있다.
아래와 같이 Pointer에는 메모리 할당을 크게 해놓고, 여러개의 객체를 사용하는 상황이다.
이 때, delete T를하면 T_2는 어떻게 해야할까?
T_2가 있는 메모리 영역도 해제되어 버린다. 물론 소멸자는 호출되지 않은 채로 말이다.
그렇기 때문에, 명시적으로 메모리 공간 내에 있는 모든 객체의 소멸자를 호출해준 뒤에 메모리영역을 해제하는 것이 훨씬 안전하고 적합한 방법이다.
placement new의 경우, 이미 할당된 메모리 영역에 초기화만 해주는 방식이기 때문에 동적할당된 메모리 영역이 아닌 스택 메모리에서도 사용할 수 있다.
int main()
{
char Array[sizeof(Test)] = {0,};
Test* T = new(Array) Test(4, 5);
return 0;
}
이런 식으로 스택 메모리에도 사용할 수 있다. 어디에 활용해야 하는지는 모르겠지만, 할 수 있다는 것 쯤은 알아두자.
여기까지 placement new에 대해 알아보았다.
사용되는 곳은 참 많겠지만, 가장 많이 사용되는 곳은 메모리 풀링이라고 한다.
메모리를 한 번에 덩어리로 할당해놓고, 필요할때마다 필요한 크기만큼 placement new로 초기화하여 사용하는 것이다.
소멸자도 직접 호출해줘야 하고, placement new를 할 때, 메모리,주소의 오프셋도 잘 관리해야 하는 만큼 번거롭고 헷갈리는 부분이 많지만 실제 프로그래밍에서 사용되고 있는 만큼 지식을 쌓아두면 좋을 것 같다.
'C++ > C++' 카테고리의 다른 글
C++ - 가변 인자 템플릿 (0) | 2024.04.18 |
---|---|
C++ - 코루틴 (coroutine) (0) | 2024.04.14 |
C++ - string_view (읽기 전용 문자열 컨테이너) (0) | 2024.04.11 |
C++ - SSO (Small String Optimization) (0) | 2024.04.11 |
C++ - malloc/free , new/delete (0) | 2024.04.10 |