C + + features ----- unique_ptr smart pointer

Keywords: C++

Background: in C + +, dynamic memory is usually managed through a pair of operators new, which allocate space for an object in dynamic memory and return the pointer of the object. You can choose to initialize the object; delete, accept the pointer of a dynamic object, destroy the object and free the memory associated with it. It is easy to have problems in use, because it is difficult to release memory at the right time. Forgetting to release will cause memory leakage. In order to use dynamic memory more safely, the C++11 standard library provides two kinds of smart pointers to manage dynamic objects, shared_ptr and unique_ptr.

std::unique_ptr is used to replace STD:: Auto in C++11 standard_ PTR pointer container (in C++11, auto_ptr is discarded). It cannot work with other unique_ Pointer objects of PTR type share the memory of the object. This "ownership" can only be transferred through the move function of the standard library. unique_ptr is a pointer encapsulation type that removes the copy constructor and retains the move constructor. Normal copy or assignment operations are not supported.

 

Example program:

1. Constructor

// unique_ptr constructor example
#include <iostream>
#include <memory>

int main () {
  std::default_delete<int> d;
  std::unique_ptr<int> u1;
  std::unique_ptr<int> u2 (nullptr);
  std::unique_ptr<int> u3 (new int);
  std::unique_ptr<int> u4 (new int, d);
  std::unique_ptr<int> u5 (new int, std::default_delete<int>());
  std::unique_ptr<int> u6 (std::move(u5));
  std::unique_ptr<int> u7 (std::move(u6));
  std::unique_ptr<int> u8 (std::auto_ptr<int>(new int));

  std::cout << "u1: " << (u1?"not null":"null") << '\n';
  std::cout << "u2: " << (u2?"not null":"null") << '\n';
  std::cout << "u3: " << (u3?"not null":"null") << '\n';
  std::cout << "u4: " << (u4?"not null":"null") << '\n';
  std::cout << "u5: " << (u5?"not null":"null") << '\n';
  std::cout << "u6: " << (u6?"not null":"null") << '\n';
  std::cout << "u7: " << (u7?"not null":"null") << '\n';
  std::cout << "u8: " << (u8?"not null":"null") << '\n';

  return 0;
}

The results are as follows:

If you assign a value directly, an error will be reported.

 

2. Destructor

// unique_ptr destructor example
#include <iostream>
#include <memory>

int main () {
    auto deleter = [](int*p){
        delete p;
        std::cout << "[deleter called]\n";
    };

    std::unique_ptr<int,decltype(deleter)> foo (new int,deleter);

    std::cout << "foo " << (foo?"is not":"is") << " empty\n";

    return 0;                        // [deleter called]
}

Output:

 

  Note: after return0, delete is called.

 

3. Assignment algorithm

// unique_ptr::operator= example
#include <iostream>
#include <memory>

int main () {
    std::unique_ptr<int> foo;
    std::unique_ptr<int> bar;

    foo = std::unique_ptr<int>(new int (101));  // rvalue

    std::cout << "foo before: ";
    if (foo) std::cout << *foo << '\n'; else std::cout << "empty\n";

    bar = std::move(foo);                       // using std::move

    std::cout << "foo after: ";
    if (foo) std::cout << *foo << '\n'; else std::cout << "empty\n";

    std::cout << "bar: ";
    if (bar) std::cout << *bar << '\n'; else std::cout << "empty\n";

    return 0;
}

output

 

  Note: unique features can be reflected here. It can only point to one object. The assignment uses the move constructor std::move()

 

4. Get and release methods: the get function does not unique_ptr Release ownership of the pointer (that is, it is still responsible for deleting managed data at some point). Therefore, the value returned by this function should not be used to construct a new managed pointer.

To get the stored pointer and release ownership of it, call instead unique_ptr::release.

 

// unique_ptr::get vs unique_ptr::release
#include <iostream>
#include <memory>

int main () {
    // foo   bar    p
    // ---   ---   ---
    std::unique_ptr<int> foo;                // null
    std::unique_ptr<int> bar;                // null  null
    int* p = nullptr;                        // null  null  null

    foo = std::unique_ptr<int>(new int(10)); // (10)  null  null
    bar = std::move(foo);                    // null  (10)  null
    p = bar.get();                           // null  (10)  (10)
    *p = 20;                                 // null  (20)  (20)
    p = nullptr;                             // null  (20)  null

    foo = std::unique_ptr<int>(new int(30)); // (30)  (20)  null
    p = foo.release();                       // null  (20)  (30)
    *p = 40;                                 // null  (20)  (40)

    std::cout << "foo: ";
    if (foo) std::cout << *foo << '\n'; else std::cout << "(null)\n";

    std::cout << "bar: ";
    if (bar) std::cout << *bar << '\n'; else std::cout << "(null)\n";

    std::cout << "p: ";
    if (p) std::cout << *p << '\n'; else std::cout << "(null)\n";
    std::cout << '\n';

    delete p;   // the program is now responsible of deleting the object pointed to by p
    // bar deletes its managed object automatically

    return 0;
}

 

5,get_ Delete method

// unique_ptr deleter with state
#include <iostream>
#include <memory>

class state_deleter {  // a deleter class with state
    int count_;
public:
    state_deleter() : count_(0) {}
    template <class T>
    void operator()(T* p) {
        std::cout << "[deleted #" << ++count_ << "]\n";
        delete p;
    }
};

int main () {
    state_deleter del;

    std::unique_ptr<int> p;   // uses default deleter

    // alpha and beta use independent copies of the deleter:
    std::unique_ptr<int,state_deleter> alpha (new int);
    std::unique_ptr<int,state_deleter> beta (new int,alpha.get_deleter());

    // gamma and delta share the deleter "del" (deleter type is a reference!):
    std::unique_ptr<int,state_deleter&> gamma (new int,del);
    std::unique_ptr<int,state_deleter&> delta (new int,gamma.get_deleter());

    std::cout << "resetting alpha..."; alpha.reset(new int);
    std::cout << "resetting beta..."; beta.reset(new int);
    std::cout << "resetting gamma..."; gamma.reset(new int);
    std::cout << "resetting delta..."; delta.reset(new int);

    std::cout << "calling gamma/delta deleter...";
    gamma.get_deleter()(new int);

    alpha.get_deleter() = state_deleter();  // a brand new deleter for alpha

    // additional deletions when unique_ptr objects reach out of scope
    // (in inverse order of declaration)

    return 0;
}

Output:

 

 

6. reset method

// unique_ptr::reset example
#include <iostream>
#include <memory>

int main () {
  std::unique_ptr<int> up;  // empty

  up.reset (new int);       // takes ownership of pointer
  *up=5;
  std::cout << *up << '\n';

  up.reset (new int);       // deletes managed object, acquires new pointer
  *up=10;
  std::cout << *up << '\n';

  up.reset();               // deletes managed object

  return 0;
}

Output:

 

Posted by mjm on Mon, 01 Nov 2021 21:14:24 -0700