Creating shared_ptr from raw pointer in C++

Recently I was writing some C++ code and one thing I needed to do is to pass a pointer of an object within itself to another function. At first, I was using raw pointers and everything worked fine. However, later as I decided to use std::shared_ptr instead, I encounter error where the same pointer was freed twice. After some search online, I discovered that I need std::enable_shared_from_this.

What’s causing the problem?

When I first learnt about std::shared_ptr, I knew that it could keep track of how many references of the same pointer are there and will only free the resources when the reference count goes to 0. I didn’t think too much about how this is actually implemented back then, but obviously this is not some magic. The reason why it can keep track is because std::shared_ptr has a pointer to a struct that stores the reference count besides the pointer to the actual object. Therefore, when you create a std::shared_ptr from another one, it will increment the count properly (the two std::shared_ptrs point to the same struct). If you create two std::shared_ptr from the same raw pointer, although they actually point to the same resource, they have no way of knowing it!

#include <memory>
#include <iostream>

using namespace std;

struct Obj
{
    ~Obj() {cout << "Destructor called on " << this << endl;}
};

int main()
{
    Obj *ptr = new Obj();
    shared_ptr<Obj> sptr_1(ptr);
    shared_ptr<Obj> sptr_2(ptr);
}
$ g++ -std=c++11 -o double_free double_free.cc
$ MALLOC_CHECK_=1 ./double_free
Destructor called on 0x602010
Destructor called on 0x602010
*** glibc detected *** ./double_free: free(): invalid pointer: 0x0000000000602010 ***

When I was creating a std::shared_ptr from this inside an object, this is exactly the problem that I had in my code.

P.S. I must set MALLOC_CHECK_ in order to force the check on double free. Otherwise the program finishes without error although the desctructor is clearly called twice on the same object.

std::enable_shared_from_this to the rescue

When you are not creating a std::shared_ptr of an object inside itself, you can obviously avoid the problem by creating std::shared_ptr from existing ones. How do we solve the problem if you must create inside the object? Turns out, there is something called std::enable_shared_from_this in the standard library. This is a class, which if you inherite your class from, allows you to create std::shared_ptr from this with the right reference counting.

#include <memory>
#include <iostream>

using namespace std;

struct Obj : public enable_shared_from_this<Obj>
{
        shared_ptr<Obj> Get() {return shared_from_this();}
        ~Obj() {cout << "Destructor called on " << this << endl;}
};

int main()
{
        shared_ptr<Obj> sptr_1 = make_shared<Obj>();
        shared_ptr<Obj> sptr_2 = sptr_1->Get();
}
$ g++ -std=c++11 -o enable_shared_from_this enable_shared_from_this.cc
$ MALLOC_CHECK_=1 ./enable_shared_from_this
Destructor called on 0x604020

As demonstrated in the example above, the destructor is only called once although we are creating a std::shared_ptr inside the object.

I haven’t dig into the source code of libc myself to understand how this is actually implemented, but I saw this post on StackOverflow explaning how it could be done. Basically, std::enable_shared_from_this can hold a std::weak_ptr (this is an excellent use case of std::weak_ptr in my opinion) to the std::shared_ptr when the std::shared_ptr is constructed. std::enable_shared_from_this::shared_from_this() can then create new std::shared_ptrs based on those information, thus having the correct reference count.

Conclusion

In this blog post, I explained why you would run into trouble by creating std::shared_ptr from raw pointer in some cases. I also described how to avoid this problem with the use of std::enable_shared_from_this. I hope you have a better understanding of std::shared_ptr after reading this post!

References

 
comments powered by Disqus