Click here to Skip to main content
15,891,033 members
Articles / Programming Languages / C++

Exception Safe Assignment

Rate me:
Please Sign up or sign in to vote.
3.33/5 (2 votes)
8 Apr 2019MIT1 min read 4K   1   1
Exception safe assignment operator of resource owning objects

Longer title: exception safe assignment operator of resource owning objects. Uff. Because the object owns a resource, how do we write an exception safe assignment operator which will have to free up the old and allocate the new resource. By exception safe, I don’t mean that it will never throw, that’s not possible. Instead, I mean safe in the sense that it either succeeds OR in case of exceptions, the state of assigned to object is exactly as it was prior to the assignment. Like this:

C++
int main()
{
	S s1, s2;
	s1 = s2;
}

If assignment operator...

C++
s1 = s2

...throws an exception, we want the state of...

C++
s1

...and...

C++
s2

...to be as it was in line #3.

The trick is two fold:

  1. a copy constructor is needed, and
  2. noexcept swap function. Like this:
C++
S(const S& s)
: m_resource(new int)
{
	*m_resource = *s.m_resource;
}
// ...
void swap(S& s) noexcept
{
	std::swap(m_resource, s.m_resource);
}

Here, the copy constructor allocates the new resource first, then copies its content; the swap function just swaps pointers to the resources, which is always a noexcept operation. Having implemented a copy constructor and swap function, we can now implement every assignment operator to have a strong exception guarantee like this:

C++
S& operator = (const S& s)
{
	S temp(s); // May throw
	swap(temp); // Will never throw
	return *this;
}

Here’s how it works: we first make a temporary copy, which does the resource allocation. At this stage, exceptions can be thrown, but we have not yet modified the assigned to object. Only after the resource allocation succeeds do we perform the noexcept swap. The destructor of your temporary object will take care of cleaning up the currently owned resource (that’s RAII at its best).

Complete listing (assignment.cpp):

C++
#include <iostream>
#include <utility>

using namespace std;

struct S
{
	S()
	: m_resource(new int)
	{
		cout << "S()" << endl;
		*m_resource = 1;
	}

	S(const S& s)
	: m_resource(new int)
	{
		cout << "S(const S&)" << endl;
		*m_resource = *s.m_resource;
	}

	S(S&&) = delete;

	S& operator = (const S& s)
	{
		cout << "operator = (const S&)" << endl;
		S temp(s); // May throw
		swap(temp); // Will never throw
		return *this;
	}

	S& operator = (S&&) = delete;

	~S()
	{
		cout << "~S()" << endl;
		delete m_resource;
	}

	void swap(S& s) noexcept
	{
		std::swap(m_resource, s.m_resource);
	}

	int* m_resource;
};

int main()
{
	S s1, s2;
	s1 = s2;
}

Program output:

S()
S()
operator = (const S&)
S(const S&)
~S()
~S()
~S()

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCOW? Pin
Paul Tait8-Apr-19 22:39
Paul Tait8-Apr-19 22:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.