copying classes

Started by
24 comments, last by mfawcett 19 years ago
Say I have class A and class B. Class A has NO custom copy contructor or assignement operator defined. B HAS a custom copy constructor and assignement operator defined. Class A contains an object of class B in it. Now I do:

A x, y = {some parameters};


x = y; //remember that A had no custom assignement operator
Will the object of type B inside A, be copied bit by bit (so pointers are not updated if necessary), or will the custom assignement operator of B be called (B has one after all as I mentioned above)? In other words, if class A itself does not need a deep copy, but class B does need a deep copy and has it defined, and class A contains a class B in it, will the default shallow copy of A call B's deep copy, or not? Also, because I don't want to type the same thing twice, if the code of the copy constructor and assignement operator are exactly the same, is there a way to have to type it only once?
Advertisement
B's copy constructor will be called. The default copy operation isn't a bitwise copy but a memberwise copy.

In other words, if all your members either are basic type or have their own copy constructor (destructor, assignment operator), the enclosing class doesn't need you to define a copy constructor (destructor, assignment operator).

Quote:Also, because I don't want to type the same thing twice, if the code of the copy constructor and assignement operator are exactly the same, is there a way to have to type it only once?


Yes and no... The safest way is to implement the assignment operator as a copy construction of a temporary and a swap with the target variable. You will, of course, have to implement the swap function... probably in terms of other member swaps.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
Yes and no... The safest way is to implement the assignment operator as a copy construction of a temporary and a swap with the target variable. You will, of course, have to implement the swap function... probably in terms of other member swaps.


Do you think it could be possible to call the assignement operator from the copy constructor (if I type somthing like "this = the_other_one" in the copy constructor and define the operator= function)?
I believe Fruny ment something like this:
#include <algorithm>class MyClass{public:	MyClass(const MyClass & rhs) {/*Copy constructor's code*/}	MyClass & operator=(const MyClass & rhs)	{		MyClass temp(rhs); //Create a copy		std::swap(classMemberVariables, temp.classMemberVariables); //Swap the variables		return *this;	}};

The reason to create a temp is to prevent problems with self-assigments if resources have to be freed, e.g.
MyClass instance;//Later on in the functionMyClass & anotherInstance = instance;//Further downinstance = anotherInstance;

Pretend that your class contains a pointer, what happens if you did this?
MyClass & operator=(const MyClass & rhs){	this->myPointer = rhs.myPointer;	rhs.myPointer = 0; //Oh dear, rhs == *this!! We've creater a memory leak	return *this;}

Hope that helps
desertcube, I've always just compared the other object to myself, is there a reason why you would prefer the swap approach to this?? It seems like a neat approach but if you assigning to your self should be disallowed I think the

if ( rhs !== this ) approach is more clear.

Cheers
Chris
CheersChris
Quote:Original post by chollida1
desertcube, I've always just compared the other object to myself, is there a reason why you would prefer the swap approach to this?? It seems like a neat approach but if you assigning to your self should be disallowed I think the

if ( rhs !== this ) approach is more clear.

Cheers
Chris

I'm not entirely sure in all honestly!! I read it somewhere and thought it was smart!

A quick google gave me this, which basically says that using a swap function makes sure that the assignment will be safe from exceptions (well, it will be uneffected if an exception occurs.) Hopefully, this should make it a but clearer:
class MyClass{public:	MyClass() {itsPointer = new int[8];}	~MyClass() {delete [] itsPointer;}	MyClass(const MyClass & rhs);	MyClass & operator=(const MyClass & rhs);	//Functionsprivate:	int * itsPointer;};MyClass::MyClass(const MyClass & rhs){	itsPointer = new int[8]; //This may throw if there isn't enough memory!	std::copy(&rhs.itsPointer[0], &rhs.itsPointer[7], itsPointer); //Copy over the values}MyClass & MyClass::operator=(const MyClass & rhs){	//This statement may throw if the copy constructor fails,	//but if it does, our class will be unaffected, as we haven't changed anything!	MyClass temp(rhs);	//If you have a lot of member variables, then you'd probably create a seperate swap function.	std::swap(itsPointer, temp.itsPointer);	return *this; //Allow chaining	//As temp goes out of scope, its destructor will get called,	//but since it's pointer is now our old pointer, that peice of memory will get freed instead!}
Quote:Original post by chollida1
desertcube, I've always just compared the other object to myself, is there a reason why you would prefer the swap approach to this?? It seems like a neat approach but if you assigning to your self should be disallowed I think the

if ( rhs !== this ) approach is more clear.


If your code relies upon checking for self assignment then it cannot be made exception safe. See GotW #23: Any copy assignment that "must" check for self-assignment is not exception-safe.

Checking for self assignment is a useful optimisation, however. But it shouldn't be necessary for the correct running of the code.

The original question comes up a lot as people are trying to avoid writing the same code twice (or copying and pasting). Swap is a useful member function to have on any class and makes it easy to implement the assignment operator in terms of the copy constructor and the swap function.

class MyClass {  int memberA;  double memberB;  std::vector<double> memberC;  public:  MyClass() : memberA(4), memberB(17.5), memberC(20, 1.55) {}  void swap(MyClass& other) {    std::swap(memberA, other.memberA);    std::swap(memberB, other.memberB);    memberC.swap(other);//vector has its own swap member; more efficient than calling std::swap  }  MyClass(const MyClass & rhs) {    memberA = rhs.memberA;    memberB = rhs.memberB;    memberC = rhs.memberC;  }  MyClass& operator=(const MyClass& rhs) {    MyClass temp(rhs);    temp.swap(*this);    return *this;  }};
petewood, shouldn't your copy constructor be implemented using the initialiser list rather then an assignment list?

  MyClass(const MyClass & rhs) {    memberA = rhs.memberA;    memberB = rhs.memberB;    memberC = rhs.memberC;  }// what if any of these variables are const? assignment will fail// safer (and more efficient too)  MyClass(const MyClass & rhs)   : memberA(rhs.memberA)  , memberB(rhs.memberB)  , memberC(rhs.memberC)  {  }


fundamentally, I'd say that assignment and copy construction are two different things, even if they happen to contain similar code. I also fail to see the benefit in moving the work from the = operator to a swap function. There shouldn't be that much code repition anyway (i.e. if your class has 50 members, it's time to look at your class design!)

I'd disagree with that gotw article that copy construction should be implemented using an overloaded assignment op (or private copy function), as again, they are not the same thing. While it's initally fine if your class has POD's, if you add a UDT member that does something funny in it's copy/assignment you're in tonnes of trouble.
if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Quote:Original post by Fruny
B's copy constructor will be called. The default copy operation isn't a bitwise copy but a memberwise copy.

In other words, if all your members either are basic type or have their own copy constructor (destructor, assignment operator), the enclosing class doesn't need you to define a copy constructor (destructor, assignment operator).


This is one more reason to use std::string, BTW: it removes one source of pointer members from your object, thus increasing the chance that you will be able to escape from the Tyranny of the Rule of Three. :)

Quote:
Quote:Also, because I don't want to type the same thing twice, if the code of the copy constructor and assignement operator are exactly the same, is there a way to have to type it only once?


Yes and no... The safest way is to implement the assignment operator as a copy construction of a temporary and a swap with the target variable. You will, of course, have to implement the swap function... probably in terms of other member swaps.


Is std::swap not good enough in general? Won't that do a memberwise swap? o_O
Quote:Original post by ChaosEngine
petewood, shouldn't your copy constructor be implemented using the initialiser list rather then an assignment list?

Shh, you're making me look bad.

(c:

This topic is closed to new replies.

Advertisement