정체불명의 모모

[C++] std::allocator<T> 클래스 본문

프로그래밍(c++)

[C++] std::allocator<T> 클래스

정체불명의 모모 2021. 8. 5. 12:27

c++ 표준 allocator에 대해 알아보겠습니다.

커스텀 allocator를 알아보기 전에 표준에 대해 더 자세히 알아갈 필요가 있다고 느꼈습니다.


 

ㅁ <memory> allocator<T>

: 일반적으로 c++에서 메모리를 동적으로 할당하고 해제 할 때 new / delete 연산자를 사용하는데

  allocator클래스는 주로 라이브러리 작성할 때, 특히 표준 라이브러리의 컨테이너를 구현할 때 많이 사용 됩니다.

    할당자는 *fine-grained(정확한 개념은 모르겠습니다.) 방식 즉, 메모리 관리를 좀 더 세밀하게 컨트롤 해야 하고

  유연하고 효율적으로 사용해야 할 경우에 유저가 원하는 메모리 할당 방식으로 구현할 수 있습니다.

  allocator 클래스를 상속받아 멤버 함수를 override 해서 커스텀 할 수 있습니다.

 

컨테이너는 메모리를 최대한 효율적으로 관리할 수 있어야 하는데 new / delete 연산자로는 세밀한 메모리 관리 능력이

떨어집니다. 컨테이너는 주로 데이터를 저장하고 관리하는 공간입니다.

따라서 저장 공간의 생성, 확장 ,추가, 삭제 등이 빈번 하게 발생합니다. 

 

▷ 만약 위 과정에서 new / delete 연산자를 사용할 경우 문제점 (new 연산자를 사용하게 되면 따라오는 조건)

1. 기본 생성자 필요

2. 메모리 할당

3. 모든 요소 초기화

 

위 3가지 조건이 세트로 따라오게 되는데 이는 요소가 많아질수록 컴퓨터 자원의 사용량이 상당히 증가하게 됩니다.

 또한, new 연산자는 라이브러리 개발자가 원하는 메모리 할당 방식으로 커스터마이징 할 수 없습니다.

allocator 클래스를 사용하면 위 단계들을 각각 원할 때 사용할 수 있습니다.

 

▷ 대표적인 컨테이너인 vector

// CLASS TEMPLATE vector
template < class _Ty, class _Alloc = allocator<_Ty>>
class vector
{	
	// varying size array of values
}

: 우리는 벡터를 사용할때 타입 매개변수로 하나만을 전달합니다.

  하지만, 사실 vector 는 기본 타입 매개변수로 아래와 같이 allocator가 하나 더 사용됩니다.

 

"위 처럼 특정 컨테이너에 최적화된 유연한 메모리 사용과 관리를 위해 대부분의 컨테이너들은 allocator를 사용합니다."

 

 

ㅁ allocator이 좋은 이유

1. 메모리를 동적 할당할 경우 초기화 되지 않은 공간(uninitialized)으로 메모리를 allocate 할 수 있습니다.

   : new 연산자의  경우 메모리를 할당하고 기본적으로 값 또는 객체를 의무적으로 초기화 해줍니다.

     이는 요소가 많을 수록 오버헤드가 높아지게 되고 초기화를 원치 않을 경우에도 불필요한 오버헤드가 발생하게 됩니다.

     new 연사자의 경우 두번의 초기화를 하게 됩니다.(불필요한 초기화가 필수여서)

 

▷ 두번의 초기화가 일어남 

      new 연산자 사용시 : (객체 생성 할때 초기화) int a = 0;     ->
      (나중에 원하는 값 넣어 초기화)  int a = 3; 

▷ allocator의 멤버함수를 이용하면?

: 메모리의 할당은 되었지만 초기화 되지 않은 상태의 메모리의 시작 주소를 얻을 수 있습니다.

  원래 초기화 되지 않은 메모리 공간에 객체를 직접 할당할 수 없습니다.

    하지만 해당 클래스의 멤버 함수 또는 관련 함수가 초기화 되지 않은 공간에 객체를 저장할 수 있도록 해줍니다.

 

