Search code examples
c++pointersmemory-managementsmart-pointersunique-ptr

Why does the pointer exist even after the unique_ptr to which the pointer is assigned goes out of scope?


I recently started learning about smart pointers and move semantics in C++. But I can't figure out why this code works. I have such code:

#include <iostream>
#include <memory>

using namespace std;

class Test
{
public:
    Test() 
    {
        cout << "Object created" << endl;
    }

    void testMethod()
    {
        cout << "Object existing" << endl;
    }

    ~Test() 
    {
        cout << "Object destroyed" << endl;
    }
};

int main(int argc, char *argv[])
{
    Test* testPtr = new Test{};
    {
        unique_ptr<Test> testSmartPtr(testPtr);
    }
    testPtr->testMethod();
    
    return 0;
}

My output is:

Object created
Object destroyed
Object existing

Why does row testPtr->testMethod() work? Doesn't unique_ptr delete the pointer assigned to it on destruction if the pointer is an lvalue?

Edit: I learned from the comments that this method doesn't check if the pointer exists. If so, is there a way to check if the pointer is valid?

Edit: I learned that I shouldn't do anything with invalid pointers. Thank you for all your answers and comments.

Edit: Even this code works:

#include <iostream>
#include <memory>

using namespace std;

class Test
{
public:
    Test(int num) :
        number{ num }
    {
        cout << "Object created" << endl;
    }

    void testMethod(int valueToAdd)
    {
        number += valueToAdd;
        cout << "Object current value: " << number << endl;
    }

    ~Test() 
    {
        cout << "Object destroyed" << endl;
    }

private:
    int number;
};

int main(int argc, char *argv[])
{
    Test* testPtr = new Test(42);
    {
        unique_ptr<Test> testSmartPtr(testPtr);
    }
    testPtr->testMethod(3);
    
    return 0;
}

I think it's because the compiler optimized it. Anyway, I really shouldn't do anything with invalid pointers.


Solution

  • You do not need a std::unique_ptr to write code with the same issue

    int main(int argc, char *argv[])
    {
        Test* testPtr = new Test{};
        delete testPtr;
        testPtr->testMethod();       // UNDEFINED !!!
        return 0;
    }
    

    The output is the same as yours here https://godbolt.org/z/8bocKGj1M, but it could be something else entirely. The code has undefined behavior. You shall not dereference an invalid pointer.

    If you had actually used members of the object in testMethod() some faulty output or a crash is more likely, but also not guaranteed. Looking ok is the worst incarnation of undefined behavior.

    Your code demonstrates nicely why you should ban raw new completely. At least you should call new only as parameter to the smart pointers constructor, or even better, use std::make_unique. Its basically just a wrapper around a constructor call via new and its main purpose is to let you write code that is free of new:

    int main(int argc, char *argv[])
    {
        auto testPtr = std::make_unique<Test>();
        testPtr->testMethod();    
        return 0;
    }
    

    Even then you can access the raw pointer and do wrong stuff. Smart pointers help with ownership, but they are not fool-proof.