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?
Printable View
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.
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::operator=(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)
{
}
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.Quote:
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.
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.
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);
}
}
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?Quote:
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.Quote:
What does the colon mean? Does it have something to do with inheritance?
It does. How else would you explain two_contained(other)?Quote:
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.Quote:
but it doesn't seem to work.
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
}
I see. Explain what this code snippet does:
Code:two_contained(other).swap(*this);
from my std docu about swap:
so I guess it's some variation ofQuote:
Assigns the contents of a to b and the contents of b to a.
But I'm also guess I'm totally wrong :)Code:void swap(T a, T b)
{
T temp = a;
a = b;
b = temp;
}
Explain the part before the swap.
Thats the copy-ctor. It creates a new object with its members initialized to the actual values of the object passed as parameter.Code:two_contained(const two_contained &other)
: inner_list(other.inner_list), inner_vector(other.inner_vector)
{}
But I don't see there this copy-ctor is called from inside the assignment operator. If it's used inside the assignment operator, I guess it should look like this:
Code:two_contained &operator =(const two_contained &other)
{
delete this;
this( new two_contained(other) ); // looks weird, buts that that I think of if i read
// "assignement operator in terms of copy constructor"
}
You have a major block there ;)
The bold part is the copy constructor call. It constructs a temporary object of type two_contained that is a copy of the object referenced by other.Code:two_contained &operator =(const two_contained &other)
{
two_contained(other).swap(*this);
}
Then, the contents of that temporary object are swapped with those of the current object. Now the current object has the copy of other, while the temporary has the old contents of the current object. Finally, the temporary is destructed, taking with it the old value of the current object.
By the way, I forgot a return *this at the end of the copy assignment operator.
Well, take a look at the copy assignment operator:Quote:
But I don't see there this copy-ctor is called from inside the assignment operator.
It is not obvious that "two_contained(other)" invokes the copy constructor to create a temporary two_contained object? Of course, CornedBee neglected to return *this, but that is a minor omission in this context.Code:two_contained &operator =(const two_contained &other)
{
two_contained(other).swap(*this);
}
aye, ok, I'm blind or something. sorry. probably that swap confused me a bit: why is it preferred instead of just assigning the values?
But my main question is answered now. A temp object is created and destructed, so the original just gets new values without releasing and reacquiring any ressources. thank for your patience!
The idea is to ensure that the copy constructor and copy assignment operator have the same semantics. One way is to manually keep them in sync should the list of member variables change. By using a swap() member function, one now keeps the copy constructor and swap() member function in sync, and then the changes are propagated to the copy assignment operator automatically. Additionally, we now have a swap() member function that could potentially perform an optimised swap as compared to the generic swap.Quote:
probably that swap confused me a bit: why is it preferred instead of just assigning the values?
Other reasons have to do with exception safety: if the copy constructor is exception safe, and the member swap function is exception safe, then the copy assignment operator is exception safe.
I'm not sure about that. If you create a temporary, it will aquire resources and initialize them. Swap then switches the resources. At the end, the temporary will go out of scope and release the resources in its destructor.Quote:
A temp object is created and destructed, so the original just gets new values without releasing and reacquiring any ressources. thank for your patience!
All this could be optimized not to use temporary resources, but then you'd lose exception safety. With the swap technique, if creating the temporary fails (throws), the original contents will be left as they were (because we won't get to the swapping part), rather than corrupting them entirely.
But I may be wrong.
For anyone who still isn't sure about the copy-constructor call:
Take this code snippet:Have you ever seen the cast written like this?:Code:float f=1.23;
int x = (int)f;
No this isn't just a cast where someone forgot to put brackets arount the type. The reason this works (in C++) is that it is not a cast! It is a constructor call. Try it in C and see that it doesn't work. In fact if 'f' was declared as 'int' then this would be a copy-constructor call.Code:float f=1.23;
int x = int(f);
Now, in this case the type is 'two_contained' not 'int', and the variable being copied is 'other' and is also of type 'two_contained'. Same syntax as above though. Look similiar now?
Since the result of the expression is a new 'two_contained' object, we're free to call whatever methods on it we like, including swap. Furthur proof can be seen from the fact that 'other' is declared as a const-reference, and yet swap takes a non-const-reference, as it obviously modifies the inputs.Code:two_contained(other)
I was thinking of a hybrid approach.
For example, consider a string class that has these members: unsigned len - size of the string, unsigned capacity - size of buffer, always at least size + 1, char* buffer - dynamic character array.
I have the following copy constructor and swap method:
Now, the copy constructor always allocates memory, but in the assignment we could simply write into the buffer without any memory allocations, if the buffer is large enough. If we assign a smaller string to a larger one, part of the buffer would just remain unused.Code:String::String(const String& s):
len(s.size()), capacity(s.size()+1),
buffer(new char[capacity])
{
strcpy(buffer, s.c_str());
}
void String::swap(String& s)
{
std::swap(len, s.len);
std::swap(capacity, s.capacity);
std::swap(buffer, s.buffer);
}
This assignment should be quite exception safe. Allocating a larger buffer can fail (I guess this can't be avoided) but if it does, the original String will remain unmodified.Code:String& String::operator= (const String& s)
{
if (this != &s) {
if (capacity > s.size()) { //we have enough memory
strcpy(buffer, s.c_str());
len = s.size();
}
else {
//use temporary to create a larger memory block
//and to delete current buffer
String(s).swap(*this);
}
}
return *this;
}
I meant, if the copy constructor allocates dynamic memory, then in the swap idiom: 1) memory is allocated when the temporary is constructed; 2) pointers are swapped; 3) the destructor of the temporary releases the resources that the "assigned-to" object previously held.
If there are no memory allocations, there probably won't be much overhead.
Yes, but that's an optimization that works in a very specific case: when a class allocates memory. Oh, and when you can guarantee that your held object type has a nothrow copy constructor. E.g. std::basic_string can do it, because the character type must be a POD, but std::vector can't if it want to provide the strong exception guarantee, because any of the copy constructors might throw.
On an irrelevant side note, memcpy would be a better choice here than strcpy.
And pheres, I'm not sure what you mean. You can't just copy allocated resources in a copy constructor or copy assignment operator. All RAII classes must do one of three things:
1) Create a copy of the resource.
2) Increment a reference count on the resource.
3) Disallow copying.
Otherwise, you get multiple de-allocations of the resource.
Hm, just to test if I got it: If one really writes the copy constructor in a way so it does acquire own ressources for the new object instead of just copying the handle to the ressources of the other object (update of reference count included), then that swap method demonstrated above in the assignement operator does not suit very well if allocating is very complex?
Why not? Actually, it's perfectly suited. Or else I really don't get what you mean.
I have object 1, to which I want to assign the actual values of object 2.
Method 1 (the one demonstrated here):
So I'm creating a temp object inside the assignment operator as a copy of object 2 (*), to reuse the copy constructor.
After that I call swap to actually assign the new values to object 1.
Method 2:
Just assign the value of every variable of object 2 to object 1 without using the copy constructor.
(*) If that copy construction takes very long - maybe think of mounting a remote directory on another continent over a 9600 baud connection to get an object representing that directory - isn't then method 2 not far far better?
somethere above must be my error...
Yes (in terms of efficiency), but then copy construction and copy assignment no longer follows the same semantics: copy construction causes the new object to acquire its own resources, copy assignment causes the object to have handles to the other object's resources. This difference in behaviour is likely to be undesirable.Quote:
If that copy construction takes very long - maybe think of mounting a remote directory on another continent over a 9600 baud connection to get an object representing that directory - isn't then method 2 not far far better?
To avoid this, the copy assignment operator would have to use the network as well... upon which one can go back to the copy constructor+swap idiom and not be any worse off.
I pointed out the 3 valid methods of copy behaviour. They apply to copy assignment just as much as copy construction.
Take the mount example. Object A is constructed and mounts the remote share. Its destructor will eventually unmount the share.
Object B is made a copy of object A. It doesn't matter whether it's through copy assignment or copy construction. Now object B is naive and just copies over A's variables.
Object B is destructed. It unmounts the share.
Object A is asked to perform some operation that needs the share. But B unmounted it. Object A makes the program crash.
Oops.
Object B can do two things: it can either increment a reference count on the mount, and only unmount if the count drops to zero, or it can mount the remote share again in its own private location (thus copying the resource). There are no other options. And it doesn't matter whether this was done through copy assignment or copy construction.
Disallowing copying is another option (and perhaps a sensible one, if copying is so slow), but then you wouldn't have an object B.
But the point is, it all applies equally to copy assignment and copy construction. And swap() is still O(1), fast and exception-safe.
The only time you should implement copy assignment differently is when you can reuse the old resource and still maintain the same guarantees. basic_string can reuse its block of memory. vector can do it if it detects a POD member type. If your remote share is always the same, you could avoid mounting and unmounting. (But then, because it's so slow, you should have one single global mount shared by all the objects.)
Pheres: Apart from the problems others have pointed out with Method 2, don't forget that you have to deal with disposing of the current resources allocated by the object you're assigning to.
The swap method takes care of this because the existing values are of the object being assigned to are swapped to the temporary object which then immediately goes out of scope and gets destroyed. It's all rather clever actually.
I'm impressed at the heights of complexity this discussion has reached. Obviously, writing copy constructors and assignment operators which work as similarly as possible (within the bounds of sense), are efficient, exception safe, and easy to understand and maintain is HARD.
One of the principles I adhere to in writing C++ code is to avoid creating copy constructors and assignment operators at almost all costs, because of this exact problem. It is almost always possible to create classes that do what you want by wrapping types in container types which enforce specific types of copy behaviors.
As a bonus, designs based on containers are exception-safe automatically.
That's the key. Make every resource that needs handling be wrapped by its own class. Thus, the complexity of the big three never rises beyond that single resource, and classes using the resource needn't bother with anything.
Thank you guys, that discussion made my mind a lot clearer about this topic.
Two questions: The term "big three" does refer to acquire own ressource / increment ref.count / disallow copying? Or is it something else?
How would that apply to the mount example? Or could you give another short example demonstrating that? Thank youQuote:
It is almost always possible to create classes that do what you want by wrapping types in container types which enforce specific types of copy behaviors.
I believe it refers to the copy constructor, copy assignment operator and destructor.Quote:
The term "big three" does refer to acquire own ressource / increment ref.count / disallow copying?
In the mount example I would actually make the class non-copyable. Object B would then be a "view" of object A rather than a copy of A itself -- in other words, a different class entirely.
But first I would question the need for a copy or a view of A in the first place.
Thanks everyone for all the discussion. Can anyone explain why my simple class for a point won't compile?
Code:class point{
public:
point(){;}
~point(){;}
point(const point&);
double v[2];
void setpoint(double, double, double);
point &operator =(const point &rhs);
double &operator[] (unsigned i) { return v[i]; }
};
point &point::operator =(const point &rhs){
v[0] = rhs[0];
v[1] = rhs[1];
v[1] = rhs[2];
return *this;
}
point::point (const point &pt) {
*this = pt;
}
void point::setpoint(double a, double b, double c) {
v[0] = a;
v[1] = b;
v[2] = c;
}
However, in this case it seems to me that you don't need to write the copy contructor and assignment at all, as they don't do anything other than a shallow copy (memberwise copy). (This might also be why you might be able to implement the copy constructor in terms of assignment and not the other way round, as the thread discusses.)Code:point &point::operator =(const point &rhs){
v[0] = rhs.v[0];
v[1] = rhs.v[1];
v[2] = rhs.v[2]; //out of bounds
return *this;
}
double v[2];
needs to be
double v[3];
Post your compile error messages.
Imagine you have a class A which holds some member by pointer:
Conceptually, class A "owns" the object b. For unspecified reasons, it must be referenced via a pointer and not held directly by value. The copy semantics are such that if an object of class A is copied, the "b" object is DUPLICATED and the result holds a pointer to a NEW object. You could implement this by writing a custom assignment operator:Code:class A
{
private:
Member *b;
};
Or b could be copied by first default-constructing it and then using Member's assignment operator. But a much better solution is to create a wrapper type which handles the copying of the pointed-to object for you:Code:A &operator=(const A &other)
{
delete b; // Free the one I have
b = new Member(*(other.b)); // Copy the other one
}
Then, the definition of class A would look like:Code:template <typename T>
class copied_ptr
{
public:
copied_ptr(T *ptr);
: obj(ptr)
{
}
copied_ptr(const copied_ptr &other)
: obj(other.obj ? new T(*(other.obj)) : 0)
{
}
copied_ptr &operator=(const copied_ptr &other)
{
delete obj;
obj = other.obj ? new T(*(other.obj)) : 0;
return *this;
}
// This type can automatically convert to the underlying pointer type.
operator T *()
{
return obj;
}
// An explicit get-pointer method for when the above automatic conversion is not appropriate.
T *get()
{
return obj;
}
// It also allows member dereference
T *operator->()
{
return obj;
}
private:
T *obj;
};
Class A no longer needs a custom assignment operator. The default assignment operator is sufficient, because it invokes the assignment operator of copied_ptr<Member> which does the right thing.Code:class A
{
private:
copied_ptr<Member> b;
};
You might be thinking "Holy cow, that's a lot of code, compared to the two lines in the custom assignment operator..." The difference is, you only have to write copied_ptr<> ONCE. Once you have it, you can use it anywhere you need THOSE specific copy semantics. And once you get it right, it is going to STAY right. Compare that with writing zillions of assignment operators everywhere you need this concept. You are bound to make a mistake in one of them.
Hopefully that illustrates the basic idea behind wrapping types in order to cause certain copy behaviors. PLEASE don't use the copied_ptr<> template. I whipped it up just for this example, so it is missing features and is nowhere near as useful as the real copied_ptr<> I use. Also, it might have errors in it, although I don't see any obvious ones. Point being, it is just an illustration.
>> when some type is not default constructable - shame on you!
Sorry, this was from way earlier in the thread, but since I'm posting anyway I'll say that I completely disagree. Only provide default construction when it makes sense for the class. If you provide it when not necessary then "shame on you".
BTW, brewbuck, does the copied_ptr<> you use require the class to implement a clone() function or something similar? How does it do with base and derived classes?
It requires that the underlying type be copy-constructible, that's about it. If the copy constructor of the wrapped type does the right thing, then copied_ptr<> does the right thing. If the wrapped type has some oddity, such as requiring cloning to be done through a clone() method, then I usually invent a wrapper specific to that type. But it doesn't happen often.
A far less common but equally useful wrapper is not_copied<>, which does nothing at all in its copy constructor and assignment operator. I use that when I specifically DO NOT want a particular member to be copied when the object is copied. For instance, if it has a const member. When a const member is present, the compiler can't generate a default copy constructor or assignment operator, forcing you to write one yourself -- unless you wrap the type up in a not_copied<>.
I would imagine that copied_ptr<> doesn't work too well on inheritance hierarchies. For example, if you had a copied_ptr<Base> then it would slice the object during copy, right?
I was looking for a smart pointer a while ago that implemented C++ style copy semantics and worked with polymorphic classes, but only found some that required a clone() style function.
Yeah, it would. I didn't see what you were getting at originally. I do have a cloned_ptr<> template which invokes clone(). Like everyone else, I haven't found a better way...
EDIT: Actually, that isn't accurate. My cloned_ptr<> template actually takes two parameters -- the type of the object itself, and a "cloner" type which is responsible for cloning the object. This type defaults to a default_cloner<> which just calls the object's clone() method. But if the object doesn't HAVE a clone() method, you can write an appropriate replacement which clones it and use that as the cloner type. So it's a bit more flexible, as it doesn't require the name of the clone method to be "clone()".
Thank you, very interesting. to be honest, most of my c++ classed had private copy constructors and assignment operators so far. But I think I'd feel a bit more comfortable now If I've to use them.