The Copy Constructor, operator=(), and Destructor

Learn about the importance of copy constructors and destructors, along with their implementation.

The matrix case study: Adding the copy constructor

Let’s implement the copy constructor using the deep copy for our Matrix class. We’ll add a parameterized constructor that receives a Matrix object as the parameter. Let’s add the signature in the Matrix.h file.

Matrix(const Matrix& M);

Let’s add the definition in the Matrix.cpp file.

Matrix::Matrix(const Matrix& M)
{
this->Vs = nullptr;
this->Allocate2D(M.rows,M.cols); // Allocates new memory for the matrix according to the given number of rows and columns
this->copyValues(M); // Coppies the object in the newly allocated space
}
Implementation of the copy constructor
  • Here, we can see that we allocate memory using the Allocate2D() function and copy the values using copyValues() function in the newly allocated memory.

Overloading the assignment operator (=)

Let’s overload the assignment operator (=) by implementing the operator=() function for the Matrix class.

Matrix& Matrix::operator=(const Matrix& M)
{
if (this == &M)
return *this;
if(this->Vs!=nullptr)
this->DeleteMatrix();
this->Allocate2D(M.rows, M.cols);
this->copyValues(M);
return *this;
}
Overloading the assignment operator (=) for the Matrix class

Why do we need a destructor in the Matrix class?

The destructor is needed in this context to ensure proper cleanup and deallocation of resources held by a Matrix object. In particular, the destructor is responsible for releasing dynamically allocated memory used by a Matrix object, such as the memory for storing matrix elements and the memory for the row pointers. By defining a destructor, we can guarantee that the Matrix object’s resources are properly released when it goes out of scope or is explicitly destroyed, preventing memory leaks and freeing system resources.

Matrix::~Matrix()
{
this->DeleteMatrix();
}
void Matrix::DeleteMatrix()
{
for (int i = 0; i < this->rows; i++)
delete[]this->Vs[i];
delete[]this->Vs;
this->Vs = nullptr;
this->rows = 0;
this->cols = 0;
}
The destructor for the Matrix class
  • The above code snippet shows the destructor Matrix::~Matrix() and the accompanying member function Matrix::DeleteMatrix() in the Matrix class. The destructor is responsible for ensuring a proper cleanup of the Matrix object. It calls the DeleteMatrix() function, which iterates through each row of the matrix and deletes the dynamically allocated memory for the elements in that row. It then deletes the memory for the row pointers, sets the matrix pointer to nullptr, and updates the number of rows and columns to 0, indicating an empty matrix. By calling DeleteMatrix() in the destructor, the Matrix object is effectively cleaned up, and any dynamically allocated memory is deallocated, preventing memory leaks and freeing system resources.

Complete basic implementation

Here’s the complete implementation of the copy constructor and overloading of the assignment (=) operator.


3 3
4 7 5
9 8 5
3 7 2
Complete implementation of the copy constructor and the overloaded = operator
  • This code snippet is a program that loads matrices from a file, makes a copy of the loaded matrices, and displays the original and copied matrices. The LoadMatrix() function reads the matrices from the file and stores them in a dynamically allocated array. In the main() function, the LoadMatrix() function is called to load the matrices. The pointer M2 is then assigned the value of M1 to create a copy. Finally, the original and copied matrices are displayed.

Here’s the pictorial representation of the two matrices M1 and M2 (stored as arrays) in terms of memory.

Press + to interact
Deep copy of a matrix object
Deep copy of a matrix object

Now that we have established the necessary foundation of loading matrices and understand copy constructors and the assignment operator, we can proceed to implement various operators on matrices. By doing so, we aim to create a comprehensive matrix calculator that can perform a wide range of operations.