template<typename T>
class A{
public:
A(){}
~A(){
delete [] array;
size = 1;
}
// Copy-constructor
A(const A &rhs){
size = rhs.size;
array = new T[rhs.size];
for(int i = 0; i < rhs.size; i++){
array[i] = rhs.array[i];
}
}
//Copy Assignment
A& operator=(const A &rhs){
Chain copy = rhs;
std::swap(*this, copy);
return *this;
}
// Move Constructor
// Moving also uses the value of an object to set the value to another object.
// The source loses that content, which is taken over by the destination.
A(A &&rhs){
size = rhs.size;
array = new T[rhs.size];
for(int i = 0; i < rhs.size; i++){
array[i] = rhs.array[i];
}
delete [] rhs.array;
rhs.size = 1;
}
// Move-Assignment
A& operator=(A &&rhs){
delete [] array;
array = new T[rhs.size];
for(int i = 0; i < rhs.size; i++){
array[i] = rhs.array[i];
}
delete [] rhs.array;
rhs.size = 1;
return *this;
}
private:
size_t size = 1;
T array = new T[size];
};
From what I understand, move constructor is similar to the copy constructor, after you transfer from the source object to the destination object, you delete the source object. I am sure that my code is incorrect, so I am asking if someone can clarify these special members to me.
> move constructor is similar to the copy constructor,
> after you transfer from the source object to the destination object, you delete the source object.
After inexpensively transferring the resources from the rvalue, you leave it in a safely-destructible state.
@JLBorges Thanks for the explanation for the move constructor.
I have refined my code a bit and I'm having problems deleting the source object.
When I try to perform a move using the constructor, it does not delete the source object.
> I am not sure how I am able to use std::swap then delete the source object,
> since swap only moves the values of two objects.
Repeat: you are not expected to delete the source object; you are expected to leave it in a safely destructible state.
For instance, in the code that you originally posted:
1 2 3 4 5 6 7 8 9 10 11
A(A &&rhs){
size = rhs.size;
array = new T[rhs.size];
for(int i = 0; i < rhs.size; i++){
array[i] = rhs.array[i];
}
// delete [] rhs.array;rhs.array = nullptr ; // make rhs safely destructible
// rhs.size = 1;
rhs.size = 0 ; // if we want to leave it in a consistent state
}
Note: swap works because there are in-class member initialisers; by the time the body of the move constructor is entered into, we already have a default initialised object.,
I suggest not to use swap because that is not what copy/move is supposed to do.
In the move constructor/operator: Make a shallow copy (i.e. copy the pointer). Then set the object moved from to an empty state. Do not delete the moved data.
move was introduced to avoid unnecessary time consuming copy operation especially for returning a local container variable like vector.
I realized my mistake, I failed to set the member variables size and array to the default initialized value. When I used std::swap it would swap the values, but would not perform the move function correctly if it was:
move(c) should cause c to be empty, therefore we need to swap a and c such that c would be empty after the swap.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
A(A &&rhs){
// Set member variable to default
size = 0;
array = nullptr;
std::swap(size, rhs.size_);
std::swap(array, rhs.array_);
}
// Move-assignment
A& operator=(A &&rhs){
// Set member variable to default
size = 0;
array= nullptr;
std::swap(size, rhs.size_);
std::swap(array, rhs.array_);
return *this;
}
Thanks for the help, and feel free to comment if I have the concept wrong because I recently started looking at C++11 and it takes some time to grasp the basics of C++11 since I'm still trying to master the basics of the older version of C++.
In practice, that is pretty much what moving an object is supposed to do, since swapping can be implemented efficiently in the same way moving can.
While this might be true, it also contradicts expectation and hence is error prone. Hard to detect errors like left and right values are really swapped.
Thus i would recommend swap only when swap is intended.
it also contradicts expectation and hence is error prone
cire wrote:
Copy-and-swap-idiom's continuing relevancy in C++11:
I think you guys are talking about different things.
A move that is implemented in terms of swap is a valid move, and in fact GNU libstdc++ std::string's move actually swapped until std::string specification tweaks outlawed it:
I agree this violates the principle of the least surprise, given the general expectations of what move-assignment should do.
But a copy-and-swap assignment does not have that effect: it first moves out of the argument, and then modifies the parameter (by swapping and destroying), so the original argument is never touched by the swap.
No, you don't. Again, you're leaking memory here. The link you supply concerns a constructor. You aren't dealing with a constructor.
Setting a pointer to null does not "release" anything. If you must release the resource immediately in the removed-from object use the appropriate method (delete[] here,) prior to setting it to null although releasing it isn't strictly necessary since it will be released regardless when the original moved-from object is destroyed and its destructor runs.
The primary thing is to leave the objects in states consistent with the invariants for the type(s) involved so that the moved-from object can be safely destroyed (or assigned to) afterwards.