I have a class with own resource management:
class Lol
{
private:
// This is data which this class allocates
char *mName = nullptr;
public:
Lol(std::string str) // In constructor just copy data from string
{
auto cstr = str.c_str();
auto len = strlen(cstr);
mName = new char[len + 1];
strcpy(mName, cstr);
};
~Lol() // And of course clean up
{
delete[] mName;
}
}
I implemented copy constructor which just copies managed data:
Lol(const Lol &other)
{
auto cstr = other.mName;
auto len = strlen(cstr);
mName = new char[len + 1];
strcpy(mName, cstr);
};
I also need to implement copy assignment operator. I just did this:
Lol &operator=(const Lol &other)
{
if (this == &other)
{
return *this;
}
// Clean up my resources
this->~Lol();
// And copy resources from "other" using already implemented copy constructor
new (this) Lol(other);
}
Looks like this copy assignment operator will work for all classes. Why do I need to have another code in the copy assignment operator? What is use case for it?
Use the copy and swap Idiom.
Lol &operator=(const Lol &other)
{
if (this == &other)
{
return *this;
}
// Clean up my resources
this->~Lol();
// Copy can throw.
// Then your object is in an undefined state.
new (this) Lol(other);
// You forgot the return:
return *this;
}
So this does not provide the strong (or any) exception gurantees.
The preferred way would be:
Lol& operator=(Lol const& other)
{
Lol copy(other); // Here we use the copy constructor
// And the destructor at the end of
// function cleans up the scope
// Note this happens after the swap
// so you are cleaning up what was in
// this object.
swap(copy);
return *this;
}
void swap(Lol& other) noexcept
{
std::swap(mName, other.mName);
}
Nowadays we have improved on this original
Lol& operator=(Lol copy) // Notice we have moved the copy here.
{
swap(copy);
return *this;
}
The exciting thing here is that this version of assignment works for both copy and move assignment just as effeciently.