Are the following assignment and copy move constructors the most efficient? if anybody have other way please tell me? I mean what bout std::swap? and calling assignment through copy constructor is safe in the code below?
#include <iostream>
#include <functional>
#include <algorithm>
#include <utility>
using std::cout;
using std::cin;
using std::endl;
using std::bind;
class Widget
{
public:
Widget(int length)
:length_(length),
data_(new int[length])
{
cout<<__FUNCTION__<<"("<<length<<")"<<endl;
}
~Widget()
{
cout<<endl<<__FUNCTION__<<"()"<<endl;
if (data_)
{
cout<<"deleting source"<<endl;
}
else
{
cout<<"deleting Moved object"<<endl;
}
cout<<endl<<endl;
}
Widget(const Widget& other)
:length_(other.length_),
data_(new int[length_])
{
cout<<__FUNCTION__<<"(const Widget& other)"<<endl;
std::copy(other.data_,other.data_ + length_,data_);
}
Widget(Widget&& other)
/*
:length_(other.length_),
data_(new int[length_])*/
{
cout<<__FUNCTION__<<"(Widget&& other)"<<endl;
length_ = 0;
data_ = nullptr;
std::swap(length_,other.length_);
std::swap(data_,other.data_);
}
Widget& operator = (Widget&& other)
{
cout<<__FUNCTION__<<"(Widget&& other)"<<endl;
std::swap(length_,other.length_);
std::swap(data_,other.data_);
return *this;
}
Widget& operator = (const Widget& other)
{
cout<<__FUNCTION__<<"(const Widget& other)"<<endl;
Widget tem(other);
std::swap(length_,tem.length_);
std::swap(data_,tem.data_);
return *this;
}
int length()
{
return length_;
}
private:
int length_;
int* data_;
};
int main()
{
{
Widget w1(1);
Widget w2(std::move(Widget(2)));
w1 = std::move(w2);
}
cout<<"ENTER"<<endl;
cin.get();
return 0;
}
The answer is in response to @Abdulrhman's complaint in the comments above that things fail for some (obscure) sequences of assignments. Put into a seperate answer because it's more readable that way.
The complaint was that
crashes. Here's the output I get from
with some code added to
Widget
to log constructor, destructor and assignment operator calls. Interleaves are comments about where those calls come fromI can see no problem there, and compiling this with clang and
-faddress-sanitizer, -fcatch-undefined-behaviour
doesn't complain either.Note, though, that the second assigment (the left
=
operator) copies instead of moving. This is because the first (right) assignment operator returns an lvalue-reference.Looks fine from an efficiency POV, but contains an awful lot of duplicated code. I'd
swap()
operator for your class.length_
anddata_
where they are declared.You might want to use
std::memcpy
instead ofstd::copy
since you're dealing with a raw array anyway. Some compilers will do that for you, but probably not all of them...Here's a de-duplicated version of your code. Note how there is only one place which needs to know how two instances of
Widget
are swapped. And only one place which knows how to allocate a Widget of a given size.Edit: You usually also want to use argument-dependent lookup to locate swap, just in case you ever have non-primitive members.
Edit: Integrated @Philipp's suggestion of making the assignment operator take it's argument by value. That way, it acts as both move assignment and copy assignment operator. In the move case, not that if you pass a temporary, it won't be copied, since the move constructor, not the copy constructor will be used to pass the argument.
Edit: C++11 allows non-cost members to be called on rvalues for compatibility with previous versions of the standard. This allows weird code like
Widget(...) = someWidget
to compile. Makingoperator=
require an lvalue forthis
by putting&
after the declaration prevents that. Note though that the code is correct even without that restriction, but it nevertheless seems like a good idea, so I added it.Edit: As Guillaume Papin pointed out, the destructor should use
delete[]
instead of plaindelete
. The C++ standard mandates that memory allocated vianew []
be deleted viadelete []
, i.e. it allowsnew' and
new []` to use different heaps.You don't need so many swaps and assignments in your move constructor. This:
does the minimum work for the move constructor: 4 assignments in total. Your version had 8, counting the ones in the calls to swap().
Your move assignment is OK, but you might want to consider just writing one operator=() to cover both cases:
This is slightly less efficient than your version, in that it can move twice.