Language/C++

[C++] 스마트 포인터

마탁이 2020. 12. 13. 16:59

1. 스마트 포인터
  - 동적으로 할당된 메모리를 자동으로 해제시켜주는 클래스

  1) auto_ptr
    - 동적으로 할당된 메모리도 자동으로 해제하는 기능을 가진 포인터 래퍼 클래스.
    - memory 헤더 파일을 포함시켜야 한다.
         #include <memory>
     - 단점
       > malloc으로 할당한 경우는 자동 해제를 못 한다.
         (이 경우 새로 클래스를 만들어야 한다)
       > <>에 [] 연산자를 지원하지 않는다.
     - 선언 형태
          auto_ptr<자료형> 변수명(new 자료형);
     - auto_ptr은 포인터와 같이 4 byte를 가진다.

 

  2) unique_ptr
   - 하나의 스마트 포인터만이 특정 객체를 소유할 수 있도록 객체에 소유권 개념을 도입한 스마트 포인터.
   - shard_ptr과 달리 참조 카운트가 1을 넘을 수 없다.
     > unique_ptr로 할당한 메모리는 딱 하나의 포인터 변수로만 해당 객체를 가리킬 수 있다.
     > 두 개 이상의 포인터가 하나의 공간을 가리키면 컴파일 에러가 난다.
     > 따라서 참조 카운트 포인터가 필요없어 4 byte의 크기를 가진다.

   - 선언 형태

       unique_ptr<자료형> 변수명(new 자료형);

   - 객체 선언 형태

       shared_ptr<자료형> 변수명 = make_unique<자료형>(생성자 매개 변수);

   - 객체 = move(객체);

     > 소유권을 이전하는 함수

   - 객체.reset()

     > 가지고 있는 메모리 영역을 해제 하는 함수

   - auto_ptr, shard_ptr 과는 달리 <>에 []를 지원한다.

 

  3) shared_ptr
   - 한 자원을 여러 포인터가 가리킬 때 쓰는 포인터
   - 참조 카운트를 통해 참조 카운트가 0이 되는 순간 메모리를 해제한다.
     > 참조 카운트 : 해당 메모리를 참조하는 포인터가 몇 개 인지를 나타내는 값.
     > 이 참조 카운트는 참조 횟수를 관리하는 객체 Control Block에 저장해 두기 때문에 용량이 늘지 않는다.
   - 컨테이너에서 포인터 복사본을 반환 시 원본을 유지하고 싶을 경우 사용.
     > 가리키는 주소의 메모리는 모든 소유자가 없어지기 전까지(참조 카운트가 0이 될 때까지) 삭제되지 않는다.
   - 선언 형태
       shard_ptr<자료형> 변수명(new 자료형);
   - 객체 선언 형태
       shard_ptr<자료형> 변수명 = make_shard<자료형>(생성자 매개 변수);
   - 객체.use_count()
     > 참조 카운트를 반환하는 함수
   - shared_ptr는 <>에 []를 지원하지 않기 때문에 <Type>(new Type[])형태로 배열을 선언
     > [] 연산자를 지원하지 않는다.
     > auto 키워드를 이용하면 이를 극복할 수 있다.

 

   * new 선언과 make_shared를 이용한 shard_ptr 선언의 차이점
    - (new Type)으로 선언한 경우 Type 할당과 컨트롤 블록 공간 할당의 두 가지 할당을 따로 진행한다.
    - make_shared<Type>()할당의 경우 Type 할당과 컨트롤 블록 할당 두 가지를 동시에 진행한다.
      따라서 make_shared 할당이 더 빠르고 처리 순서로 인한 문제를 발생하지 않아 안전하다.

   * shard_ptr의 경우 8 byte를 가진다.
    - 가리키는 객체의 포인터(4 byte) + 컨트롤 블록을 가리키는 포인터(4 byte)

 

   * 컨트롤 블록이 생성되는 조건
    1) make_shared로 shared_ptr을 생성한 경우
    2) 소유권이 보장된 unique_ptr, auto_ptr로 부터 shared_ptr이 생성된 경우
      (아직 공유되지 않은 특정 객체를 공유하겠다고 선언하는 것)
    3) raw pointer가 다른 shard_ptr에 따로 공유된 적이 없을 때 shard_ptr이 만들어 졌을 경우
     > 이미 공유된 자원은 shard_ptr 또는 weak_ptr을 사용해야 한다.
     > 다른 shard_ptr에 공유된 적이 있는 raw pointer로 shared_ptr을 만들면 중복 컨트롤러 발생
        따라서 shard_ptr을 생성할 때 raw pointer를 쓰지 않는 것이 좋음.

   * 순환 참조로 메모리 누수가 발생할 수 있다.
    - 서로 상대방을 가리키는 shared_ptr을 가지고 있으면 참조 횟수는 절대 0이 되지 않는다.

   * shard_ptr의 라이프 타임은 프로그램이 종료될 때 메모리 할당이 해제된다.

 

  3) weak_ptr
   - shared_ptr와 함께 사용할 수 있는 (참조자 역할, shared_ptr의 객체만 참조) 스마트 포인터
   - shared_ptr의 객체를 weak_ptr로 참조하면 shared_ptr의 참조 카운트가 아닌 weak_ptr 참조 카운트가 증가.
   - shared_ptr로만 변환 가능하며 weak_ptr, shared_ptr로만 복사 생성, 대입 연산이 가능하다.
   - weak_ptr는 해당 객체에 직접적인 접근이 불가능하다.
    > lock()함수를 통해 shared_ptr롤 변환한 뒤 get 함수로 접근할 수 있다.
   - expired() 함수로 참조하고 있는 shard_ptr의 상태를 체크 할 수 있다.
    > 참조한 shared_ptr가 소멸되었으면 true, 살아있으면 false 반환
   - shared_ptr의 순환 참조를 제거하기 위해 사용된다.
    > 서로가 상대방을 가리키는 shard_ptr을 가지고 있으면 참조 횟수는 절대 0이 되지 않는다.
    > weak_ptr일 경우 참조 횟수에 포함되지 않으므로 누수가 일어나지 않음.

 

  4) boost::scoped_ptr
   - 범위를 벗어나면 메모리를 해제하는 스마트 포인터 (범위 제한적)
   - namespace boost 에서 제공하는 스마트포인터
   - scoped_ptr은 복사할 수 없다.
    따라서 scoped_ptr을 멤버로 가지고 있는 객체도 복사할 수 없다.

 

'Language > C++' 카테고리의 다른 글

[C++] 입출력 속도  (0) 2020.12.13
[C++] 얕은 복사와 깊은 복사  (0) 2020.12.13
[C++] 가상함수  (0) 2020.12.13
[C++] 선언과 정의  (0) 2020.12.13
[C++] 변수 크기와 sizeof() 연산  (0) 2020.12.13