2. 할당받은 메모리에 객체를 생성 후 메모리 해제(deallocate) 없이 생성한 객체들을 소멸(destroy)시킬 수 있습니다.

    : 즉 메모리의 재할당 없이 그 공간을 allocate 했던 초기 상태로 만들 수 있습니다. 

      new 연산자로 할당한 메모리 공간은 delete를 사용하면 메모리 공간이 해제가 되죠, 즉 하나의 과정을 세밀하게 나누어 

      컨트롤 할 수 있습니다.

 

      - 또한, 해당 라이브러리는 할당받은 메모리 공간 중 객체가 생성된 공간과 아직 초기화되지 않은 공간을 알 수 있는 방법을

         제공해 줍니다.

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

      충분한 공간 ex )  8 bytes(데이터형) * 5(개) + 4bytes = 44bytes 할당  (시작 주소 반환)
  • deallocate(T* , size_t) : 메모리 공간을 해제하는 함수입니다.
    • T* : 포인터는 allocate로 할당했던 메모리의 시작 주소를 가리키는 포인터이며 
    • size_t : 인자의 개수
  • construct(T* , const T&) : 초기화 되지 않은 공간에 요소를 저장하는 함수 입니다.
    • T* : 포인터가 가리키는 위치에 레퍼런스로 받은 객체를 저장합니다. 
      초기화 되지 않은 공간에 *(간접 참조) 연산자를 사용하여 값을 대입할 경우 에러가 발생합니다.
    • const T& : 저장할 객체
  • destroy(T*) :  객체를 소멸시킵니다.
    소멸과 메모리 해제는 다릅니다. 
    T 타입 포인터를 인자로 받으며 포인터가 가리키는 위치의 객체의 소멸자를 호출합니다
    즉, 인자로 전달된 포인터가 가리키는 객체의 소멸자를 호출합니다. 
    destroy를 호출 하지 않고 deallocate를 호출할 경우 각 요소에 저장된 객체는 사라지겠지만
    사라진 객체가 가리키던 객체는 그대로 메모리에 남아 있어 메모리 누수(memory leak)가 발생 할 수 있습니다.
  • uninitialized_copy( ) : 멤버 함수는 아니지만 자주 사용되는 관련된 함수 입니다.
    STL 의 std::copy 함수와 비슷하며 입력 반복자 2개(first , last)와 순방향 반복자 1개(out)를 인자로 받습니다.
    (first , last) 범위의 요소들을 out이 가리키는 위치에 순서대로 복사합니다.
    그리고 이 함수는 복사가 완료된 위치의 다음 요소를 가리키는 포인터를 반환합니다.
      ex) 1~5 위치에 복사 완료 했다면 6위치를 가리키는 포인터를 반환 합니다.
  • uninitialized_fill( ) : 주어진 범위의 공간을 3번째 인자로 주어진 값으로 채웁니다.
    ex ) uninitialized_fill(first , last , val) // (first , last) 범위의 각 모든 요소에 val 을 저장 합니다.

https://woo-dev.tistory.com/51

 

[C++] std::allocator 클래스

안녕하세요. 오늘은 헤더의 allocator 클래스에 대해 알아보겠습니다. allocator는 유연한 메모리 관리를 위한 클래스로 할당자라고도 합니다. 일반적으로 c++에서 메모리를 동적으로 할당하고 해제

woo-dev.tistory.com

위 내용은 제가 작성한 포스팅을 보면 더 쉽게 이해 되실 겁니다!

https://uncertainty-momo.tistory.com/47

 

[ C / C++ ] malloc( ) 와 new 의 차이점

최근 면접에서 대답 하지 못했던 부분에 대해 공부를 해보았습니다. 평소에 크게 신경쓰지 못했던것 이었는데, 더 근본적인 부분들을 신경써보게 되는 계기가 되었다. 질문 : malloc 과 new의 차이

uncertainty-momo.tistory.com

 

Comments