I see. Explain what this code snippet does:
Code:two_contained(other).swap(*this);
Printable View
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?