Search code examples
c++constructorshared-ptrweak-ptr

Automatically adding and removing an object from a list


I have a class. When this class is instantiated, I want the instance added to a list. When the object is deleted, I want it removed from the list.

So I give the object a shared pointer to itself. I then have a list of weak pointers to those shared pointers. When an object is created, it creates a shared pointer to itself, makes a weak pointer to that, and puts the weak pointer in a list.

When the object is destroyed, the shared pointer is as well. Whenever I try to access a member in the list, I ensure that it hasn't expired and that its use count isn't 0. Despite this, I still crash when the list member is destroyed. Why? Can I get around it? Here's my SSCCE:

#include <iostream>
#include <memory>
#include <vector>

class test
{
    private:
        std::shared_ptr<test> self;

    public:
        int val;
        test(int set);

        test(test &copy) = delete; // making sure there weren't issues 
                                   // with a wrong instance being deleted
};

std::vector<std::weak_ptr<test>> tests;

test::test(int set):
    val(set)
{
    this->self = std::shared_ptr<test>(this);

    tests.push_back(std::weak_ptr<test>(this->self));
}

void printTests()
{
    for (auto i = tests.begin(); i != tests.end(); i++)
    {
        if (i->use_count() == 0 || i->expired())
        {
            tests.erase(i);
            continue;
        }

        std::cout << i->lock()->val << std::endl;
    }

    std::cout << std::endl;
}

int main(int argc, char **argv)
{
    {
        test t(3);

        std::cout << "First tests printing: " << std::endl;

        printTests();
    } // SEGFAULTS HERE

    std::cout << "Second tests printing: " << std::endl;
    printTests();

    return 0;
}

The output of this program is as follows:

First tests printing:
3

Segmentation fault (core dumped)

Solution

  • Your issue is with how you are creating the self pointer:

     this->self = std::shared_ptr<test>(this);
    

    When a shared_ptr is created with this constructor, according to the documentation,

    When T is not an array type, constructs a shared_ptr that owns the pointer p. ... p must be a pointer to an object that was allocated via a C++ new expression or be 0

    So the issue is that the shared_ptr is taking ownership of your stack object, so when the object gets destructed (and the shared_ptr along with it), shared_ptr is trying to delete your object that is on the stack. This is not valid.

    For your use case, if you expect tests to outlive your vector, then you might be able to just store this.