c++pointersdelete-operator

Can I re-use a pointer in this case?


Suppose:

struct Foo
{
    Obj* pObj;
    Foo() : pObj(NULL);
};

Obj* CreateObj()
{
   //do some stuff and then
   return new Obj; //obj is a class
}

int main()
{
   Foo foo;
   foo.pObj = CreateObj();
   DoSomeOperationWithTheObj( foo.pObj );
   //suppose foo is a monster that should be 'killed' or deleted now
   delete foo.pObj;
   foo.pObj = NULL;
   //the question is can this pointer be 're-used' now like this:
   foo.pObj = CreateObj(); //create another object
}

Since the pointer was deleted, isn't there a problem re-using it right?


Solution

  • As for your original question: yes, you can reassign to pointers like that. A pointer holds just a memory address and nothing more.

    But you should not actually ever do this because handling raw pointers like this can lead to bugs, you already have a few of those in your code. Modern C++ allows you to do this way more nice and without concern. Suppose we start from this (compilable) code, i replaced Obj by an int, but the fact that it's a native type instead of a class does not matter:

    #include <iostream>
    
    struct Foo
    {
        int* pObj;
        Foo() : pObj(NULL) {}
    };
    
    int* CreateObj()
    {
       return new int(42); //obj is a class
    }
    
    int main()
    {
       Foo foo;
       foo.pObj = CreateObj();
       std::cout << *foo.pObj << std::endl;
       delete foo.pObj;
       foo.pObj = new int(13);
       std::cout << *foo.pObj << std::endl;
       delete foo.pObj;
    }
    

    We can convert this to the following:

    #include <iostream>
    #include <memory>
    
    struct Foo
    {
        std::unique_ptr<int> pObj;
        Foo() : pObj(NULL) {}
    };
    
    std::unique_ptr<int> CreateObj()
    {
       return std::unique_ptr<int>(new int(42));
    }
    
    int main()
    {
       Foo foo;
       foo.pObj = CreateObj();
       std::cout << *foo.pObj << std::endl;
       foo.pObj = std::unique_ptr<int>(new int(13));
       std::cout << *foo.pObj << std::endl;
    }
    

    Note that the major change is that I removed raw pointers and replaced them with the unique_ptr wrapper. This has a few advantages:

    1. You clearly state ownership, a unique_ptr can only be owned by the current scope. While createObj creates the object, by returning the temporary (nameless) unique_ptr it releases ownership so the caller can delete it whenever it wants. This will avoid tricky memleaks.
    2. Deletes happen automatically for you, either when the unique_ptr goes out of scope or when it is overridden (e.g. by the assignment operator).