views:

207

answers:

5

As I know that C++ compiler creates a copy constructor for each class. In which cases we have to write user defined copy constructors? Can you give some examples?

+12  A: 

The copy constructor generated by the compiler does member-wise copying. Sometimes that is not sufficient. For example:

class Class {
public:
    Class( const char* str );
    ~Class();
private:
    char* stored;
};

Class::Class( const char* str )
{
    stored = new char[srtlen( str ) + 1 ];
    strcpy( stored, str );
}

Class::~Class()
{
    delete[] stored;
}

in this case member-wise copying of stored member will not duplicate the buffer (only the pointer will be copied), so the first to be destroyed copy sharing the buffer will call delete[] successfully and the second will run into undefined behavior. You need deep copying copy constructor (and assignment operator as well).

Class::Class( const Class& another )
{
    stored = new char[strlen(another.stored) + 1];
    strcpy( stored, another.stored );
}

void Class::operator = ( const Class& another )
{
    char* temp = new char[strlen(another.stored) + 1];
    strcpy( temp, another.stored);
    delete[] stored;
    stored = temp;
}
sharptooth
+1 for ` Sometimes that is not sufficient. ` and the example.
Prasoon Saurav
It doesn't perform bit-wise, but member-wise copy which in particular invokes the copy-ctor for class-type members.
Georg Fritzsche
@Georg Fritzsche: Thank you, fixed.
sharptooth
Dont write the assingment operator like that. Its not exception safe. (if the new throws an exception the object is left in an undefined state with store pointing at a deallocated part of memory (deallocate the memory ONLY after all operations that can throw have completed succesfully)). A simple solution is to use the copy swap idium.
Martin York
@sharptooth 3rd line from the bottom you have `delete stored[];` and I believe it should be `delete [] stored;`
Peter Ajtai
I know it's just an example, but you should point out the better solution is to use `std::string`. The general idea is that only utility classes that manage resources need to overload the Big Three, and that all other classes should just use those utility classes, removing the need to define any of the Big Three.
GMan
@Martin York: I agree about exception safety. But what is copy swap idiom?
sharptooth
@sharp: Hm, it's spread around the site, but I don't see it as it's own question. I'll write up a nice big long explanation for it, but for now check out http://stackoverflow.com/questions/276173/what-are-your-favorite-c-coding-style-idioms/2034447#2034447 and http://stackoverflow.com/questions/1734628/copy-constructor-and-operator-overload-in-c-is-a-common-function-possible and http://stackoverflow.com/questions/2143787/what-is-copy-elision-and-how-it-optimizes-copy-and-swap-idiom
GMan
[More C++ Idioms/Copy-and-swap](http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Copy-and-swap)
Georg Fritzsche
@GMan, @Georg Fritzsche: Thank you, I see now.
sharptooth
@sharptooth: Copy swap idium explained here: http://stackoverflow.com/questions/255612/c-dynamically-allocating-an-array-of-objects/255744#255744 . Alternatively move the 'delete []' to the last line.
Martin York
@Martin: Idiom*, by the way. :)
GMan
@GMan: You could have said that 22 minutes ago when I could still edit the comment. :-) I blame IE for not underlining bad spelling like my normal browser.
Martin York
you should initialize your variables in constructor-initializer-list.
YeenFei
@Martin: I wanted to make sure it was carved in stone. :P
GMan
I've formally asked and explained the copy-and-swap idiom here: http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
GMan
+3  A: 

If you have a class that has dynamically allocated content. For example you store the title of a book as a char * and set the title with new, copy will not work.

You would have to write a copy constructor that does title = new char[length+1] and then strcpy(title, titleIn). The copy constructor would just do a "shallow" copy.

Peter Ajtai
+1  A: 

Copy Constructor is called when an object is either passed by value, returned by value, or explicitly copied. If there is no copy constructor, c++ creates a default copy constructor which makes a shallow copy. If the object has no pointers to dynamically allocated memory then shallow copy will do.

Prabhu Jayaraman
+2  A: 

I am a bit peeved that the rule of the Big Three wasn't cited.

This rule is very simple:

The Big Three:
Whenever you are writing either one of Destructor, Copy Constructor or Copy Assignment Operator, you probably need to write the other two.

But there is a more general guideline that you should follow, which derives from the need to write exception-safe code:

Each resource should be managed by a dedicated object

Here sharptooth's code is still (mostly) fine, however if he were to add a second attribute to his class it would not be. Consider the following class:

class Erroneous
{
public:
  Erroneous();
  // ... others
private:
  Foo* mFoo;
  Bar* mBar;
};

Erroneous::Erroneous(): mFoo(new Foo()), mBar(new Bar()) {}

What happens if new Bar throws ? How do you delete the object pointed to by mFoo ? There are solutions (function level try/catch ...), they just don't scale.

The proper way to deal with the situation is to use proper classes instead of raw pointers.

class Righteous
{
public:
private:
  std::unique_ptr<Foo> mFoo;
  std::unique_ptr<Bar> mBar;
};

With the same constructor implementation (yes, the same), I now have exception safety for free!!! Isn't it exciting ? And best of all, I no longer need to worry about a proper destructor! I do need to write my own Copy Constructor and Assignment Operator though, because unique_ptr does not define these operations... but it doesn't matter here ;)

And therefore, sharptooth's class revisited:

class Class
{
public:
  Class(char const* str): mData(str) {}
private:
  std::string mData;
};

I don't know about you, but I find mine easier ;)

Matthieu M.
+1  A: 

It's often a good idea to disable copy ctor, and operator= unless the class specifically needs it. This may prevent inefficiencies such as passing an arg by value when reference is intended. Also the compiler generated methods may be invalid.

seand