Dangling pointer example
Asked Answered
L

4

5

In the following code, why does s1.printVal causes a dangling pointer error? Isn't the s1 object, i.e. its pointer, still accessible until it's destroyed?

class Sample
{
  public:
    int *ptr;
    Sample(int i)
    {
        ptr = new int(i);
    }

    ~Sample()
    {
        delete ptr;
    }
    void PrintVal()
    {
        cout << "The value is " << *ptr;
    }
};

void SomeFunc(Sample x)
{
    cout << "Say i am in someFunc " << endl;
}

int main()
{
    Sample s1 = 10;
    SomeFunc(s1);
    s1.PrintVal(); // dangling pointer
}

Source

Louislouisa answered 12/8, 2010 at 13:58 Comment(1)
Not a very "correct" example. It illustrates the dangling pointer problem and what the lack of copy ctor\copy assignment operator does to abstractions holding pointers to dynamically allocated memory but the implicit conversion from int just gives me the creeps.Sorayasorb
H
14

The problem here is the copy that is done for argument of the SomeFunc(). That copy de-allocates your pointer when destroyed. You need to also implement a copy constructor, and copy assignment operator. See rule of three.

Edit:

Here's "expanded" pseudo-code, i.e. what the compiler does for you in the main() function:

// main
addr0 = grab_stack_space( sizeof( Sample )); // alloc stack space for s1
Sample::ctor( addr0, 10 );                   // call ctor of Sample
addr1 = grab_stack_space( sizeof( Sample )); // alloc stack for argument
Sample::ctor( addr1, addr0 );                // call COPY-ctor of Sample
SomeFunc( addr1 );                           // call SomeFunc
Sample::dtor( addr1 );                       // XXX: destruct the copy
free_stack_space( addr1, sizeof( Sample ));  // free stack taken by copy
Sample::PrintVal( addr0 );                   // call member func on s1
Sample::dtor( addr0 );                       // destruct s1
free_stack_space( addr0, sizeof( Sample ));  // YYY: free stack taken by s1

This is not the exact representation, but a conceptual explanation. It just helps to think in terms of what compiler has to do with your code.

The pointer member of Sample is delete-ed at the step marked with XXX, and then delete-ed again at the step YYY.

Hummock answered 12/8, 2010 at 14:1 Comment(0)
S
3

Nikolai's answer explains everything, but here is a possible alternative:

If you intend multiple instances of Sample to share the same underlying pointer, you may also use something like boost::shared_ptr instead of a raw pointer.

This has a little cost, but probably no more than if you try to do it yourself.

Moreover, this would avoid the need to write any of the copy constructor, destructor and assignement operator.

Shaunna answered 12/8, 2010 at 14:28 Comment(0)
E
2

When you call SomeFunc(Sample x), the object x is created by calling Sample's copy-constructor. Since you didn't explicitly write one, the compiler creates an implicit one. Normally, the implicit one is fine: It does a member-by-member copy (If you had used a vector<int> instead of an int* your code would have worked). However, in this case, it's no good. It just copies that value of ptr, so now, x.ptr & s1.ptr point to the same int[]. Now, when SomeFunc ends, x is destroy, and the destructor is called on it, meaning ptr is deleted. That frees the memory used by x, but since it's the same value, it's also the memory used by s1.

Edythedythe answered 12/8, 2010 at 14:9 Comment(0)
B
1

Nikolai's answer is absolutely right. As is ereOn's.

You also need to consider the difference between pass-by-value and pass-by-reference.

If SomeFunc was declared as:

void SomeFunc(Sample& x)

or even better

void SomeFunc(const Sample& x)

you would not have a dangling pointer problem.

The way you have defined SomeFunc, the Sample object is passed by value, which means a temporary copy is made for use within the scope of SomeFunc. Then, when SomeFunc returns the temporary object goes out of scope and its destructor gets called, which deletes the integer pointed to by ptr.

If you pass a reference to the Sample object, then no copies get made while calling SomeFunc and, hence, no destructor gets called when SomeFunc returns.

Baculiform answered 12/8, 2010 at 14:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.