Search code examples
c++vectorshared-ptrweak-ptr

Accessing weak_ptr after deleting the original pointer


I get a runtime error when I execute the below code:

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

using namespace std;

class Test
{
public:
    int value;
    Test( )
    {
        value = 1;
    }

    ~Test( )
    {

    }
};

int main() {
    shared_ptr<Test> pTest =  shared_ptr<Test>( new Test( ) );
    std::vector<weak_ptr<Test>> testList;

    testList.push_back( weak_ptr<Test>(pTest) );
    cout << "\n Before Deletion";
    for (unsigned int i=0; i < testList.size(); i++)
    {
        try
        {
            auto p = testList[i].lock();
            cout << "\n Item not null: " << p->value;
        }
        catch (bad_weak_ptr b)
        {
            wcout << L"Null object" << endl;
        }
    }

    pTest.reset( );

    cout << "\n After Deletion";
    for (unsigned int i=0; i < testList.size(); i++)
    {
        try
        {
            auto p = testList[i].lock();
            cout << "\n Item not null: " << p->value;
        }
        catch (bad_weak_ptr b)
        {
            wcout << L"Null object" << endl;
        }
    }

    // your code goes here
    return 0;
}

I was trying to find whether the pointer is still accessible (shouldn't be) after deleting the original shared pointer.


Solution

  • Lock doesn't check for null, it promotes a weak_ptr to a shared pointer. It doesn't throw when the object is deleted, but rather returns null.

    This is what you should be doing.

    #include <iostream>
    #include <vector>
    #include <memory>
    
    using namespace std;
    
    class Test
    {
    public:
        int value;
        Test( )
        {
            value = 1;
        }
    
        ~Test( )
        {
    
        }
    };
    
    int main() {
        shared_ptr<Test> pTest =  shared_ptr<Test>( new Test( ) );
        std::vector<weak_ptr<Test>> testList;
    
        testList.push_back( weak_ptr<Test>(pTest) );
        cout << "\n Before Deletion";
        for (unsigned int i=0; i < testList.size(); i++)
        {
          auto p = testList[i].lock();
            if (p)
            {
    
                cout << "\n Item not null: " << p->value;
            }
            else
            {
                wcout << L"Null object" << endl;
            }
        }
    
        pTest.reset( );
    
        cout << "\n After Deletion";
        for (unsigned int i=0; i < testList.size(); i++)
        {
          auto p = testList[i].lock();
            if (p)
            {
    
                cout << "\n Item not null: " << p->value;
            }
            else
            {
                wcout << L"Null object" << endl;
            }
        }
    
        // your code goes here
        return 0;
    }
    

    To clarify, what happens when you to test_list>lock after pTest is reset is you get a shared_ptr which contains NULL. and ((Test*)NULL)->value is what happen when you do p->value, which is an obvious segfault.

    The whole point of having a weak_ptr is to allow users the ability to safely get a reference to an object that may go out of scope. Exceptions would be a poor mechanism for this because exceptions are SLOW, and it is not necessarily a fatal error if the weak_ptr's parent goes out of scope. So the mechanism is lock either returns a new counted reference when promoted, or NULL if it can no longer promote the ownership level (the parent has expired).