Design a site like this with WordPress.com
Get started

…C++ is Kinda Cool

I’ve been working on a C++ project (blame Qt), and I recently stumbled across an issue that seemed to be caused by not following the Rule of 3/5: after ‘reconstructing’ an object by assigning a newly-constructed temporary object to it, my program began crashing with some kind of use-after-free error.

I decided to do some research, which sent me down the rabbit hole that is copy constructors, copy assignment operators, move constructors, and move assignment operators.

After I picked my jaw up off the floor, I set about adding these to one of my classes. Unfortunately, the code was quite large, verbose, and full of duplication:

SpriteMappings::SpriteMappings(const SpriteMappings &other)
	: frames(other.frames)
    , pieces(other.pieces)
{
	this->frames = new Frame[this->total_frames];
	std::copy(other.frames, other.frames + other.total_frames, this->frames);

	this->pieces = new Piece[this->total_pieces];
	std::copy(other.pieces, other.pieces + other.total_pieces, this->pieces);
}

SpriteMappings::SpriteMappings(SpriteMappings &&other)
    : total_frames(other.total_frames)
	, total_pieces(other.total_pieces)
{
	this->frames = other.frames;
	this->pieces = other.pieces;

	other.frames = nullptr;
	other.total_frames = 0;
	other.pieces = nullptr;
	other.total_pieces = 0;
}

SpriteMappings& SpriteMappings::operator=(const SpriteMappings &other)
{
	if (this->total_frames != other.total_frames)
	{
		this->total_frames = other.total_frames;
		delete[] this->frames;
		this->frames = new Frame[this->total_frames];
	}

	std::copy(other.frames, other.frames + other.total_frames, this->frames);

	if (this->total_pieces != other.total_pieces)
	{
		this->total_pieces = other.total_pieces;
		delete[] this->pieces;
		this->pieces = new Piece[this->total_pieces];
	}

	std::copy(other.pieces, other.pieces + other.total_pieces, this->pieces);

	return *this;
}

SpriteMappings& SpriteMappings::operator=(SpriteMappings &&other)
{
	this->total_frames = other.total_frames;
	delete[] this->frames;
	this->frames = other.frames;

	this->total_pieces = other.total_pieces;
	delete[] this->pieces;
	this->pieces = other.pieces;

	other.frames = nullptr;
	other.total_frames = 0;
	other.pieces = nullptr;
	other.total_pieces = 0;

	return *this;
}

SpriteMappings::~SpriteMappings()
{
	delete[] this->frames;
	delete[] this->pieces;
}

I didn’t like this, especially since a copy/move constructor and its corresponding assignment operator seemed to mostly do the same thing – could these not share code somehow?

A method I found that did allow a constructor and assignment operator to share code was the ‘copy and swap idiom‘. Not only that, but it also allowed copy constructors/operators to share code with move constructors/operators. This code compactness seemed great, but I didn’t like that the process of swapping required a third, temporary object. Considering that my objects were responsible for large buffers, this seemed like an awful waste of RAM.

The code that I’d written had a lot of duplication: the code used to copy/move each of the object’s buffers was exactly the same. This had me wondering if could make a buffer class that would allow me to make both buffers share their copy/move code. But, wait, doesn’t C++ already have a bunch of container classes that do that? After giving it some thought, I settled on replacing my class’s buffers with vectors, and, as a result, I was able to greatly simplify the constructors and assignment operators:

 SpriteMappings::SpriteMappings(const SpriteMappings &other)
	: frames(other.frames)
    , pieces(other.pieces)
{

}

SpriteMappings::SpriteMappings(SpriteMappings &&other)
    : frames(std::move(other.frames))
    , pieces(std::move(other.pieces))
{

}

SpriteMappings& SpriteMappings::operator=(const SpriteMappings &other)
{
	this->frames = other.frames;
	this->pieces = other.pieces;

	return *this;
}

SpriteMappings& SpriteMappings::operator=(SpriteMappings &&other)
{
	this->frames = std::move(other.frames);
	this->pieces = std::move(other.pieces);

	return *this;
}

SpriteMappings::~SpriteMappings()
{
	
}

My, it’s so minimal! It’s so sleek! It’s so efficient that the destructor doesn’t need any code!

Wait… the destructor doesn’t need any code?

The Rule of 3/5 says that if you need a copy/move constructor, copy/move assignment operator, or a destructor, then you probably need all of them. But clearly I don’t actually need an explicit destructor anymore, as the default implicit one will do the job just fine!

Actually… now that I think about it, all of those methods can be replaced with their defaults.

After doing that, here’s my code:

// Haha

That’s right: there isn’t any! By using the proper containers, I don’t need explicit copy/move constructors, copy/move assignment operators, or even a destructor anymore! The code’s practically writing itself!

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: