How to create and use shared pointer in C++?

In this tutorial, you will learn “how to create and use a shared pointer in C++”. This tutorial will be specific to shared pointers, so the primary pre-requisite of this tutorial is that you should have basic knowledge about pointers and smart pointers.

If you don’t have the basic knowledge of pointers and smart pointers, you should read the below-mentioned articles before reading this article.

 

What is a shared pointer in C++?

A shared_ptr is used to represent shared ownership. It is a type of smart pointer that is designed for scenarios in which the lifetime of the object in memory is managed by more than one owner.

Like the unique_ptr, shared_ptr is also defined in the <memory> header in the C++ Standard Library. Because it follows the concept of shared ownership, after initializing a shared_ptr you can copy it, assign it or pass it by value in function arguments. All the instances point to the same allocated object.

The shared_ptr is a “reference counted pointer“. A reference counter is increased whenever a new shared_ptr is added and decreases whenever a shared_ptr goes out of scope or is reset. When the reference count reaches zero, the pointed object is deleted. It means the last remaining owner of the pointer is responsible for destroying the object.

The conclusion of the above statement is that the owned object is destroyed when either of the following happens:

1. The last remaining shared_ptr owning the object is destroyed ( reference count is zero).
2. The last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().

 

The following example shows how the shared_ptr instance point to the allocated memory location and reference count increases from 0 to 1.

What is shared pointer in C++

Remark: A shared_ptr is said to be empty if it does not own a pointer.

 

Syntax of a shared pointer in C++:

//since C++11

template< class T > class shared_ptr;

Where,

shared_ptr is a smart pointer that retains shared ownership of an object through a pointer.

 

How to create an instance of shared_ptr?

The below-mentioned example shows how to create instances of a shared pointer.

/*
  Object ptr owns dynamically allocated int
*/
std::shared_ptr<int> ptr(new int);

shared_ptr creation

Now ptr is owing to the memory of unnamed integer object. Using  ptr you can access this allocated memory.

Remark: You can also create a shared pointer with std::make_shared. See the below expressions.

std::shared_ptr<int> ptr = std::make_shared<int>();

                   OR

auto ptr = std::make_shared<int>();

 

The dynamically allocated object is destroyed when the created shared pointer object is destroyed (If it is a single owner). See the below example code.

void foo()
{
  shared_ptr<int> ptr (new int);

} <<---- ptr is destructed outside of this of curly braces.

The object ptr is a stack-allocated object. When control goes out of the scope, it destroys automatically and also destroys the dynamically allocated unnamed int object if it is the last remaining owner of the pointer.

Destroying the own object last owner shared pointer

 

Shared Ownership of shared_ptr:

It follows the concept of shared ownership. This means an allocated object can be shared by more than once shared pointers. After initializing a shared_ptr you can copy it, assign it or pass it by value in function arguments. Each instance will point to the same allocated object.

The below example shows how to declare and initialize a shared pointer instance that shares the ownership of an object which is already owned by another shared_ptr.

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    /*
    Create an shared ptr
    object that store the pointer to
    the int object
    */
    shared_ptr<int> ptr1(new int);

    //returns a pointer to the managed object
    cout << "ptr1.get() = "<< ptr1.get() << endl;

    //print the reference count
    cout << "ptr1.use_count() = " << ptr1.use_count() << endl;


    cout <<"\nCreate another shared pointer "
         "and Initialize with copy constructor.\n";
    /*
     Second shared_ptr object will also point to same pointer internally
     It will make the reference count to 2.
    */
    shared_ptr<int> ptr2(ptr1);

    //print the reference count and manged object
    cout << "ptr1.get() = "<< ptr1.get() << endl;
    cout << "ptr2.get() = "<< ptr2.get() << endl;
    cout << "ptr1.use_count() = " << ptr1.use_count() << endl;
    cout << "ptr2.use_count() = " << ptr2.use_count() << endl;


    return 0;
}

 Output:

Ownership of shared ptr

 

The ptr1 is the first shared pointer which is owing to an unnamed int object. The reference count value is 1. Th ptr2 to is the second shared pointer which is sharing the allocating object with ptr1. Now the reference count value is 2 because the allocated object is shared by two owners.

shared pointers shared an int object c++ aticleworld

 

In the above example, I Initialize ptr2 with ptr1 by copy constructor. If you want you can normally assign ptr1 to ptr2. See some more examples.

//Initialize via assignment. Increments ref count.
auto ptr2 = ptr1;


//Initialize with copy constructor. Increments ref count.
auto ptr2(ptr1);


//Initialize with nullptr. ptr2 is empty.
 shared_ptr<int> ptr2(nullptr);

//Initialize via assignment. Increments ref count.
 ptr2 = ptr1;

 

 

Different Operations supported by shared_ptr:

You will see different operations supported by the share pointers with the help of programming examples.

Get the stored pointer:

By calling  get()  we can get the stored pointer. The get function returns the stored pointer. See the below example,

#include <iostream>
#include <memory>
using namespace std;

int main ()
{
    /*
    Create an share pointer
    object that store the pointer to
    the unnamed int object
    */
    int* ptr1 = new int (27);

    /*
      Shared the allocated object
      with another shared pointer
    */
    shared_ptr<int> ptr2 (ptr1);

    if (ptr2.get()==ptr1)
    {
        cout << "ptr2 and ptr1 point to the same location\n";
    }

    /*
      Ways of accessing the same address.
      Remember get() != 0.
    */
    cout << *ptr2.get() << "\n";
    cout << *ptr2 << "\n";
    cout << *ptr1 << "\n";

    return 0;
}

Output:

ptr2 and ptr1 point to the same location
27
27
27

 

Remark: Do not explicitly delete the raw pointer because it is shared by the smart pointer. You might get UB.

 

Resetting a shared_ptr:

The reset() member function replaces the managed object with an object pointed to by p. Let’s see the reset member function with different signatures supported by the shared pointer.

1. void reset() noexcept;

2. template<class Y> void reset(Y* p);

3. template<class Y, class D> void reset(Y* p, D d);

4. template<class Y, class D, class A> void reset(Y* p, D d, A a);

Parameters:

p - pointer to an object to acquire ownership of
d - deleter to store for deletion of the object
A - allocator to use for internal allocations

Calling reset function with signature_1 releases the ownership of the managed object. You must remember calling the reset() eliminating one owner of the pointer, but all of the other owners are still owning the object. See the below example code.

#include <iostream>
#include <memory>
using namespace std;
class Test
{
public:
    ~Test()
    {
        cout << "Test destroyed." << endl;
    }


};
int main()
{
    std::shared_ptr<Test> p = std::make_shared<Test>();
    std::shared_ptr<Test> q = p;
    cout << "p.reset()...\n";
    p.reset();
    cout << "q.reset()...\n";
    q.reset();
    cout << "end of the code...\n";
    return 0;
}

Output:

p.reset()...
q.reset()...
Test destroyed. ->>> Destructor Calling after releasing from both owner.
end of the code...

 

In all other cases, the shared pointer acquires ownership  p with a use count of 1. If the object pointed to by p is already owned, the function generally results in undefined behavior. See another example where we acquire a new pointer.

#include <iostream>
#include <memory>
using namespace std;
class Test
{
public:
    ~Test()
    {
        cout << "Test destroyed.\n" << endl;
    }


};
int main()
{
    cout << "Created new Test Object\n";
    shared_ptr<Test> ptr = std::make_shared<Test>();

    cout << "use_count() = "<< ptr.use_count()
         << ", ptr = " << ptr <<"\n\n";

    // deletes old managed object, acquires new pointer
    std::cout << "call ptr.reset()...\n";
    ptr.reset(new Test());

    std::cout << "After reset(): use_count() = " << ptr.use_count()
              << ", ptr = " << ptr << "\nLeaving the scope...\n";

    return 0;
}

Output:

share pointer with reset in C++

 

Calling swap():

Calling the swap()member function exchanges the stored pointer values and the ownerships of *this and r. The reference counts also transfer without any altering and destroying.

/*
  Syntax of shared_ptr swap() member function.
*/

void swap(shared_ptr& r) noexcept;

Parameters:

r - Another shared_ptr object.

 

The following example shows the working of the swap() member function. In which I am swapping two shared_ptr with the help of swap().

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    shared_ptr<int> ptr1(new int(27));
    shared_ptr<int> ptr2(new int(6));

    cout << "print pointers owned by shared_ptrs...\n";
    //returns a pointer to the managed object
    cout << "ptr1.get() = "<< ptr1.get() << endl;
    cout << "ptr2.get() = "<< ptr2.get() << endl;

    cout << "print value the which owned by shared_ptrs...\n";
    cout << "*ptr1 = "<< *ptr1 << endl;
    cout << "*ptr2 = "<< *ptr2 << endl;

    cout << "Calling swap on shared_ptrs...\n";
    ptr1.swap(ptr2);

    cout << "AFTER SWAP:- print pointers owned by shared_ptrs...\n";
    //returns a pointer to the managed object
    cout << "ptr1.get() = "<< ptr1.get() << endl;
    cout << "ptr2.get() = "<< ptr2.get() << endl;

    cout << "AFTER SWAP:- print value the which owned by shared_ptrs...\n";
    cout << "*ptr1 = "<< *ptr1 << endl;
    cout << "*ptr2 = "<< *ptr2 << endl;

    return 0;
}

Output:

Swap with shared pointer C++

 

Checking empty shared_ptr in C++:

We can check whether a shared_ptr is associated with an object or not.

#include <iostream>
#include <memory>
using namespace std;

int main ()
{
    shared_ptr<int> ptr1;
    shared_ptr<int> ptr2 (new int(27));
    
    //Check first shared pointer
    if (ptr1)
    {
        std::cout << "ptr1 points to " << *ptr1 << '\n';
    }
    else
    {
        std::cout << "ptr1 is empty\n";
    }
    //Check second shared pointer
    if (ptr2)
    {
        std::cout << "ptr2 points to " << *ptr2 << '\n';
    }
    else
    {
        std::cout << "ptr2 is empty\n";
    }
    return 0;
}

Output:

ptr1 is empty
ptr2 points to 27

 

How to Pass to a shared pointer in function:

The following examples show how to create shared_ptr instances and pass them between functions. Here we will see the three function signatures to pass the shared_ptr.

Pass the shared_ptr by value:

If you want to share the ownership with the function, you should pass the shared_ptr by value. Otherwise, there is no reason to pass by value because it introduces a small amount of overhead. The pass-by-value invokes the copy constructor, increments the reference count, and makes the callee an owner.

#include <iostream>
#include <memory>
using namespace std;

class Test
{
public:
    Test()
    {
        cout<<"Object Created\n";
    }
    ~Test()
    {
        cout<<"Object Destroyed\n";
    }

private:
};


//function printing the value
void foo(shared_ptr<Test> p)
{
    // p is a shared owner.
    //print reference count:2
    cout<< p.use_count()<<endl;
}


int main()
{
    auto ptr = make_shared<Test>();

    //print reference count: 1
    cout<< ptr.use_count()<<endl; // 1

    //passing shared pointer in function foo()
    foo(ptr);

    //print reference count: 1
    cout<< ptr.use_count()<<endl;

    return 0;
}

Output:

Object Created
1
2
1
Object Destroyed

 

Pass the shared_ptr by reference:

If you will pass the shared pointer by reference, the reference count will not increment. So it will not share the ownership. Here you can not give the guarantee that the allocated resource will stay alive during the execution of this function, but you can reseat the resource. Reseat means “making a reference or a smart pointer refers to a different object”. See the below example,

void foo(std::shared_ptr<Test>& ptr)
{
    // This will change the resource of caller
    ptr = std::make_shared<Test>();
}

 

Pass the shared_ptr by const reference:

Use a const shared_ptr& as a parameter only if you’re not sure whether or not you will take a copy and share ownership. Otherwise use Test* or Test& (if not nullable) instead. If you want to modify the shared_ptr, use only a non-const shared_ptr& as a parameter.

 

Return shared pointer from a function:

You can return a shared_ptr from a function. See the below code.

#include <iostream>
#include <memory>
using namespace std;

//function printing the value
shared_ptr<int> foo()
{
    shared_ptr<int> ptr = shared_ptr<int>(new int(27));
    /* you can also use below expression
     auto ptr = make_shared<int>(27);
     */
    return ptr;
}


int main()
{
    shared_ptr<int> ptr  = foo();
    // true: ptr has an object.
    if (ptr)
    {
        cout<< "ptr owned an object.\n";
        cout <<"*ptr = " << *ptr;
    }
    return 0;
}

Output:

ptr owned an object.
*ptr = 27

 

 

How to initialize a shared pointer in C++ if it is a class member?

The below code shows how to initialize a shared_ptr that is a class member.

#include <iostream>
#include <memory>
using namespace std;


class MyTest
{
public:
    void doSomething()
    {
        cout << "Share this post\n";
    }
};

class Test
{
private:
    // Test owns the shared_ptr.
    shared_ptr<MyTest> m_ptr;
public:
    /* Initialize by using make_unique
       with MyTest default constructor.
    */
    Test(shared_ptr<MyTest> ptr) : m_ptr (ptr)
    {
    }
    void callMytestFun()
    {
        m_ptr->doSomething();
        cout<< "m_ptr.use_count() = " << m_ptr.use_count()<<endl;
    }
};


int main()
{
    //create class object
    Test test(make_shared<MyTest>());

    //calling function of Mytest
    test.callMytestFun();

    return 0;
}

Output:

Share this post
m_ptr.use_count() = 1

 

Recommended Articles for you:

References:
Dynamic memory management.

Leave a Reply

Your email address will not be published. Required fields are marked *