Tuesday, March 11, 2025

unique_ptr

Overview
unique_ptr are useful when a resource such as a piece of memory or a file handle needs to be singly managed.

Details
unique_ptr 
unique_ptr are useful when a single resource such as a piece of memory or a file handle is allocated, used in multiple places and later recycled.
unique_ptr 
is replacement for auto_ptr. It can hold a single resource such as a pointer to an object or an array of objects. I
t also provides option to supply a custom deleter object to recycle the resource when the  unique_ptr  is destroyed.  Inot  supplied,  default_delete is used.
It has complete ownership of the resource it's holding. When the unique_ptr object is destroyed  gracefully or during stack rewind. , the deleter will be called to free up resources.

Syntax
template <class T, class D = default_delete<T>> 
class unique_ptr

//array specialization	
template <class T, class D> 
class unique_ptr<T[],D>

member types
NameDescription
element_typefirst template parameter (T). The type of the managed object
deleter_type
second template parameter (D). The type of the stored deleter.
Defaults to default_delete<T>
pointer
remove_reference<D>::type::pointer, if this type exists
T*, otherwise The pointer type.

Constructors
Commonly used constructors defined for unique_ptr<T>.
NameDescription
unique_ptr()Default constructor.
Example:
unique_ptr<int>();
unique_ptr(nullptr_t)Same as default constructor.
Example:
unique_ptr<int>(nullptr);
unique_ptr(pointer p)Takes ownership of p and use default deleter
Example:
unique_ptr<int>(new int(10));
unique_ptr
(
pointer p,
typename conditional<is_reference<D>::value,D,const D&> del) 
Takes ownership of p and copy lvalue deleter del
Example:
auto d = default_delete<int>();
unique_ptr<int>  (new int, d);
unique_ptr
(
pointer p, 
typename remove_reference<D>::type&& del)
Takes ownership of p and move rvalue deleter del
Example:
unique_ptr<int>  (new int, default_delete<int>());
unique_ptr(unique_ptr&& x)Move pointer and copy deleter from x if it's a reference or else move it.
Example:
unique_ptr<int>  x(new int, default_delete<int>());
unique_ptr<int> (move(x));
template <typename U, typename E
unique_ptr (unique_ptr<U,E>&& x) 
move pointer and copy deleter from x if it's a reference or else move it.
Example:
unique_ptr<int>  t(new int, default_delete<int>());
unique_ptr<int> x (move(t));
unique_ptr<int>(move(x));
unique_ptr<T>(auto_ptr<T>&& x)takes ownership of autoptr x
Example:
auto_ptr<int> x(new int);
unique_ptr<int> (move(x));
The example 2 demonstrates it. 

Methods
The following describes functionality in detail.
NameDescription
pointer get()Returns the pointer of the resource it's holding without relinquishing the ownership.

Example
unique_ptr<int[]>  x(new int[10], default_delete<int[]>());
x.get()[3]=10;
pointer release()Returns the pointer of the resource it's holding also relinquishes the ownership without calling the deleter.
Example
unique_ptr<int[]>  x(new int[10], default_delete<int[]>());
auto p = x.release();
p[5]=10;
delete [] p;
  1. void reset (pointer ptr = pointer())
  2. void reset (nullptr_t = nullptr) 
Destroys the resource it's currently holding by calling deleter and releases the resource. Optionally new resource can be set to it.
  1. Replaces with same type of  resource.
  2. Becomes empty
Example
cout << boolalpha;
unique_ptr<long[]>  x(new long[10], default_delete<long[]>());
x.reset();
//prints false
cout << (bool)x << endl;
x.reset(new long[10]);
//prints true
cout << (bool)x << endl;
operator bool()Returns true if a resource is managed otherwise false.
typename add_lvalue_reference<T>::type operator*()
Object dereferencing. Returns the object owned, equivalent to *get().

pointer operator->() Member dereferencing. Returns a pointer to the object owned, equivalent to get().
Example
struct person {string name; unsigned char age;};
unique_ptr<person>  x(new person("khrisha",6));
//prints khrisha
cout << x->name << endl;
//prints 6
cout << (short) (*x).age << endl;
element_type& operator[](size_t i)Array accessing index operator.
Returns a reference to the i-th object (zero-based) in the managed array, equivalent to: get()[i].
This member function is exclusive of the array-specialization of unique_ptr (unique_ptr<T[],D>). The non-specialized version does not include it.

