As far as I can tell, the assignment operator does everything the copy constructor does. Does the assignment operator take longer to run? Does the assignment operator not do the job when an object is passed to a function?
As far as I can tell, the assignment operator does everything the copy constructor does. Does the assignment operator take longer to run? Does the assignment operator not do the job when an object is passed to a function?
They are similar but they have very different jobs. The copy constructor makes new things like something else out of nothing. Assignment on one object takes another object: the left hand side becomes like the right hand side.
Example:
I took some liberties with that code, but nothing that you still can't learn from. Hopefully the compiler generated copy constructor and assignment operator are enough to teach you the difference between the two operations.Code:#include <iostream> class Cat { public: int my_age, my_mice_caught; Cat( int the_age = 1, int these_mice = 0 ) : my_age( the_age ), my_mice_caught( these_mice ) { } }; int main() { Cat a; Cat b( 5, 12 ); std::cout <<"Before swap:\n"; std::cout <<"a > mice caught: " <<a.my_mice_caught <<" age: " <<a.my_age <<".\n"; std::cout <<"b > mice caught: " <<b.my_mice_caught <<" age: " <<b.my_age <<".\n"; Cat *copy = new Cat( b ); b = a; a = *copy; delete copy; std::cout <<"After swap:\n"; std::cout <<"a > mice caught: " <<a.my_mice_caught <<" age: " <<a.my_age <<".\n"; std::cout <<"b > mice caught: " <<b.my_mice_caught <<" age: " <<b.my_age <<".\n"; }
But you should learn how to write your own, and this board is the very place to do it. Quite recently we've had a lot of threads on this very subject, so read them. Basically what works well for most objects is writing the copy constructor in terms of assignment, and then letting the assignment operator do the real work of copying over data from one object to the other.
Last edited by whiteflags; 10-12-2007 at 08:03 PM.
That leads to double-initialization of everything. For instance:
When a2 is constructed, the default constructor of class B is called. Then this newly constructed object is immediately overwritten with the data from a1's B object (via B's assignment operator). In other words, B::B() gets called and then B:perator=(const B &) gets called.Code:class A { public: B b; A(const A &other) { *this = other; } A &operator=(const A &other) { b = other.b; } }; A a1; A a2(a1);
Basically, the work performed by B::B() is a total waste because it is just going to be overwritten with some other object anyway. And what if there IS no function B::B() ?
In general copy constructors should only call other copy constructors. In other words:
The code duplication can be annoying, true. But that should serve as motivation for you to design your classes so that you don't have to write copy constructors and assignment operators in the first place.Code:A(const A &other) : b(other.b) { }
Last edited by brewbuck; 10-12-2007 at 08:35 PM.
I think that what works well is the opposite: writing the copy assignment operator in terms of the copy constructor. Of course, you need to have a suitable destructor and swap function.Basically what works well for most objects is writing the copy constructor in terms of assignment, and then letting the assignment operator do the real work of copying over data from one object to the other.
Look up a C++ Reference and learn How To Ask Questions The Smart WayOriginally Posted by Bjarne Stroustrup (2000-10-14)
Admittedly that approach confuses me to no end because I have no idea how swap() works in that context. I'm not adverse to learning new things. I admit I'm not the brightest of the bunch but I find my approach good basically because I understand it well, and it's easy (for me) to either write an operator= that never fails or throws exceptions in a decent manner, (when some type is not default constructable - shame on you!). Brewbuck's advice is good because it delegates work, but B better be copy constructable too!
Unless you've profiled your code and found that this type of assignment is actually having a noticable affect on performance, I agree with LaserLight. This way, if you change code in the copy constructor you don't have to worry about changing the assignment operator too (i.e. one less way for bugs to get into your code).Code:class A { public: A( const A& a ) { ... } A& operator=( const A& a ) { A temp( a ); swap( temp, *this ); return *this; } };
My confusion is genuine. How can you swap A without it's assignment operator? Implementing the operator in terms of itself confuses me.
Well, the kind of places where you even need to write your own copy constructor or assignment operator tend to be things that internally contain a bunch of pointers to other objects. Now there a good chance that operator = allocates more memory and does a deep copy of these members. However all swap should do is swap the pointer values around between the two objects - far less work!
As a matter of fact, even if some of those members aren't just raw pointers, they may be types which themselves implement a specialisation of swap, which means your own swap specialisation can simply call swap on those members. It all boils down to a very cheap way to completely swap two objects.
Last edited by iMalc; 10-13-2007 at 02:21 AM.
My homepage
Advice: Take only as directed - If symptoms persist, please see your debugger
Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"
Depends entirely on how you write it. Basically, if you write a swap(), you must swap() all contained RAII members, too. Of course, this requires that the contained type has a proper swap() too. E.g.:
OK, so far, so good. Now, we need to implement swap(). We do this simply by calling swap() for every member.Code:class two_contained { std::list<int> inner_list; std::vector<int> inner_vector; public: two_contained(const two_contained &other) : inner_list(other.inner_list), inner_vector(other.inner_vector) {} two_contained &operator =(const two_contained &other) { two_contained(other).swap(*this); } };
Note two important points:Code:void swap(two_contained &other) { using std::swap; swap(inner_list, other.inner_list); swap(inner_vector, other.inner_vector); };
1) Calling swap() for lists and vectors is a constant-time no-throw operation. Thus, the completed swap() is a constant-time no-throw operation. Operator =, which consists of the copy constructor and swap(), thus takes O(copy constructor) + O(swap) + O(destructor), and the swap() is constant time and thus irrelevant. Operator = therefore is about as efficient as any hand-written implementation. (No matter what, = must destruct the old data and copy-construct the new or something equivalent.)
2) Note the using declaration. It's necessary (well, not in this case, but in others, and it's a good habit). Calling the function as std::swap() prevents argument-dependent lookup, and thus may miss out-of-std overloads of swap() that are specialized. Example:
Now, this specialized swap() can be found through ADL if you do swap(mfc1, mfc2) from anywhere at all. But if you have any explicit namespace qualification, i.e. you call std::swap(mfc1, mfc2), this function is not an option.Code:namespace mine { template <typename E> class my_fancy_container { // ... public: void swap(my_fancy_container &other) throw(); // Specialized, O(1) swap. }; template <typename E> void swap(my_fancy_container<E> &lhs, my_fancy_container<E> &rhs) { lhs.swap(rhs); } }
All the buzzt!
CornedBee
"There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
- Flon's Law
Ah, thanks. I hadn't even hear of ADL until now. good to know about it.
But thinking again about laserlights statement: Your example doesn't use the copy ctor in the assignment operator. Or did I misunderstand something?writing the copy assignment operator in terms of the copy constructor
I tried this
but it doesn't seem to work. Although the following doesCode:class point{ public: point(){;} ~point(){;} point(const point&); double x, y, z; point &operator =(const point &right_side); }; point &point::operator =(const point &rhs){ this(&rhs); return *this; } point::point (const point &pt) { *this = pt; }
Code:class point{ public: point(){;} ~point(){;} point(const point&); double x, y, z; point &operator =(const point &right_side); }; point &point::operator =(const point &rhs){ x = rhs.x; y = rhs.y; z = rhs.z; return *this; } point::point (const point &pt) { *this = pt; }
No, it is an initialisation list that initialises the member variables. In particular, the member variable b of the current object is initialised to the member variable b of the other object.What does the colon mean? Does it have something to do with inheritance?
It does. How else would you explain two_contained(other)?Your example doesn't use the copy ctor in the assignment operator. Or did I misunderstand something?
What does this(&rhs) mean? It looks like you are trying to do some kind of constructor chaining (which is not a feature of C++) after the current object has been constructed. Even if this syntax was allowed, there would be a type mismatch since &rhs returns a point*, but the copy constructor does not take a point*. Read CornedBee's post as it pretty much describes what I had in mind.but it doesn't seem to work.
Look up a C++ Reference and learn How To Ask Questions The Smart WayOriginally Posted by Bjarne Stroustrup (2000-10-14)
I see the copy-ctor. but it isn't used inside operator=, as the statement "used in terms of copy constructor" suggests to me.
I guess I misunderstand somethingCode:two_contained &operator =(const two_contained &other) { two_contained(other).swap(*this); // swap, but no call to copy ctor }