How to use std::enable_shared_from_this

⚪ Together with the callback function: shared_from_this();


How to use std::enable_shared_from_this


std::enable_shared_from_this is a C++ functionality, added since C++11, that allows a class to create shared instances shared_ptr of itself.

This mechanism is useful when you need to create a shared_ptr inside a method of the class itself, especially to avoid duplication and maintain the correct reference count.


Usage

First of all, your class needs to inherit publicly(std::enable_shared_from_this<T>) for your class type, example:

class MyClass : public std::enable_shared_from_this<MyClass> {

The member function of this class that has return type: shared_ptr will no longer return *this but instead shared_from_this(), example:

  • The use of *this is not appropriate, because it does not increment the reference count of shared_ptr;
  • The use of *this can lead to memory management problems and possible dangling pointers (or wild pointers, which do not point to appropriate locations);
  • The use of *this is only appropriate for raw pointers and references.
// Bad idea
std::shared_ptr<MyClass> set_info() {
  return *this;
}

// Good idea
std::shared_ptr<MyClass> set_info() {
  return shared_from_this();
}

Examples

Let’s say you have this code that adds and increments members of a class, as we saw in this video:

#include <iostream>

template<class T>
class Vector2 {
    public:
        T x, y;
        Vector2(T xin, T yin) : x(xin), y(yin){}

        Vector2 operator + (const Vector2& rhs){
            return Vector2(x + rhs.x, y + rhs.y); 
        } 

        Vector2 & increment(int number){ 
            x += number;
            y += number;
            return *this;
        } 

        void print(){ 
            std::cout << x << " and " << y << '\n';
        }
};

int main (){ Vector2 v1(1, 2), v2(3, 4);
    Vector2 v3 = v1 + v2;
    std::cout << "v3.x: " << v3.x << '\n'; // 4 
    std::cout << "v3.y: " << v3.y << '\n'; // 6 
    // Or just: 
    v3.print(); // 4 and 6 
    v3.increment(5);
    v3.print(); // 9 and 11 return 0;
}

Note that the Vector2 class has a member function: increment which is a reference to itself and returns a *this!

Translating this code to use std::enable_shared_from_this, it would look like this:

#include <iostream>
#include <memory>

template<class T>
class Vector2 : public std::enable_shared_from_this<Vector2<T>> {
    public:
        T x, y;
        Vector2(T xin, T yin) : x(xin), y(yin) {}

        Vector2 operator + (const Vector2& rhs) {
            return Vector2(x + rhs.x, y + rhs.y);
        }

        std::shared_ptr<Vector2> increment(int number) {
            x += number;
            y += number; 
            return this->shared_from_this();
        } 

        void print() { 
            std::cout << x << " and " << y << '\n';
        } 
};

int main() { auto v1 = std::make_shared<Vector2<int>>(1, 2);
    auto v2 = std::make_shared<Vector2<int>>(3, 4);
    Vector2<int> v3 = *v1 + *v2;
    std::cout << "v3.x: " << v3.x << '\n'; // 4 
    std::cout << "v3.y: " << v3.y << '\n'; // 6 
    // Or just: v3.print(); // 4 and 6 
    auto v3_ptr = std::make_shared<Vector2<int>>(v3);
    v3_ptr->increment(5);
    v3_ptr->print(); // 9 and 11

    return 0;
}

Note that we inherited publicly: std::enable_shared_from_this and the type of increment is now std::shared_ptr and returns: shared_from_this().

From this code we have automatic reference management and we can even count them, example:

std::cout << "Number of references (how many times we instantiate/create object/pointer) for v1: "
<< v1.use_count() << '\n'; // 1

std::cout << "Number of references (how many times we instantiate/create object/pointer) for v2: "
<< v2.use_count() << '\n'; // 1

std::cout << "Number of references (how many times we instantiate/create an object/pointer) for v3_ptr: "
<< v3_ptr.use_count() << '\n'; // 1

Much more modern and like a boss!

For more information, check out the links below!



cpp cppdaily


Share


YouTube channel

Subscribe


Marcos Oliveira

Marcos Oliveira

Software developer
https://github.com/terroo

Related articles