• C++ 프로그램에서 new 키워드를 사용하여 동적으로 할당받은 메모리는, 반드시 delete 키워드를 사용하여 해제해야 합니다.

  • C++에서는 메모리 누수(memory leak)로부터 프로그램의 안전성을 보장하기 위해 스마트 포인터를 제공하고 있습니다.

  • 스마트 포인터(smart pointer)란 포인터처럼 동작하는 클래스 템플릿으로, 사용이 끝난 메모리를 자동으로 해제해 줍니다.

  • C++11에서 새롭게 도입된 스마트 포인터는 unique_ptr, shared_ptr, weak_ptr입니다.

  • unique_ptr 실제 데이터에 대한 소유권을 오직 하나만 가지도록 하는 것

  • shared_ptr 실제 데이터에 대한 소유권을 여러 개의 스마트포인터가 공유하면서 참조 카운트 방식으로 관리

  • weak_ptr share_ptr 실제 데이터를 공유하지만 참조 카운트의 값을 증가하거나 감소시키지 않는다


 

1.unique_ptr

unique_ptr

  • 하나의 스마트 포인터만이 특정 객체를 소유할 수 있도록, 객체에 소유권 개념을 도입한 스마트 포인터입니다.
  • 이 스마트 포인터는 해당 객체의 소유권을 가지고 있을 때만, 소멸자가 해당 객체를 삭제할 수 있습니다.
  • 순환 참조 문제를 해결하기 위해서 참조가 단 1개만 존재하는 스마트 포인터
  • unique_ptr 인스턴스는 move() 멤버 함수를 통해 소유권을 이전할 수는 있지만, 복사할 수는 없습니다.(Move::이동시퀸스로 이전)
  • 소유권이 이전되면, 이전 unique_ptr 인스턴스는 더는 해당 객체를 소유하지 않게 재설정됩니다.

보통의 C++ 객체에 대해 스마트 포인터가 필요한 상황에서는 주로 unique_ptr을 사용하면 됩니다.

  • std::unique_ptr는 복사 생성자와 복사 대입 연산자가 아예 구현되어 있지 않다.
  • 그렇기 때문에 복사가 애초에 불가능하고 이동만 가능하다.
  • 이동은 std::move() 함수로만 가능하다.
  • 포인터는 get() 멤버 함수로 얻을 수 있고 메모리 해제는 reset() 멤버 함수로 한다.

포인터를 복사할 수 없는거지 포인터가 가리키는 객체를 복사할 수 없는 것은 아니다. 또 복사 대입 연산시 좌항을 참조자로 선언하고 정의하는 것도 포인터의 포인터를 통해 참조하는 것이기 때문에 가능하다.

    std::unique_ptr<int> foo(new int(3));

    auto bar = *foo;
    auto& baz = foo;

    bar = 0;                    //bar =0으로 되고 , foo는 변화없음
    baz = 0;                   //baz -> empty , foo ->empty 됨

 

const std::unique로 선언된 포인터는 std::move()로도 이동시킬 수 없다. 
비트 수준의 상수성을 가지고 있기 때문에 데이터를 다른 곳으로 이동을 시킬 수 없어서 그렇다.

 const std::unique_ptr<int> foo(new int(3));
    auto bar = std::move(foo);                             //컴파일 에러

    std::unique_ptr<int> foo(new int(3));
    auto bar = std::move(foo);                                //정상

 


2.shared_ptr

std::shared_ptr는 이름처럼 가리키는 객체의 소유권을 다른 포인터들과 공유할 수 있는 포인터다.

  • std::unique_ptr과는 다르게 복사도 마음껏 할 수 있다.
  • 같은 객체를 가리키는 std::shared_ptr은 레퍼런스 카운팅으로 추적된다.
  • 참조된 횟수를 세는 것이므로 포인터가 복사될 때 마다 1씩 증가한다. 그리고 해제될 때 마다 1씩 감소한다. 
    포인터가 가리키는 객체의 메모리가 해제되는 시점은 레퍼런스 카운트가 0이 될 때이다.
  • 레퍼런스 카운트는 use_count() 멤버 함수로 가져올 수 있다.
  • make_shared는 (custom) deleter를 사용 할수가 없다
  • 공유자원에 좋음?

std::shared_ptr 객체가 복사되어도 메모리 공간은 늘어나지 않는다.
객체의 메모리 공간은 그대로 두고, 레퍼런스 카운트만 건드리기 때문에 그렇다.

void main() {
    std::shared_ptr<int> foo(new int(3)); // reference count = 1
    auto bar = foo;                            // reference count = 2

    foo.reset();                                  // reference count = 1
    bar.reset();                                  // reference count = 0, 이떄 객체가 완전히 해제된다.
}

 

레퍼런스 카운트가 0이 되어 참조하는 객체를 해제할 때 std::shared_ptr는 delete 연산자를 사용한다.
여기에 문제가 있다. delete[]는 사용하지 않는 것이 문제다. 
해결방법은 아래처럼, 벡터에 shard_ptr을 넣거나, Deleter를 만드는 것이다

int main() {
    std::shared_ptr<int> foo(new int[1024]); // 이렇게 배열의 포인터를 선언하지 말고
    foo.reset();

    std::vector<std::shared_ptr<int>> bars; // 포인터 벡터를 만들면 해결된다.
    bars.push_back(std::shared_ptr<int>(new int(3)));

    for(auto& bar : bars) {
        bar.reset();
        //삭제될떄 delete 가 됨
    }
}

 

std::shared_ptr의 기본 생성자

constexpr shared_ptr() noexcept; 
constexpr shared_ptr( std::nullptr_t ) noexcept; 

template< class Y >
explicit shared_ptr( Y* ptr ); 

template< class Y, class Deleter >
shared_ptr( Y* ptr, Deleter d );

template< class Deleter >
shared_ptr( std::nullptr_t ptr, Deleter d );   //Deleter 함수 객체 정의 가능

template< class Y, class Deleter, class Alloc >
shared_ptr( Y* ptr, Deleter d, Alloc alloc ); 

 

Deleter 예시

template<typename T>
struct ArrayDeleter {
    void operator() (T* ptr) {
        delete[] ptr;
    }
};

void main() {
    std::shared_ptr<int> foo(new int[1024], ArrayDeleter<int>());   
    foo.reset(); //   delete[]을 한다. 이제 배열 제대로 해제될 것이다.
}

 

샘플

    shared_ptr<int> Ashard(new int(33),ArrayDeleter<int>());
   //shared_ptr<int> Bshard = make_shared<int>(55, ArrayDeleter<int>());  make_shard는 deleter 안됨
   shared_ptr<int> Bshard(new int(33));

   shared_ptr<int> Cshard = std::move(Ashard);                  //A->empty 됨
   Bshard.reset();                                                 //B->empty됨
   Cshard.reset();                                                 //C->empty됨

 

'프로그래밍 > C++' 카테고리의 다른 글

이동 시멘틱  (0) 2019.04.29
함수 포인터  (0) 2019.04.29
템플릿 <template>  (0) 2019.04.29
OOP 객체지향프로그래밍  (0) 2019.04.29
C++ 11 추가된 기능  (0) 2019.04.29

+ Recent posts