Rule Of Five – C++

Hello and Welcome to my blog!

To keep my skills sharp while I am looking for work, I have been reviewing what I have learned at college.

Today I reviewed the Rule of Five in C++. The rule of five is the programming pattern that states when a class implements any of the following functions, it must implement all of them. A rule of three exist as well, the rule of five is an expanded version.

The Five functions in the rule of five are…

1 – Copy Assignment Operator
2 – Copy Constructor
3 – Deconstructor
4 – Move Assignment Operator
5 – Move Constructor

I decided to Implement the rule of five two ways, first as a Templated class and second as a Non-Templated class.

This code was good practice, and I am planning on releasing more blogs like this one soon. All the code is below, thanks for reading my blog.

Templated Rule Of Five

template < class T >
  class RuleOfFiveTemplated {
    size_t size = 0;
    T * data = nullptr;
    public:
      // Default Constructor
      RuleOfFiveTemplated() {
        std::cout << (void * ) this << ": RuleOfFiveTemplated constructor()\n";
      }

    // Constructor Overload
    RuleOfFiveTemplated(size_t size): size(size), data(new T[size]) {
      cout << (void * ) this << ": RuleOfFiveTemplated (" << size << ") constructor\n";
    }

    // One - Assignment Operator
    RuleOfFiveTemplated & operator = (const RuleOfFiveTemplated & rhs) {
      cout << (void * ) this << ": RuleOfFiveTemplated assignment operator, size = " << size << ", rhs.size = " << rhs.size << endl;
      if (this != & rhs) {
        delete[] data;
        data = nullptr;
        size = 0;

        if (rhs.data) {
          size = rhs.size;
          try {
            data = new T[size];
            memcpy(data, rhs.data, size * sizeof(T));
          } catch (const std::bad_alloc & err) {
            cout << (void * ) this << ": RuleOfFiveTemplated Failed to copy value inside data: " << err.what() << endl;
          }
        }
      } else {
        cout << (void * ) this << ": RuleOfFiveTemplated copy assignment operator called on itself" << endl;
      }
      return *this;
    }

    // Two - Copy Constructor
    RuleOfFiveTemplated(const RuleOfFiveTemplated & rhs) {
      cout << (void * ) this << ": RuleOfFiveTemplated copy constructor, size = " << size << ", rhs.size = " << rhs.size << endl;
      data = nullptr;
      * this = rhs;
    }

    // three - Move Assignment Operator
    RuleOfFiveTemplated && operator = (RuleOfFiveTemplated && rhs) noexcept {
      cout << (void * ) this << ": RuleOfFiveTemplated move assignment operator, size = " << size << ", rhs.size = " << rhs.size << endl;
      if (this != & rhs) {
        delete[] data;

        size = rhs.size;
        data = rhs.data;

        rhs.size = 0;
        rhs.data = nullptr;
      } else {
        cout << (void * ) this << ": RuleOfFiveTemplated move assignment operator called on itself\n";
      }
      return std::move( * this);
    }

    // Four - Move Constructor
    RuleOfFiveTemplated(RuleOfFiveTemplated && rhs) noexcept {
        cout << (void * ) this << ": RuleOfFiveTemplated move constructor, size = " << size << ", rhs.size = " << rhs.size << endl;
        data = nullptr;
        * this = std::move(rhs);
      }

      // Five - De-Constructor
      ~RuleOfFiveTemplated() {
        cout << (void * ) this << ": RuleOfFiveTemplated destructor, size=" << size << "\n";
        delete[] data;
      }

    // Print
    void print() {
      cout << (void * ) this << ": size=" << size << " (" << size * sizeof(T) << " BYTES)\n";
    }
  };

Non-Templated Rule Of Five

class RuleOfFive {
	size_t size = 0;
	double* data = nullptr;
public:
	RuleOfFive() {
		cout << (void*)this << ": RuleOfFive default constructor" << endl;
	}

	RuleOfFive(double size) : size(size), data(new double[size]) {
		cout << (void*)this << ": RuleOfFive constructor overload." << endl;
	}

