Turn Signal Example

In the following code, why does s1.printVal cause a dangling pointer error? Is not the object s1 , i.e. Is his pointer still accessible until it is 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 } 

A source

+6
c ++ dangling-pointer
source share
4 answers

The problem here is copy , which is executed for the SomeFunc() argument. This copy unselects the pointer when it is destroyed. You also need to implement the copy constructor and copy assignment operator . See the rule of three .

Edit:

Here's the "extended" pseudo-code, that is 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 an accurate representation, but a conceptual explanation. It just helps to think that the compiler is related to your code.

The Sample pointer element is delete -ed at step marked XXX , and then delete -ed again at step YYY .

+15
source share

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

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

This has a small cost, but probably nothing more if you try to do it yourself.

In addition, this avoids the need to write any of the copy constructor, destructor, and assignment operator.

+3
source share

When you call SomeFunc(Sample x) , the x object is created by calling Sample copy-constructor. Since you are not explicitly writing one, the compiler creates an implicit. There is usually an implicit one: it performs a phased copy (if you used vector<int> instead of int* , your code would work). However, in this case it is not good. It just copies this ptr value, so now x.ptr and s1.ptr point to the same int []. Now that SomeFunc ends, x destroyed, and the destructor is called on it, which means ptr . This frees up the memory used by x , but since this is the same value, it is also the memory used by s1 .

+2
source share

Nikolai’s answer is absolutely right. Like ereOn.

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

If SomeFunc was declared as:

void SomeFunc(Sample& x)

or even better

void SomeFunc(const Sample& x)

you would not have a problem with a dangling pointer.

As you defined SomeFunc , the Sample object is passed by value, which means that the temporary copy is intended to be used within the scope of SomeFunc . Then, when SomeFunc returns a temporary object, it goes out of scope, and its destructor is called, which removes the integer pointed to by ptr .

If you pass a reference to the Sample object, then when SomeFunc called SomeFunc copies will not be created, and therefore, no destructor will be called when SomeFunc .

+1
source share

All Articles