2
votes

I was reading about smart pointers in C++ and saw this example given as how smart pointers handle "dangling pointer" problem if normal pointers are used. P.S. I know auto_ptr is deprecated by C++ 2011 but it still has unique_ptr or some such equivalent of it.

Dangling pointers. A common pitfall of regular pointers is the dangling pointer: a pointer that points to an object that is already deleted. The following code(jsut for example purpose) illustrates this situation:

    MyClass* p(new MyClass);//Assume we have a MyClass definition available
    MyClass* q = p;
    delete p;
    p->DoSomething();   
    p = NULL;           // p is not dangling anymore
    q->DoSomething();   // Don't.. q is still dangling!

Using auto_ptr, this is solved by setting its pointer to NULL when it is copied (Copy constructor implemented as below):

template <class T>
auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs)
{
    if (this != &rhs) {
        delete ptr;
        ptr = rhs.ptr;
        rhs.ptr = NULL;
    }
    return *this;
}

And now if we have the same code using auto_ptr as below:

auto_ptr<MyClass> p(new MyClass);
auto_ptr<MyClass> q = p;
delete p;
p->DoSomething();   
p = NULL;           
q->DoSomething();   

I get the logic in the aqbove copy constructor but how does making the rhs.ptr = NULL help. I mean if one dereferences this pointer later (q in above code example), it would crash as it is made NULL by the smart pointer copy constructor.

Where as what would have happened if it was a normal pointer and we would have dereferenced a dangling pointer? Some undefined behavior maybe. But how does using auto_ptr help

5
Your first example is already undefined behavior before anything is dangling. Try to understand regular pointers first.pmr
@pmr - Would you please write a line explaining how is that.goldenmean
You new up one object. Point with two different pointers at it. delete it and then call a method on it.pmr
I don't know whether you've miscopied or what, but the initial examples are broken. The initial one accesses through a dangling pointer, the example code for std::auto_ptr isn't a legal implementation (and would fail on self-assignment if it were), and the last example does a delete on an auto_ptr, which shouldn't even compile.James Kanze

5 Answers

9
votes

Smart pointers won't necessarily protect you if you dereference them without checking them first; what they give you is a guarantee that you can check whether they are valid before dereferencing.

The problem with a dangling pointer is that it doesn't point to anything valid, but there's no way to tell:

Thing * p = new Thing;
Thing * q = p;

if (p) p->DoSomething(); // OK: checks p, calls function
if (q) q->DoSomething(); // OK: checks q, calls function

delete p;
p = nullptr;

if (p) p->DoSomething(); // OK: checks p, does nothing
if (q) q->DoSomething(); // BOOM! Undefined behaviour

With a smart pointer, you'll never have that situation; either the pointer is valid, or you can tell that it's invalid before dereferencing:

auto_ptr<Thing> p(new Thing);
auto_ptr<Thing> q = p;

if (p) p->DoSomething(); // OK: checks p, does nothing
if (q) q->DoSomething(); // OK: checks q, calls function

p.reset(); // does nothing

if (p) p->DoSomething(); // OK: checks p, does nothing
if (q) q->DoSomething(); // OK: checks q, calls function

q.reset(); // deletes the object

if (p) p->DoSomething(); // OK: checks p, does nothing
if (q) q->DoSomething(); // OK: checks q, does nothing
3
votes

With auto_ptr you can specifically test for NULL to make sure that the pointer is actually pointing to something. The copying of ownership of the pointer value through the copy-constructor and operator= method ensure that if ownership of the pointer is "moved", the pointer will actually be NULL. Finally, when an auto_ptr goes out-of-scope, it will free the memory for the pointer that it owns if the internal pointer is not set to NULL (i.e., ownership has not been relinquished).

On the other-hand, with naked pointers, you cannot know for sure if the pointer is dangling, or who "owns" the pointer unless you take care to make sure to set your pointers to NULL after deletion, etc... but doing this manually is not exception-safe. On the other-hand, the interface of auto_ptr makes the "ownership" of the pointer explicit, and makes dynamic memory allocation exception-safe.

3
votes

First, it prevents your code from compiling. The statement delete p is invalid because you can't delete an auto_ptr. When you store an address in a smart pointer, you should no longer call delete on it; any call to delete is immediately a cause for closer inspection of the code — it just looks wrong.

Once you remove the delete statement, you'll get run-time errors sooner. After initializing q with p, p contains a null pointer. Thus, p->DoSomething() calls the method on a null pointer, and that should cause a crash sooner than you'd get in your original code, where you call a method on a stale, invalid pointer. (Calling methods on null pointers causes undefined behavior. The typical result is a crash, such as an access violation or a segmentation fault. Calling methods on invalid pointers is undefined, too, but typically results in more subtle errors, such as discarded data or incorrect calculations, not crashes.)

2
votes

Well first of all the example is wrong because it calls p->DoSomething() after deleting p. Secondly when using auto_ptr you won't call delete directly, that's taken care of by auto_ptr's destructor. And thirdly, the point of auto_ptr is that it disallows multiple copies of the pointer, so it's not possible for there to be a dangling pointer. A null pointer exception is preferable to a dangling pointer, because the behavior is defined.

1
votes

With a shared_ptr you never manually delete the memory it is managing; it is automatically deleted when the last reference to it goes out of scope. This makes a dangling pointer pretty much impossible unless you manually reset() it.