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;
}