[C++] std::allocator<T>

allocator
C++allocator
avatar
2025.02.11
·
9 min read

3369
[C++] <memory> std::allocator<T> 클래스
안녕하세요. 오늘은 헤더의 allocator 클래스에 대해 알아보겠습니다. allocator는 유연한 메모리 관리를 위한 클래스로 할당자라고도 합니다. 일반적으로 c++에서 메모리를 동적으로 할당하고 해제할 때 new/delete 연산자를 사용하는데 allocator 클래스는 주로 라이브러리를 작성할 때, 특히 표준 라이브러리의 컨테이너를 구현할 때 많이 사용됩니다. 할당자는 fine-grained 방식 즉, 메모리 관리를 좀 더 세밀하게 컨트롤 해야하고 유연하고 효율적으로 사용해야 할 경우에 유저가 원하는 메모리 할당 방식으로 구현할 수 있습니다. 즉 allocator 클래스를 상속받아 멤버 함수를 override해서 커스텀할 수 있습니다. 컨테이너는 메모리를 최대한 효율적으로 관리할 수 있어야 하는데..
https://woo-dev.tistory.com/51

std::allocator<T>

C++ 표준 라이브러리에서 제공하는 기본 메모리 할당자로, 주로 STL 컨테이너에서 내부적으로 메모리 관리를 위해 사용된다. 메모리 관리를 좀 더 세밀하게 컨트롤 해야하고 유연하고 효율적으로 사용해야 할 경우, 오버라이드를 통해 유저가 원하는 메모리 할당 방식으로 구현할 수 있다.

클래스 타입이라는 가정하에 new 연산자를 사용하게 되면 필수적으로 따라오는 3가지 조건이 있다.

  1. 기본 생성자 필요

  2. 메모리 할당

  3. 모든 요소 초기화

new 연산자는 라이브러리 개발자가 원하는 메모리 할당 방식으로 커스터마이징 할 수 없는데, allocator 클래스를 사용하면 위 단계들을 각각 원할 때 사용할 수 있다.

_EXPORT_STD template <class _Ty, class _Alloc = allocator<_Ty>>
class vector

대표적인 컨테이너인 vector 를 사용할 때, 우리는 보통 타입 매개변수 하나만 전달한다. 하지만 코드를 보면 알 수 있듯, 기본 매개변수allocator 가 사용되고 있다. 특정 컨테이너에 최적화된 유연한 메모리 사용과 관리를 위해, 대부분의 컨테이너들은 allocator 를 사용하는 것이다.

좋은 점

클래스 타입의 요소를 갖는 크기 10의 벡터를 선언했다고 가정해보자. 만약 내부적으로 벡터가 new 연산자를 통해 생성된다면, 벡터는 할당과 동시에 모든 요소들이 기본 생성자를 통해 초기화 되어있을 것이다. 그리고 사용자가 이 벡터에 원하는 요소를 저장하면, 각 요소들을 두번씩 초기화하게 될 것이다.

만약 allocator 의 멤버 함수를 이용하면, 메모리의 할당은 되었지만 초기화 되지 않은 상태의 메모리의 시작 주소를 얻을 수 있다. 원래 초기화되지 않은 메모리 공간에 객체를 직접 할당할 수 없지만, 해당 클래스의 멤버 함수 또는 관련 함수가 초기화 되지 않은 공간에 객체를 저장할 수 있도록 해준다.

그리고 할당받은 메모리에 객체를 생성 후, 메모리 해제 없이 생성한 객체들을 소멸시킬 수 있다. 즉, 메모리의 재할당 없이 그 공간을 allocate 했던 초기 상태로 만들 수 있다는 소리이다. 또한 해당 라이브러리는 할당받은 메모리 공간 중 객체가 생성된 공간과 아직 초기화되지 않은 공간을 알 수 있는 방법을 제공한다.

allocator

template <class T>
class allocator
{
public:
   T* allocate(size_t);
   void deallocate(T*, size_t);
   void construct(T*, const T&);
   void destory(T*);
   
   ....
};

template <class In, class For>
For uninitialized_copy(In, In, For);

template <class For, class T>
void uninitialized_fill(For, For, const T&);

allocator 클래스는 주로 4개의 멤버 함수와 멤버 함수는 아니지만 용도에 따라 관련된 함수 2개를 사용한다.

  1. allocate(size_t) : 초기화되지 않은 메모리 공간을 할당하여 그 시작 주소를 반환하는 함수. 매개변수는 필요한 T 객체의 개수이며, 인자로 전달된 개수만큼 T타입의 객체를 충분히 할당할 수 있는 공간을 만든다.

  2. deallocate(T*, size_t) : 메모리 공간을 해제하는 함수. 인자로 포인터와 개수를 받는다. 포인터는 allocate로 할당했던 메모리의 시작 주소를 가리키는 포인터이며, 개수는 allocate로 전달했던 인자의 개수이다.

  3. construct(T*, const T&) : 초기화되지 않은 공간에 요소를 저장하는 함수. T타입 포인터와 객체를 레퍼런스로 받으며 포인터가 가리키는 위치에 객체를 저장한다. 초기화되지 않은 공간에 * 연산자를 사용하여 값을 대입할 경우 에러가 발생하는데, 이 함수를 통해 에러없이 저장할 수 있다.

  4. destory(T*) : 객체를 소멸시키는 함수. 소멸과 메모리 해제는 다르다. 인자로 전달된 포인터가 가리키는 객체의 소멸자를 호출한다. destroy 를 호출하지 않고 deallocate 를 호출할 경우, 각 요소에 저장된 객체는 사라지겠지만 사라진 객체가 가리키던 객체는 그대로 메모리에 남아있어 메모리 누수가 발생할 수 있다.

  5. uninitialized_copy(In, In, For) : 초기화되지 않은 메모리 영역에 있는 객체들을 복사하여 새롭게 초기화하는 데 사용된다. 복사할 객체들이 위치한 입력 범위의 시작과 끝을 나타내는 반복자 2개(first, last)객체를 복사하여 생성할 대상 메모리의 시작 주소를 나타내는 순방향 반복자 1개(out)를 인자로 받는다. [first, last) 범위의 요소들을 out이 가리키는 위치에 순서대로 복사하고, 복사가 완료된 위치의 다음 요소를 가리키는 포인터를 반환한다.

  6. uninitialized_fill : 주어진 범위의 공간을 3번째 인자로 주어진 값으로 채운다.







- 컬렉션 아티클