I tried to implement a simple container class using the copy-and-move idiom. I am learning the changes to C++ as of C++11 and later.
- Is the code exception safe?
- I chose to use a smart pointer. Is
std::unique_ptr
a safe choice? I read that it should not be copied, but since I am doing the copying of the content explicitly, and since it provides a swap function, I could not see any objection. Is there a preferred practice, considering the array content type might be any type including an object type? - When I let the compiler do the copy for the copy assignment operator (like
...::operator=(MyContainer other)
) I got an ambiguity with the move assignment operator. So I chose to put the copy operation into the function (which I prefer visually, it might look naive and oldschool, but as an aging brain I find it more explicit). Also here: Is there another, preferred practice? And to overcome my personal preference: What is the real difference of letting the compiler do the copy? Both are taking place on the stack, aren't they?
Header part
class MyContainer
{
private:
std::size_t mSize;
std::unique_ptr<int[]> mArray;
static void _swap(MyContainer& first, MyContainer& second);
void _reset(void);
public:
MyContainer(std::size_t size); // default constructor
MyContainer(const MyContainer& other); // copy constructor
MyContainer(MyContainer&& other) noexcept; // move constructor
MyContainer& operator=(const MyContainer& other); // copy assignment operator
MyContainer& operator=(MyContainer&& other) noexcept; // move assignment operator
void dump(void);
/* ... */
~MyContainer(void);
};
Source part
void MyContainer::_swap(MyContainer& first, MyContainer& second) // static
{
std::swap(first.mSize, second.mSize);
first.mArray.swap(second.mArray);
}
void MyContainer::_reset(void)
{
mSize = 0;
mArray.reset();
}
MyContainer::MyContainer(std::size_t size = 0) // default constructor
: mSize(size),
mArray(mSize ? new int[mSize] : nullptr)
{
// for testing purpose only
for (int i = 0; i < mSize; i++)
mArray.get()[i] = i;
}
MyContainer::MyContainer(const MyContainer& other) // copy constructor
: mSize(other.mSize),
mArray(mSize ? new int[mSize] : nullptr)
{
std::copy(other.mArray.get(), other.mArray.get() + mSize, mArray.get());
}
MyContainer::MyContainer(MyContainer&& other) noexcept // move constructor
: MyContainer() // init using default constructor (C++11)
{
_swap(*this, other);
}
MyContainer& MyContainer::operator=(const MyContainer& other) // copy assignment
{
MyContainer tmp(other);
_swap(*this, tmp);
return *this;
}
MyContainer& MyContainer::operator=(MyContainer&& other) noexcept // move assignment
{
if (this != &other)
{
_reset();
_swap(*this, other);
}
return *this;
}
void MyContainer::dump(void)
{
std::cout << "Size: " << mSize << std::endl;
for (int i = 0; i < mSize; i++)
std::cout << mArray.get()[i] << std::endl;
}
MyContainer::~MyContainer(void)
{
}