Smart Pointers

#include <memory>  

std::unique_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};
void process_item(unique_ptr<Foo> p)
{
   if (!p) { return; }
   cout << "Processing " << p->name << '\n';
}
int main()
{
    {
        unique_ptr<Foo> p1 {new Foo{"foo"}};
        auto            p2 (make_unique<Foo>("bar"));
    }
    process_item(make_unique<Foo>("foo1"));
    auto p1 (make_unique<Foo>("foo2"));
    auto p2 (make_unique<Foo>("foo3"));
    process_item(move(p1));//unique_ptr have only move constructor and move assigment
    cout << "End of main()\n";
}

After we left the scope, both objects are destructed immediately and their memory is released to the heap. Let’s have a look at the process_item function and how to use it with unique_ptr now. If we construct a new Foo instance, managed by a unique_ptr in the function call, then its lifetime is reduced to the scope of the function. When process_item returns, the object is destroyed
f we want to call process_item with an object that already existed before the call, then we need to transfer ownership because that function takes a unique_ptr by value, which means that calling it would lead to a copy. But unique_ptr cannot be copied, it can only be moved. Let’s create two new Foo objects and move one into process_item. By looking at the terminal output later, we will see that foo2 is destroyed when process_item returns because we transferred ownership to it. foo3 will continue living until the main function returns

CTOR foo
CTOR bar
DTOR bar
DTOR foo
CTOR foo1
Processing foo1
DTOR foo1
CTOR foo2
CTOR foo3
Processing foo2
DTOR foo2
End of main()
DTOR foo3

std::shared_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};

void f(shared_ptr<Foo> sp)
{
      cout << "f: use counter at " << sp.use_count() << '\n';
}
int main()
{
   shared_ptr<Foo> fa;
   {
        cout << "Inner scope begin\n";
        shared_ptr<Foo> f1 {new Foo{"foo"}};
        auto            f2 (make_shared<Foo>("bar"));
        cout << "f1's use counter at " << f1.use_count() << '\n';
        fa = f1;
        cout << "f1's use counter at " << f1.use_count() << '\n';
   }
    cout << "Back to outer scope\n";
    cout << fa.use_count() << '\n';
    cout << "first f() call\n";
    f(fa);
    cout << "second f() call\n";
    f(move(fa));
    cout << "end of main()\n";
}
Inner scope begin
CTOR foo
CTOR bar
f1's use counter at 1
f1's use counter at 2
DTOR bar
Back to outer scope
1
first f() call
f: use counter at 2
second f() call
f: use counter at 1
DTOR foo
end of main()

std::weak_ptr

class Foo
{
  public:
       string name;

   Foo(string n): name{move(n)}{ 
      cout << "CTOR " << name << '\n'; 
   }

   ~Foo() { cout << "DTOR " << name << '\n'; }
};

void weak_ptr_info(const weak_ptr<Foo> &p)
{
   cout << "---------" << boolalpha
   << "\nexpired:   " << p.expired()
   << "\nuse_count: " << p.use_count()
   << "\ncontent:   ";
   if (const auto sp (p.lock()); sp) {
      cout << sp->value << '\n';
   } else {
      cout << "<null>\n";
   }
}

The expired function of weak_ptr tells us if the object it points to still really exists, because holding a weak pointer to an object does not prolong its lifetime! The use_count counter tells us how many shared_ptr instances are currently pointing to the object in question:

int main()
{
   weak_ptr<Foo> weak_foo;
   weak_ptr_info(weak_foo);
   {
        auto shared_foo (make_shared<Foo>(1337));
        weak_foo = shared_foo;
        weak_ptr_info(weak_foo);
   }
   weak_ptr_info(weak_foo);
}
---------
expired: true
use_count: 0
content: <null>
---------
expired: false
use_count: 1
content: 1337
DTOR Foo 1337
---------
expired: true
use_count: 0
content: <null>