	// One - Copy assignment operator
	RuleOfFive& operator=(RuleOfFive& rhs) {
		cout << (void*)this << ": RuleOfFive copy assignment operator, size = " << size << ", rhs.size = " << rhs.size << endl;
		if (this != &rhs) {
			delete[] data;
			data = nullptr;
			size = 0;

			if (rhs.data) {
				size = rhs.size;
				try
				{
					data = new double[size];
					memcpy(data, rhs.data, size * sizeof(double));
				}
				catch (const std::bad_alloc&)
				{
					cout << (void*)this << ": RuleOfFive Failed to copy value inside data" << endl;
				}
			}
		}
		else {
			cout << (void*)this << ": RuleOfFive copy assignment operator called on itself" << endl;
		}

		return *this;
	}

	// Two - Copy Constructor
	RuleOfFive(RuleOfFive& rhs) {
		cout << (void*)this << ": RuleOfFive copy constructor, size = " << size << ", rhs.size = " << rhs.size << endl;
		data = nullptr;
		*this = rhs;
	}

	// Three - Move Assignment operator
	RuleOfFive&& operator=(RuleOfFive&& rhs) noexcept {
		cout << (void*)this << ": RuleOfFive move assignment operator, size = " << size << ", rhs.size = " << rhs.size << endl;
		if (this != &rhs) {
			delete[] data;

			size = rhs.size;
			data = rhs.data;

			rhs.size = 0;
			rhs.data = nullptr;
		}
		else {
			cout << (void*)this << ": RuleOfFive move assignment operator called on itself" << endl;
		}

		return std::move(*this);
	}

	// Four - Move constructor
	RuleOfFive(RuleOfFive&& rhs) noexcept {
		cout << (void*)this << ": RuleOfFive move constructor, size = " << size << ", rhs.size = " << rhs.size << endl;
		data = nullptr;
		*this = std::move(rhs);
	}

	// Five - Deconstuctor
	~RuleOfFive() {
		cout << (void*)this << ": RuleOfFive deconstuctor" << endl;
	}

	// Print
	void print()
	{
		cout << (void*)this << ": size=" << size << " (" << size * sizeof(double) << " BYTES)" << endl;
	}
};

Main Function

#include <iostream>
#include <cstring> 
using namespace std;

int main(int argc, char** argv)
{
	cout << "Rule Of Five" << endl << endl;

	// Test Default Constructors
	cout << endl << "Testing Default Constructor" << endl << "=========================================" << endl;

	RuleOfFive x; 
	cout << "x created : ";
	x.print();
	
	RuleOfFiveTemplated<double> tx;
	cout << "tx created : ";
	tx.print();

	// Test Constructor Overloads
	cout << endl << "Testing Constructor Overloads" << endl << "=========================================" << endl;

	RuleOfFive y(1000);
	cout << "y created : ";
	y.print();

	RuleOfFiveTemplated<double> ty(1000);
	cout << "ty created : ";
	ty.print();

	// Test Copy Assignment operator
	cout << endl << "Test Rule One - Copy Assignment operator" << endl << "=========================================" << endl;

	x = y;
	cout << "x : lhs : ";
	x.print();
	cout << "y : rhs : ";
	y.print();

	tx = ty;
	cout << "tx : lhs : ";
	tx.print();
	cout << "ty : rhs : ";
	ty.print();

	// Test Copy Constructor
	cout << endl << "Test Rule Two - Copy Constructor" << endl << "=========================================" << endl;

	RuleOfFive z(x);
	cout << "z created : lhs : ";
	z.print();
	cout << "x : rhs : ";
	x.print();

	RuleOfFiveTemplated<double> tz(tx);
	cout << "tz created : lhs : ";
	tz.print();
	cout << "tx : rhs : ";
	tx.print();

	// Test Move Assignment operator
	cout << endl << "Test Rule Three - Move Assignment operator" << endl << "=========================================" << endl;

	y = std::move(x);
	cout << "y : lhs : ";
	y.print();
	cout << "x : rhs : ";
	x.print();

	ty = std::move(tx);
	cout << "ty : lhs : ";
	ty.print();
	cout << "tx : rhs : ";
	tx.print();

	// Test Move Constructor
	cout << endl << "Test Rule Four - Move Constructor" << endl << "=========================================" << endl;

	RuleOfFive a(std::move(y));
	cout << "a created : lhs : ";
	a.print();
	cout << "y : rhs : ";
	y.print();

	RuleOfFiveTemplated<double> ta(std::move(ty));
	cout << "ta created : lhs : ";
	ta.print();
	cout << "ty : rhs : ";
	ty.print();

	// Test Deconstructors
	cout << endl << "Test Rule Five - Deconstructors" << endl << "=========================================" << endl;
}

	

Leave a Reply

Your email address will not be published. Required fields are marked *