// File: ex6-7.cpp

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

class Matrix
{
private:
    int** element;
    int rows;
    int cols;
    void alloc(void);
    void release(void);
public:
    Matrix(int = 0, int = 0);  // also default constructor
    Matrix(const Matrix&); // copy constructor
    ~Matrix();
    void print(void) const;
    Matrix operator+(const Matrix&) const;
    Matrix& operator=(const Matrix&);
};

Matrix::Matrix(int r, int c) : rows(r), cols(c)
{
    cout << "Constructor called for object " << this <<endl;
    alloc();

    // initialize Matrix elements with random numbers 0-9
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            element[i][j] = rand()%10;
}

Matrix::Matrix(const Matrix& arg) : rows(arg.rows), cols(arg.cols)
{
    cout << "\nIn copy constructor for object " << this;
    cout << ", argument: " << &arg << endl;

    alloc();
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            element[i][j] = arg.element[i][j];
}

Matrix::~Matrix(void)
{
    cout << "\n~~ Destructor called for object: " << this << endl;

    release();
}

void Matrix::alloc(void)        // allocate heap memory for elements
{
    cout << "Allocate heap memory for Matrix " << this << " elements\n";

    element = new int*[rows];
    for (int i = 0; i < rows; i++)
        element[i] = new int[cols];
}

void Matrix::release(void)
{
    cout << "I got rid of Matrix " << this << "'s elements\n";

    for (int i = 0; i < rows; i++)
        delete [] element[i];
    delete [] element;
}

void Matrix::print(void) const
{
    cout << "\nMatrix values for object: "<< this << endl;

    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
            cout << element[i][j] << '\t';
        cout << endl;
    }
}

Matrix Matrix::operator+(const Matrix& arg) const
{
    cout << "\nExecuting operator+ for object: " << this;
    cout << ", argument: " << &arg << endl;

    if (rows != arg.rows || cols != arg.cols)
    {
        cerr << "Invalid Matrix addition\n";
        return (*this);
    }

    Matrix temp(rows,cols);

    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            temp.element[i][j] = element[i][j] + arg.element[i][j];

    temp.print();
    return temp;
}

Matrix& Matrix::operator=(const Matrix& arg)
{
    cout << "\nExecuting operator= for object: " << this;
    cout << ", argument: " << &arg << endl;

    // Make sure rows and cols match the argument
    if (rows != arg.rows || cols != arg.cols)
    {
        release();
        rows = arg.rows;
        cols = arg.cols;
        alloc();
    }

    for (int i = 0; i < arg.rows; i++)
        for (int j = 0; j < arg.cols; j++)
            element[i][j] = arg.element[i][j];

    return *this;
}

int main(void)
{
    Matrix A(3,4), B(3,4), C;
    A.print();
    B.print();
    C.print();
    C = A + B;
    C.print();
}

******  OUTPUT  ******

Constructor called for object 0x28fee8
Allocate heap memory for Matrix 0x28fee8 elements
Constructor called for object 0x28fedc
Allocate heap memory for Matrix 0x28fedc elements
Constructor called for object 0x28fed0
Allocate heap memory for Matrix 0x28fed0 elements

Matrix values for object: 0x28fee8
1       7       4       0
9       4       8       8
2       4       5       5

Matrix values for object: 0x28fedc
1       7       1       1
5       2       7       6
1       4       2       3

Matrix values for object: 0x28fed0

Executing operator+ for object: 0x28fee8, argument: 0x28fedc
Constructor called for object 0x28fe3c
Allocate heap memory for Matrix 0x28fe3c elements

Matrix values for object: 0x28fe3c
2       14      5       1
14      6       15      14
3       8       7       8

In copy constructor for object 0x28fef4, argument: 0x28fe3c
Allocate heap memory for Matrix 0x28fef4 elements

~~ Destructor called for object: 0x28fe3c
I got rid of Matrix 0x28fe3c's elements

Executing operator= for object: 0x28fed0, argument: 0x28fef4
I got rid of Matrix 0x28fed0's elements
Allocate heap memory for Matrix 0x28fed0 elements

~~ Destructor called for object: 0x28fef4
I got rid of Matrix 0x28fef4's elements

Matrix values for object: 0x28fed0
2       14      5       1
14      6       15      14
3       8       7       8

~~ Destructor called for object: 0x28fed0
I got rid of Matrix 0x28fed0's elements

~~ Destructor called for object: 0x28fedc
I got rid of Matrix 0x28fedc's elements

~~ Destructor called for object: 0x28fee8
I got rid of Matrix 0x28fee8's elements