0

Here is my piece of code, please help me understand how to get the copy constructor correct. My class has no copy constructor written for it

test(cosnt test&obj){
    ////////something
}

But yet when I attempt to make a copy such as test t2(t1); it appears to be copied correctly! Can you please explain to me the reason that the copy seems to work even without an explicit copy constructor?

#include <iostream>
using namespace std;

class test{
    int *ptr;
public:
    test(int t=0){
        ptr=new int;
        *ptr=t;
    }
    void increment(){
        (*ptr)++;
    }
    void display(){
        cout<<*ptr<<endl;
    }   
};

int main(){
    test t1;

    t1.display();
    t1.increment();
    t1.display();

    test t2(t1);//copy constructor works fine!
    t2.display();

    t1.increment();

    t1.display();   
    t2.display();
}
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
user3215228
  • 313
  • 1
  • 3
  • 13

3 Answers3

5

C++ is so amazing that when you don't define a copy constructor, move constructor, copy assignment and move assignment for a class, the compiler will have to define them for you (the Standard says so). Of course you can delete them via:

func() = delete;

So for example, if you want to delete the implicit copy constructor in your example you would declare:

test(const test&) = delete;

and as you can see your code won't no longer compile.

The behavior of the implicit copy constructor is the one you would expect: it will copy construct each member of the class to the the other object. In this case it will copy construct the pointer value (not the pointed value), effectively make the two object share the same pointer.

Now, your program is leaking memory right? You have called new but no delete. Let's say you want to clean your resources up by inserting:

delete ptr;

in the destructor (it's a simplified version, of course you would need to define at least a proper move assignment and move constructor). You know what will happen? A nice and beautiful runtime error, telling you that the pointer you are trying to free has not been allocated. The reason why that is, is that both your objects (t1 and t2) destructors a will be called and they will both delete the same pointer. The first correctly and the second erroneously.

For this reason a Rule of three (now Rule of Five) has been established in the C++ community. But you know what? There's even a better rule which is called Rule of Zero. To sums it up (but you should really read about it) it says: don't do RAII yourself. I'd suggest you to follow the latter.


Now, let's discuss a little a bit of new. I'm sure you know this, but I'm here to prevent future damages: you don't need to use new at all in C++. Not anymore, in most cases.

Most of the things pointers once did (optional parameter, pass array by pointer, copyable reference, etc..) has now been "deprecated" (not in the literal sense: they are still there) in favor of sexier approaches (namely boost:optional/std::optional, std::array/std::vector, std::reference_wrapper). And even when all those fails to give you what you need, you can still use std::shared_ptr and std::unique_ptr.

So, please, don't use naked new pointers. Thanks.

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • WHy should I not use new? then how will I dynamically allocate memory? – user3215228 Feb 27 '14 at 19:49
  • 1
    @user3215228, I've edited my answer to include more examples. As for your question on how will you dynamically allocate memory. Well, it depends. What do you need it for? Dynamic array? Then use `std::vector`. Just dynamic memory? `std::make_shared` or `std::make_unique` (C++14). – Shoe Feb 27 '14 at 19:59
0

If you did not define the copy constructor yourself then the compiler implicitly defines it instead of you. This copy constructor makes member-wise copies of class data members. Relative to your example of code the implicitly defined copy constructor will copy data member ptr. As the result two or more objects can refer to the same memory.

Your class also needs destructor and the copy assignment operator.

These three specail functions can look the following way for your class

test( const test &rhs ) : ptr( new int( *rhs.ptr ) )
{
}

test & operator =( const test &rhs )
{
   if ( this != &rhs )
   {
      int tmp = new int( *rhs.ptr );
      delete ptr;
      ptr = tmp;
   }

   return ( *this );
}

~test()
{
   delete ptr;
} 
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

You did not define a copy constructor. Like @jrok said, the compiler-generated default copy constructor only does shallow member-wise copy.

Your copy constructor can look something like:

public:
    test(const test& t)
    {
        ptr = new int;
        *ptr = *t.ptr;
    }

BTW, you might want to define a destrcutor too to prevent memory leak.

~test()
{
    delete ptr;
}
Kevin Le - Khnle
  • 10,579
  • 11
  • 54
  • 80
  • Yes, overload the assignment operator too like Vlad said. I remember there is a book by Scott Meyers (C++ 50 effective ways or something like that) that goes into details that you might want to check it out. – Kevin Le - Khnle Feb 27 '14 at 19:42