Example
unique_ptr<long[]>  x(new long[10], default_delete<long[]>());
x[5]=5;
  1. unique_ptr& operator=
    (unique_ptr&& x)
  2. unique_ptr& operator=
    (nullptr_t)
  3. template <class U, class E
    unique_ptr& operator=
    (unique_ptr<U,E>&& x)
  1. Move assignment operator. Equivalent to resets(x.release()) and attaching deleter of x.
  2. Effectively the same as calling reset().
  3. Converting assignment operator. The assignment operation between unique_ptr objects that point to different types needs to be between types whose pointers are implicitly convertible, and shall not involve arrays in any case.
  1. deleter_type& get_deleter() 
  2. const deleter_type& get_deleter() 
  1. Sets new deleter.
  2. Returns the stored deleter.
The example 3 demonstrates it. 

External Methods
The following describes relational operators
NameDescription
  1. template <class T1, class D1, class T2, class D2>
    bool operator ==
    (const unique_ptr<T1,D1>& lhs,
    const unique_ptr<T2,D2>& rhs)
  2. template <class T, class D>
    bool operator ==
    (const unique_ptr<T,D>& lhs, nullptr_t)
  3. template <class T, class D>
    bool operator ==
    (nullptr_t, const unique_ptr<T,D>& rhs) 
Equality operator.
  1. template <class T1, class D1, class T2, class D2>
    bool operator !=
    (const unique_ptr<T1,D1>& lhs,
    const unique_ptr<T2,D2>& rhs)
  2. template <class T, class D>
    bool operator !=
    (const unique_ptr<T,D>& lhs, nullptr_t)
  3. template <class T, class D>
    bool operator !=
    (nullptr_tconst unique_ptr<T,D>& rhs) 
Inequality operator.
  1. template <class T1, class D1, class T2, class D2>
    bool operator  >
    (const unique_ptr<T1,D1>& lhs,
    const unique_ptr<T2,D2>& rhs)
  2. template <class T, class D>
    bool operator >
    (const unique_ptr<T,D>& lhs, nullptr_t)
  3. template <class T, class D>
    bool operator >
    (nullptr_tconst unique_ptr<T,D>& rhs) 
Greater than operator.
  1. template <class T1, class D1, class T2, class D2>
    bool operator >=
    (const unique_ptr<T1,D1>& lhs,
    const unique_ptr<T2,D2>& rhs)
  2. template <class T, class D>
    bool operator >=
    (const unique_ptr<T,D>& lhs, nullptr_t)
  3. template <class T, class D>
    bool operator >=
    (nullptr_tconst unique_ptr<T,D>& rhs) 
Greater than or equal to operator.
  1. template <class T1, class D1, class T2, class D2>
    bool operator <
    (const unique_ptr<T1,D1>& lhs,
    const unique_ptr<T2,D2>& rhs)
  2. template <class T, class D>
    bool operator <
    (const unique_ptr<T,D>& lhs, nullptr_t)
  3. template <class T, class D>
    bool operator <
    (nullptr_tconst unique_ptr<T,D>& rhs) 
Less than operator.
  1. template <class T1, class D1, class T2, class D2>
    bool operator <=
    (const unique_ptr<T1,D1>& lhs,
    const unique_ptr<T2,D2>& rhs)
  2. template <class T, class D>
    bool operator <=
    (const unique_ptr<T,D>& lhs, nullptr_t)
  3. template <class T, class D>
    bool operator <=
    (nullptr_tconst unique_ptr<T,D>& rhs) 
Less than or equal to operator.

make_unique()
A template based function to generate unique_ptr.

Syntax
//scalar types
template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args );

//array types
template< class T >
unique_ptr<T> make_unique( std::size_t size ); 

Example
auto p = make_unique<int>(10);
//prints 10
cout << *p << endl;

auto p2 = make_unique<pair<int,int>>(10,20);
//prints 10 20
cout << p2->first << " " << p2->second << endl;

auto p3 = make_unique<int[]>(4);
p3[0]=10;    
//prints 10
cout << p3[0] << endl;

Usage Examples
Doubly linked list
A double linked list can be implemented using unique_ptr.  The advantage is automatic cleanup. However, each node should be owned by a single unique_ptr. Double referencing same node can be avoided by declaring each node as below. Here head of the list and next pointer of each node are owners . Rest are all pointers to the same.
struct doublelist
{
    struct node;
    using LLNODE = unique_ptr<node>;
    using LLNODEptr = unique_ptr<node>*;

    struct node
    {
        T data;
        LLNODEptr prev;
        LLNODE next;
    };
    LLNODE head;
    LLNODEptr tail;
}
The example 4  depicts a solution. 

No comments:

Post a Comment