
Data Structure
Networking
RDBMS
Operating System
Java
MS Excel
iOS
HTML
CSS
Android
Python
C Programming
C++
C#
MongoDB
MySQL
Javascript
PHP
- Selected Reading
- UPSC IAS Exams Notes
- Developer's Best Practices
- Questions and Answers
- Effective Resume Writing
- HR Interview Questions
- Computer Glossary
- Who is Who
C++ Rule Of Three.
The rule of three in C++ states that, if a class in C++ has any one (or more) of the following, then it should define all three.
- Destructor
- Copy Constructor
- Copy Assignment Constructor
These three are special member functions of class and are responsible for managing resources such as dynamic memory, file handles, sockets, etc. And if one of them is defined explicitly, means that class is managing those resources manually (like memory using new/delete), and if we fail to define others then it can lead to memory leaks, shallow copies, double deletions and undefined behavior. This is also known as the Law of Big Three or The Big Three.
When you explicitly define one of these functions, then the compiler still automatically provides default (implicit) versions of others. But there are most chances of those two to be incorrect or unsafe because they perform shallow copies (which only copy pointers) not the actual data they point to. And when both objects (original and copied) go out of scope, the destructor runs for both, and tries to delete the same pointer, which leads to double deletion and causes a runtime error or undefined behavior.
C++ Example Demonstrating the Rule of Three
Here is the following example code of implementing a full rule of three in C++ that's constructor, copy constructor, copy assignment operator, and destructor showcasing how all are defined to properly manage dynamic memory and ensuring deep copy to prevent issues like shallow copy, memory leaks, and double deletion.
#include <iostream> using namespace std; class Numbers { private: int num; int* ptr; public: // this is a constructor, which allocates dynamic memory // and performs deep copy of passed array Numbers(int n, int* p) { num = n; ptr = new int[num]; for (int i = 0; i < num; ++i) { ptr[i] = p[i]; } cout << "Constructor called\n"; } // a copy constructor (part of Rule of Three), // this performs deep copy to prevent two // objects pointing to same memory Numbers(const Numbers& other) { num = other.num; ptr = new int[num]; for (int i = 0; i < num; ++i) { ptr[i] = other.ptr[i]; } cout << "Copy Constructor called\n"; } // copy assignment operator (Rule of Three) Numbers& operator=(const Numbers& other) { if (this != &other) { // self-assignment check and then delete current memory delete[] ptr; num = other.num; ptr = new int[num]; for (int i = 0; i < num; ++i) { ptr[i] = other.ptr[i]; } } cout << "Copy Assignment Operator called\n"; return *this; } // a destructor (again a part of Rule of Three), // which frees the dynamically allocated memory ~Numbers() { delete[] ptr; ptr = nullptr; cout << "Destructor called\n"; } }; int main() { int arr[4] = {11, 22, 33, 44}; Numbers Num1(4, arr); // Constructor is called Numbers Num2(Num1); // Copy Constructor is called (deep copy) Num2 = Num1; // Copy Assignment Operator is called return 0; // Destructor called for Num2 and Num1 (in reverse order) }
Output
Constructor called Copy Constructor called Copy Assignment Operator called Destructor called Destructor called