C++ copy constructor incorrectly takes argument

Viewed 93

I have the following code that behaves weirdly. The flow I understood so far is, display(line); will call the copy constructor Line::Line(const Line &obj), and the reference of line will be passed in. However cout<<"[origin] *ptr="<<*obj.ptr<<endl; will print [origin] *ptr=32767 instead of [origin] *ptr=10.

The more weird thing is, if I uncomment // int x=3;, it will print correctly, but I really have no idea why.

You may find the executable code at: https://www.onlinegdb.com/pjbPO0X1f

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      int getLength( void );
      Line( int len );
      Line( const Line &obj);

   private:
      int *ptr;
};
 
// constructor
Line::Line(int len)
{
    ptr=&len;
    cout<<"*ptr="<<(*ptr)<<endl;
}

// copy constructor
Line::Line(const Line &obj)
{
    // int x=3;
    cout<<"[origin] *ptr="<<*obj.ptr<<endl;
    ptr = new int;
    *ptr = *obj.ptr; // copy
}

int Line::getLength( void )
{
    return *ptr;
}
 
void display(Line obj)
{
   cout << "line=" << obj.getLength() <<endl;
}

int main( )
{
   Line line(10);
   display(line);
   return 0;
}
2 Answers

You program invokes undefined behavior (UB). When your constructor finishes:

Line::Line(int len)
{
    ptr=&len;
    cout<<"*ptr="<<(*ptr)<<endl;
} // ptr is dangling

the pointer ptr is pointing to a local variable len, which no longer exists. ptr is now dangling, and any attempt at dereferencing it invokes UB.

Your program may do anything at all. You can also see some strange results like adding int x = 3 resulting in your program "behaving correctly". Don't worry about why this happens, it's just a result of UB.

It's a common error (although this is a strange version of it).

This code is wrong

// constructor
Line::Line(int len)
{
    ptr=&len;
    cout<<"*ptr="<<(*ptr)<<endl;
}

ptr is made to point to len but len is a local variable. It is destroyed when the constructor is exited. This means you have a pointer to an object which no longer exists. Sometimes this is called a dangling pointer.

Then later in the code you use this pointer

cout<<"[origin] *ptr="<<*obj.ptr<<endl;

Since the pointer is now invalid, the effects are unpredictable.

One of the many things that make pointers difficult is that the lifetime of a pointer and the lifetime of what it is pointing at are not connected in any way at all. It's up to you to make sure that your pointers are always pointing at objects that are still 'alive'.

Related