C++/Library 2021. 3. 24. 10:47

unique_ptr

unique_ptr

  • <memory> 라이브러리

  • scope를 벗어나면 자동으로 메모리를 해제하므로 메모리 누수를 방지할 수 있다.


예제

main.cpp

  #include <iostream>
  #include <memory>
  #include "Resource.h"

  using namespace std;

  int            main()
  {
    std::unique_ptr<Resource> res(new Resource(100000000));
  }
  /* stdout
  Resource length constructed
  Resource destoryed
  */

Resource.h

  #pragma once

  #include <iostream>

  class Resource
  {
  public:
    int    *data_ = nullptr;
    unsigned length_ = 0;

    Resource()
    {
      std::cout << "Resource default constructed\n";
    }

    Resource(unsigned length)
    {
      std::cout << "Resource length constructed\n";
      init(length);
    }

    Resource(const Resource& res)
    {
      std::cout << "Resource copy constructed\n";
      init(res.length_);
      for (unsigned i = 0; i < length_; ++i)
        data_[i] = res.data_[i];
    }

    ~Resource()
    {
      std::cout << "Resource destoryed\n";

      if (data_ != nullptr) delete[] data_;
    }

    void    init(unsigned length)
    {
      data_ = new int[length];
      length_ = length;
    }

    Resource& operator = (Resource& res)
    {
      std::cout << "Resource copy assignment\n";
      if (&res == this) return *this;

      if (data_ != nullptr) delete[] data_;
      init(res.length_);
      for (unsigned i = 0; i < length_; ++i)
        data_[i] = res.data_[i];
      return *this;
    }

    void    print()
    {
      for (unsigned i = 0; i < length_; ++i)
        std::cout << data_[i] << ' ';
      std::cout << std::endl;
    }

    void    setAll(const int& v)
    {
      for (unsigned i = 0; i < length_; ++i)
        data_[i] = v;
    }
  };

make_unique

  • unique_ptr를 안전하게 반환하는 함수

    main.cpp

    #include <iostream>
    #include <memory>
    #include "Resource.h"
    
    using namespace std;
    
    auto        doSomething()
    {
      //return std::unique_ptr<Resource>(new Resource(5));
      return std::make_unique<Resource>(5); // 이걸 더 추천
    }
    
    int            main()
    {
      auto res1 = doSomething();
    
      res1->setAll(5);
    
      std::unique_ptr<Resource> res2;
    
      if (res1 != nullptr)
      {
        cout << "res1 : ";
        res1->print();
      }
      if (res2 != nullptr)
      {
        cout << "res2 : ";
        res2->print();
      }
    
      cout << boolalpha;
      cout << "res1 : " << static_cast<bool>(res1) << endl;
      cout << "res2 : " << static_cast<bool>(res2) << endl;
      cout << '\n';
    
      res2 = std::move(res1);
    
      if (res1 != nullptr) 
      {
        cout << "res1 : ";
        res1->print();
      }
      if (res2 != nullptr) 
      {
        cout << "res2 : ";
        res2->print();
      }
    
      cout << "res1 : " << static_cast<bool>(res1) << endl;
      cout << "res2 : " << static_cast<bool>(res2) << endl;
    }
    
    /* stdout stderr
    Resource length constructed
    res1 : 5 5 5 5 5
    res1 : true
    res2 : false
    
    res2 : 5 5 5 5 5
    res1 : false
    res2 : true
    Resource destoryed
    */
    • 아래의 명령은 에러가 발생한다.

      main.cpp

      res2 = res1;
      error C2280: 'std::unique_ptr<Resource,std::default_delete<Resource>> &std::unique_ptr<Resource,std::default_delete<Resource>>::operator =(const std::unique_ptr<Resource,std::default_delete<Resource>> &)': attempting to reference a deleted function
      • unique_ptr는 말 그대로 포인터를 단독 사용하기 위해 쓰는 클래스이므로, 라이브러리 자체에서 복사 대입(copy assignment, operator =)을 delete한 것이다.

copy constructordelete한 상태임을 보여주는 예제

main.cpp

  #include <iostream>
  #include <memory>
  #include "Resource.h"

  using namespace std;

  auto        doSomething(std::unique_ptr<Resource> res)  // 에러
  {
    res->setAll(10);
  }

  int            main()
  {
    auto res1 = std::make_unique<Resource>(5);
    res1->setAll(1);
    res1->print();

    doSomething(res1);

    res1->print();
  }
  error C2280: 'std::unique_ptr<Resource,std::default_delete<Resource>>::unique_ptr(const std::unique_ptr<Resource,std::default_delete<Resource>> &)': attempting to reference a deleted function
  • 에러 메시지를 통해 라이브러리에서 복사 생성자(copy constructor)를 delete한 것임을 알 수 있다.
  • 이 예제에서 doSomething(std::move(res1)) 과 같이 R-value로 넘기면, doSomething 함수 내부로 unique_ptrres1이 옮겨간다.

    • 해당 함수의 scope 밖으로 나오는 순간(함수가 끝나고 스택이 되감길 때) unique_ptr의 특성으로 res가 사라진다.

    • 글로 설명하는 것보다 예제를 보는게 쉽다.

    main.cpp

    #include <iostream>
    #include <memory>
    #include "Resource.h"
    
    using namespace std;
    
    auto        doSomething(std::unique_ptr<Resource> res)
    {
      res->setAll(10);
    }
    
    int            main()
    {
      auto res1 = std::make_unique<Resource>(5);
      res1->setAll(1);
      res1->print();
    
      cout << boolalpha << static_cast<bool>(res1) << endl;
    
      doSomething(std::move(res1));
    
      cout << static_cast<bool>(res1) << endl;
    }
    
    /* stdout stderr
    Resource length constructed
    1 1 1 1 1
    true
    Resource destoryed
    false
    */

get()

  • unique_ptr로 가지고 있는 포인터를 반환한다.

<memory>

// STRUCT TEMPLATE _Get_deleter_pointer_type
template <class _Ty, class _Dx_noref, class = void>
struct _Get_deleter_pointer_type { // provide fallback
    using type = _Ty*;
};

...

template <class _Ty, class _Dx /* = default_delete<_Ty> */>
class unique_ptr {
public:
  using pointer      = typename _Get_deleter_pointer_type<_Ty, remove_reference_t<_Dx>>::type;

...

_NODISCARD pointer get() const noexcept {
    return _Mypair._Myval2;
}

...

private:
  template <class, class>
  friend class unique_ptr;

  _Compressed_pair<_Dx, pointer> _Mypair;
};

<xmemory>

template <class _Ty1, class _Ty2, bool = is_empty_v<_Ty1> && !is_final_v<_Ty1>>
class _Compressed_pair final : private _Ty1 { // store a pair of values, deriving from empty first
public:
    _Ty2 _Myval2;

...
};

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

C++ weak_ptr  (0) 2021.03.24
C++ shared_ptr  (0) 2021.03.24
C++ std::move  (0) 2021.03.24
C++ 출력 스트림 끊기  (0) 2021.03.24
C++ std::exception  (0) 2021.03.22