# Lesson 44

### Matrix Class

After talking at length about the concept of matrices in the previous lecture, we are going
to have a review of ‘code’ today with special emphasis on concepts of constructors and
destructors. We may also briefly discuss where should a programmer return by value,
return by reference besides having a cursory look on the usage of the pointers.
The data structure of the Matrix class is very simple. We have defined an arbitrary
number of functions and operators. You may add and subtract more functions in it. In this
lecture, we have chosen just a few as it is not possible to discuss each and every one in a
brief discourse. As discussed earlier, the code is available to use that can be compiled and
run. This class is not complete and a lot of things can be added to it. You should try to
enhance it, try to improve and add to its functionality.
One of the things that a programmer will prefer to do with this class is its templatization.
We have implemented it for type double. It was done due to the fact that the elements of
the Matrix are of type double. You may like to improve and make it a templatized class
so that it can also handle integer elements. This class is written in a very straightforward
manner. The double is used only for elements to develop it into a Matrix class for
integers if you replace all the double word with the int. Be careful, you cannot revert it
Page 562
back to double by just changing all the int to double as integers are used other than
element types.
Let’s discuss the code beginning with the data structure of the class. In keeping the
concepts of data hiding and encapsulation, we have put the data in the private section of
the class. We have defined number of rows (i.e. numRows) and number of columns (i.e.
numCols
) as integers. These will always be whole number. As we cannot have one and a
half row or column, so these are integers.
The private part of the Matrix class is:
int numRows, numCols;
double **elements;
These are fixed and defined. So whenever you have an object of this class, it will have a
certain number of rows and certain number of columns. These are stored in the variablesnumRows
and numCols. Where will be the values of the elements of this matrix? The
next line is double **elements; i.e. elements is an array of pointers to double. First * is
for array and second * makes it a pointer. It means that ‘elements’ is pointing to a twodimension
array of double. When we say elements[i], it means that it is pointing to an
array of double. If we say elements[i][j], it means we are talking about a particular
element of type double. We have not taken the two-dimension array in the usual way but
going to dynamic memory allocation. We have developed a general Matrix class. The
objects created from it i.e. the instances of this class, could be small matrices as 2*2.
These may also be as big matrix as 20*20. In other words, the size of the matrix is
variable. In fact, there is no requirement that size should be square. It may not be 20*20.
It may be a matrix of 3*10 i.e. three rows and ten columns. So we have complete
flexibility. When we create an object, it will store the number of rows in numRows and
number of columns in numCols. The elements will be dynamically allocated memory in
which the double value is stored.
While using the dynamic memory, it is good to keep in mind that certain things are
necessary to be implemented in the class. 1) Its constructor should make memory
allocation. We will use the new operator, necessitating the need of defining its destructor
also. Otherwise, whenever we create an object, the memory will be allocated from the
free store and not de-allocated resulting in the wastage of the memory. Therefore a
destructor is necessary while de-allocating memory. 2) The other thing while dealing
with classes having dynamic memory allocation, we need to define an assignment
operator. If we do not define the assignment operator, the default will do the member
wise copy i.e. the shallow copy. The value of pointer will be copied to the pointer but the
complete data will not be copied. We will see in the code how can we overcome this
problem..
Let’s discuss the code in the public interface of the class. The first portion of the public
interface is as:
Page 563
Matrix(int=0, int=0); // default constructor
Matrix(const Matrix & ); // copy constructor
~Matrix(); // Destructor
In the public interface, the first thing we have is the constructors. In the default
constructor, you see the number of columns and number of rows. The default values are
zero. If you just declare a matrix as Matrix m, it will be an object of class Matrix having
zero rows and zero columns i.e. memory is not allocated yet. Then we have also written a
copy constructor. Copy constructor becomes necessary while dealing with dynamic
memory allocation in the class. So we have to provide constructor, destructor, assignment
operator and the copy constructor. The declaration line of copy constructor is always the
same as the name of the class. In the argument list, we have a reference to an object of
the same class i.e. Matrix(const Matrix & ); The & represents the reference. This is the
prototype. After constructors, we have defined a destructor. Its prototype is also standard.
Here it is ~Matrix(); it takes no argument and returns nothing. Remember that
constructors and destructors return nothing.
After this, we have utility functions for the manipulation of the Matrix.
int getRows(void) const; // Utility fn, returns no. of rows
int getCols(void) const; // Utility fn, returns no. of columns
const Matrix & input(istream &is = cin); // Read from istream i.e. keyboard
const Matrix & input(ifstream &is); // Read matrix from ifstream
void output(ofstream &os) const; // Utility fn, prints matrix with graphics
void output(ostream &os = cout) const; // Utility fn, prints matrix with graphics
const Matrix& transpose(void); // Transpose the matrix and return a ref
We have defined two small functions as getRows and getCols which will return the
number of rows and number of columns of the matrix respectively. Here, you are writing
the class and not the client which will use this class. During the usage of this class, there
may be need of some more functions. You may need to add some more functionality
depending on its usage. At this point, a function may be written which will return some
particular row as a vector or the nth column of a matrix. These things are left for you to
do.
We need some function to input values into the matrix. There are two input functions
both named as input. One is used to get the value from the keyboard while the other to
get the values from some file. There is a little bit difference between the declaration of
these two that will be discussed later. The input function which will get the input from
the keyboard, takes an argument of type istream. Remember that cin, that is associated
with the keyboard, is of type istream. These are member functions and called by some
object of Matrix. The Matrix object will be available to it through this pointer. The
Page 564
istream
is passed as argument and we have given a temporary name to the input argument
i.e. is and its default value is cin. It is a nice way of handling default values. If you write
in the main program as m.input() where m is an object of type Matrix, it will get the input
from the keyboard. This is due to the fact that it is getting cin by default. We have
defined another input function and that is an example of function overloading. This
function takes ifstream as input argument i.e. a file stream. If we want to read the matrix
data from some file, this input function may be employed. There is no default argument
in it.
Similarly we have two output functions. The names are chosen as arbitrary. You may
want to use print or display. One output function will display the matrix on the screen. Its
argument is ostream while the default value will be cout. The other output function will
be used when we want to write the matrix in some file. It takes ofstream as argument and
we have not provided any default argument to it. You will have to provide a file handle to
use this function.
Let’s continue to talk about the arithmetic manipulations we want to do with the matrices.
Matrix operator+( Matrix &m) const; // Member op + for A+B; returns matrix
Matrix operator + (double d) const; // Member op + for A+d; returns matrix
const Matrix & operator += (Matrix &m); // Member op += for A +=B
friend Matrix operator + (double d, Matrix &m); // friend operator for d+A
Matrix operator-( Matrix & m) const; // Member op - for A-B; returns matrix
Matrix operator - (double d) const; // Member op - for A-d;
const Matrix & operator -= (Matrix &m); // Member op -= for A-=B;
friend Matrix operator - (double d, Matrix& m); // Friend op - for d-A;
Matrix operator*(const Matrix & m); // Member op * for A*B;
Matrix operator * (double d) const; // Member op * for A*d;
friend Matrix operator * (const double d, const Matrix& m);//friend op*, d*A
const Matrix& transpose(void); // Transpose the matrix and return a ref
const Matrix & operator = (const Matrix &m); // Assignment operator
We have defined different functions for plus. Some of these are member operators while
the others called as friend. The first plus operator is to add two matrices. It is a member
operator that takes a Matrix object as argument. The second one is to add some double
number to matrix. Remember that we are having a class of Matrix with double elements.
So we will add double number to it. It is also a member operator. When you write
something like m + d, where m is an object of type Matrix and d is a double variable, this
operator will be called. Here Matrix object is coming on the left-hand side and will be
available inside the operator definition by this pointer. On the other hand, the double
value d is presented as argument to the operator.
Page 565
There is another variety of adding double to the matrix i.e. if we write as d + m. On the
left-hand side, we don’t have Matrix. It cannot be a member function as the driving force
is not an object of our desired class. If not a member function, it will have two arguments.
First argument will be of type double while the second one is going to be of Matrix object
in which we will add the double number. As it is not a member function and we want to
manipulate the private data members of the class, it has to be a friend function. Function
will be defined outside, at file level scope. In other words, it will not be a member
function of class Matrix but declared here as a friend. It is defined as:
Friend Matrix operator + (double d, Matrix &m);
Its return type is Matrix. When we add a double number, it will remain as Matrix .We
will be able to return it. The final variant is included as an example of code reuse i.e. the
+= operator. We write in our program i += 3; and it means i = i + 3; It would be nice if
we can write A += B where A and B both are matrices.
After plus, we can do the same thing with the minus operator. Having two matrices-A and
B
, we want to do A-B. On the left hand side, we have Matrix, so it will be a member
operator. The other matrix will be passed as argument. In A-B, A is calling this operator
while B being passed as argument. We can also do A-d where A is a Matrix and d is of
type double. For this, we will have to write a member operator. All these operators are
overloaded and capable of returning Matrix. In this overloaded operator, double will be
passed as argument. Then we might want to do it as d - A, where d is double variable and
A
is of type Matrix. Since the left hand side of the minus (-) is double, we will need a
friend function, as it is not possible to employ a member function. Its prototype is as:
friend Matrix operator - (double d, Matrix& m);
Let’s now talk about multiplication. We have discussed somewhat about it in the previous
lecture. For multiplication, the first thing one needs to do is the multiplication of two
matrices i.e. A*B where A and B both are matrices. As on the left hand side of the
operator * we have a Matrix so it will be a member function so A can be accessed through
this
pointer. B will be passed as argument. A matrix should be returned. Thus, we have a
member operator that takes an argument of type Matrix and returns a Matrix. You may
like to do the same thing, which we did with the plus and minus. In other words, a
programmer will multiply a matrix with a double or multiply a double with a matrix. In
either case, we want that a matrix should be returned. So at first, A * d should be a
member function. Whereas d * A will be a friend function. Again the return type will be a
Matrix
.
In case of division, we have only one case i.e. the division of the matrix with a double
number. This is A / d where A is a Matrix while d is a double variable. We will divide all
the elements of the matrix with this double number. This will return a matrix of the same
size as of original.
Page 566
Taking benefit of the stream insertion and extraction operator, we can use double greater
than sign ( >>) and write as >> m where m is a Matrix. For this purpose, we have written
stream extraction operator. The insertion and extraction functions will be friend functions
as on the left-hand side, there will be either input stream or output stream.
We have also defined assignment function in it. As we are using dynamic memory
allocation in the class, assignment is an important operator. Another important function is
transpose, in which we will interchange the rows into columns and return a Matrix. This
is the interface of our Matrix class. You may want to add other functions and operators
like +=, -=, *=. But it is not possible to add /= due to its very limited scope.
We have used the keyword const in our class. You will find somewhere the return type as
Matrix
and somewhere as Matrix &. We will discuss each of these while dealing with
the code in detail.
Definition of Matrix Constructor

Matrix(int = 0, int = 0); // default constructor
We are using the default argument values here. In the definition of this function, we will
not repeat the default values. Default values are given at one place. The definition code is
as:
Matrix::Matrix(int row, int col) //default constructor
{
numRows = row;
numCols = col;
elements = new (double *) [numRows];
for (int i = 0; i < numRows; i++){
elements[i] = new double [ numCols];
for(int j = 0; j < numCols; j++)
elements[i][j] =0.0; // Initialize to zero
}
}
Two integers are passed to it. One represents the number of rows while the other is
related to the number of columns of the Matrix object that we want to create. At first,
we will assign these values to numRows and numCols that form a part of the data
structure of our class. Look at the next line. We have declared elements as **elements i.e.
the array of pointers to double. We can directly allocate it by getting the elements of
Page 567
numRows
times numCols of type double from free store. But we don’t have the double
pointer. Therefore, first of all, we say that element is array of pointer to double and
allocate pointers as much as needed. So we use the new operator and use double* cast. It
means that whatever is returned is of type pointer to double. How many pointers we
need? This is equal to number of rows in the matrix. There is one pointer for each of the
rows i.e. now a row can be an array. This is a favorable condition, as we have to enter
data in the columns. Now elements is numRows number of pointers to double. After
having this allocation we will run a loop.
In C/C++ languages, every row starts from zero and ends with upper-limit – 1. Now in
the loop, we have to allocate the space for every elements[i]; How much space is needed
here? This will be of type double and equal to number of columns. So when we say new
double[numCols],
it means an array of double. As elements[i] represents a pointer and
now it is pointing to an array. Remember that pointers and arrays are synonymous. Now
a pointer is pointing to this array. We have the space now and want to initialize the
matrix. For this, we have written another loop. To assign the value, we will write as:
elements[i][j] = 0.0;
Let’s review this again. First of all we have assigned the values to numRows and
numCols
. Later, we allocated the pointers of double from the free store and assigned to
elements
. Then we got the space for each of this pointer to store double. Finally, we
initialized this space with 0.0. Now we have a constructive matrix.
Is there any exceptional value? We can think of assigning negative values to number of
rows or number of columns. If you want to make sure that this does not happen, you can
put a test by saying that numRows and numCols must be greater than or equal to zero.
Passing zero is not a problem as we have zero space and nothing happens actually. Now
we get an empty matrix of dimension 0*0. But any positive number supplied will give us
a constructor and initialize zero matrix.
Let’s discuss the other constructor. This is more important in the view of this class
especially at a time when we are going to do dynamic memory allocation. This is copy
constructor. It is used when we write in our program as Matrix A(B); where A and B both
are matrices. We are going to construct A while B already exists. Here we are saying that
give us a new object called A which should be identical to the already existing object B.
So it is a copy constructor. We are constructing an object as a copy of another one that
already exists. The other usage of this copy constructor is writing in the main function as
Matrix A = B;
Remember that this is declaration and not assignment statement. Here
again copy constructor will be called. We are saying that give us a duplicate of B and its
name should be A. Here is the code of this function:
Matrix::Matrix(const Matrix &m)
{
numRows = m.numRows;
numCols = m.numCols;
Page 568
elements = new (double *) [numRows];
for (int i = 0; i < numRows; i++){
elements[i] = new double [ numCols];
for(int j = 0; j < numCols; j++)
elements[i][j] = m.elements[i][j];
}
}
We are passing it a constant reference of Matrix object to ensure that the matrix to be
copied is not being changed. Therefore we make it const. We are going to create a brand
new object that presently does not exist. We need to repeat the code of regular
constructor except the initialization part. Its rows will be equal to the rows of the object
supplied i.e. numRows = m.numRows. Similarly the columns i.e. numCols= m.numCols.
Now we have to allocate space while using the same technique earlier employed in case
of the regular constructor. In the default constructor, we initialize the elements with zero.
Here we will not initialize it with zero and assign it the value of Matrix m as
elements[i][j] = m.elements[i][j]
. Remember that we use the dot operator to access the
data members. We have not used the dot operator on the left hand side. This is due to the
fact that this object is being constructed and available in this function through this
pointer. Therefore the dot operator on the left hand side is not needed. We will use it on
the right hand side to access the data members of Matrix m. This is our copy constructor.
In this function, we have taken the number of rows and columns of the object whose copy
is being made. Then we allocate it the space and copy the values of elements one by one.
The other thing that you might want to know is the use of nested loop both in regular
constructor and the copy constructor.
Destructor of Matrix Class

‘Destructor’ is relatively simple. It becomes necessary after the use of new in the
constructor. While creating objects, a programmer gets memory from the free store. So in
the destructor, we have to return it. We will do it as:
delete [] elements;
Remember that ‘elements’ is the variable where the memory has been allocated. The []
simply, indicates that it is an array. The compiler automatically takes care of the size of
the array and the memory allocated after being returned, goes back to the free store.
There is only one line in the destructor. It is very simple but necessary in this case.
Utility Functions of Matrix

The functions getRows() and getCols() are relatively simple. They do not change
anything in the object but only read from the object. Therefore we have made this
function constant by writing the const keyword in the end. It means that it does not
change anything. The code of the getRows() functions is as follows:
int Matrix :: getRows ( ) const
{
Page 569
return numRows;
}
This function returns an int representing the number of rows. It will be used in the main
function as i = m.getRows(); where i is an int and m is a Matrix object. Same thing
applies to the getCols() function. It is of type const and returns an int representing the
number of columns.
Let’s talk about little bit more complicated function. It is the output to the screen
functions. We want that our matrix should be displayed on the screen in a beautiful way.
You have seen that in the books that matrix is written in big square brackets. The code of
the function is as:
void Matrix::output(ostream &os) const
{
// Print first row with special characters
os.setf(ios::showpoint);
os.setf(ios::fixed,ios::floatfield);
os << (char) 218;
for(int j = 0; j < numCols; j++)
os << setw(10) << " ";
os << (char) 191 << "\n";
// Print remaining rows with vertical bars only
for (int i = 0; i < numRows; i++){
os << (char) 179;
for(int j = 0; j < numCols; j++)
os << setw(10)<< setprecision(2) << elements[i][j];
os << (char) 179 << "\n";
}
// Print last row with special characters
os << (char) 192;
for(int j = 0; j < numCols; j++)
os << setw(10) << " ";
os << (char) 217 << "\n";
}
We have used special characters that can be viewed in the command window. We have
given you an exercise of printing the ASCII characters on the screen. After ASCII code
128, we have special graphic symbols. We have used the values of those symbols here.
To print those in the symbol form, we have forced it to be printed as char. If we do not
use the char, it would have printed the number 218 i.e. it would have written an integer.
Page 570
We have forced it to print the character whose ASCII value is 218. Now it prints the
graphic symbol for that character. We have referenced the ASCII table and seen which
symbol will fit in the left corner i.e. 218. So we have written it as:
os << (char) 218;
os
is the output stream. char is forcing it to be print as character. The left corner will be
printed on the screen as . Now we need the space to print the columns of the matrix. In
the first line we will print the spaces using a loop as:
for(int j = 0; j < numCols; j++)
os << setw(10) << " ";
Here we have changed the width as 10. You can change it to whatever you like. Then we
print nothing in the space of ten characters and repeat that for the number of columns in
the matrix. After this, we printed the right corner. This is the first line of the display.
Other lines will also contain the values of the elements of the matrix. These lines will
start with a vertical line and then the element values of the row and in the end we have a
vertical line. For each row, we have a vertical bar and the number values which will be
equal to number of columns (elements in each row equals to the number of columns) and
then a vertical bar in the end. In the beginning of this code, we have used two other
utilities to improve the formatting. Here we have a matrix of type double so every
element of the matrix is double. For double, we have used
os.setf(ios::fixed,ios::floatfield);
that means that it is a fixed display. Scientific notation
will not be used while decimal number is displayed with the decimal point. After this, we
have set the precision with two number of places. So we have a format and our decimal
numbers will always be printed with two decimal places. The numbers are being printed
with a width of ten characters so the last three places will be as .xx. Rest of the code is
simple enough. We have used the nested loops. Whenever you have to use rows and
columns, it will be good to use nested loops. When all the rows have been printed, we
will print the below corners. We referenced the ASCII table, got the graphic symbol,
printed it, left the enough space and then printed the other corner. The matrix is now
complete. When this is displayed on the screen, it seems nicely formatted matrix with
graphic symbols. That is our basic output function.
Let’s look at the file output function. While doing the output on the screen, we made it
nicely formatted. Now you may like to store the matrix in a file. While storing the matrix
in the file, there is no need of these lines and graphic symbol. We only need its values to
read the matrix from the file. So there is a pair of functions i.e. output the matrix in the
file and input from the file. To write the output function, we actually have to think about
the input function.
Suppose, we have declared a 2*2 Matrix m in our program. Somewhere in the program,
we want to populate this matrix from the file. Do we know that we have a 2*2 matrix in
the file. How do we know that? It may 5*5 or 7*3 matrix. So what we need to do is
somehow save the number of rows and columns in the file as well. So the output function
Page 571
that puts out on the file must put out the number of rows and number of columns and then
all of the elements of the matrix. Following is the code of this function:
void Matrix::output(ofstream &os) const
{
os.setf(ios::showpoint);
os.setf(ios::fixed,ios::floatfield);
os << numRows << " " << numCols << "\n";
for (int i = 0; i < numRows; i++){
for(int j = 0; j < numCols; j++)
os << setw(6) << setprecision(2) << elements[i][j];
os << "\n";
}
}
The code is shorter than the other output function due to non-use of the graphical
symbols. First of all, we output the number of rows and number of columns. Then for
these rows and columns, data elements are written. We have also carried out a little bit
formatting. While seeing this file in the notepad, you will notice that there is an extra line
on the top that depicts the number of rows and columns.
Input Functions

Input functions are also of two types like output functions. The first function takes input
from the keyboard while the other takes input from the file. The function that takes input
from the keyboard is written in a polite manner because humans are interacting with it.
We will display at the screen ”Input Matrix size: 3 rows by 3 columns” and it will ask
“Please enter 3 values separated by spaces for row no. 1” for each row. Spaces are
delimiter in C++. So spaces will behave as pressing enter from the keyboard. If you have
to enter four numbers in a row, you will enter as number (space) number (space) number
(space) number before pressing the enter key. We have a loop inside which will process
input stream and storing these values into elements[i][j]; So the difference between this
function and the file input function is 1) It prompts to the user and is polite. 2) It will read
from the keyboard and consider spaces as delimiter.
The other input function reads from the file. We have also stored the number of rows and
number of columns in the file. The code of this function is:
const Matrix & Matrix::input(ifstream &is)
{
int Rows, Cols;
is >> Rows;
is >> Cols;
Page 572
if(Rows > 0 && Cols > 0){
Matrix temp(Rows, Cols);
*this = temp;
for(int i = 0; i < numRows; i++){
for(int j = 0; j < numCols; j++){
is >> elements[i][j];
}
}
}
return *this;
}
First of all, we will read the number of rows and number of columns from the file. We
have put some intelligence in it. It is better to check whether numRows and numCols is
greater than zero. If it is so, then do something. Otherwise, there is nothing to do. If rows
and columns are greater than zero, then there will be a temporary matrix specifying its
rows and columns. These values are read from the file, showing that we have a matrix of
correct size. Now this matrix is already initialized to zero by our default constructor. We
can do two things. We can either read the matrix, return the value or we can first assign it
to the matrix that was calling this function. We have assigned it first as *this = temp; here
temp
is a temporary matrix which is created in this function but *this is whatever this
points to. Remember that this is a member function so this pointer points to the matrix
that is calling this function. All we have to do is to assign the temp to the matrix, which is
calling this function. This equal to sign is our assignment operator, which we have
defined in our Matrix class. If the dimensions of the calling matrix are not equal to the
temp
matrix, the assignment operator will correct the dimensions of the calling matrix. It
will assign the values, which in this case is zero so far. Now we will read the values from
the file using the nested loops. The other way is to read the values from the file and
populate the temp matrix before assigning it to the calling matrix in the end. That is the
end of the function. Remember that the temp matrix, which we have declared in this
function, will be destroyed after the exit from the function. This shows that the
assignment operator is important here. All the values will be copied and it will perform a
deep copy. Does this function return something? Its return type is reference to a const
Matrix
. Its ending line is return *this that means return whatever this points to and it is
returned as reference. The rule of thumb is whenever we are returning the this pointer, it
will be returned as a reference because this is the same object which is calling it. When
you are returning a matrix that is not a reference, it is a returned by value. The complete
matrix will be copied on the stack and returned. This is slightly wasteful. Yet you cannot
return a reference to the temp object in this code. The reference of the temp will be
returned but destroyed when the function is finished. The reference will be pointing to
nothing. So you have to be careful while returning a reference to this.
Transpose Function

The transpose of a matrix will interchange rows into columns. There are two alternative
requirements. In the first case, we have a square matrix i.e. the number of rows is equal to
Page 573
number of columns. In this situation, we don’t need extra storage to do this. If the number
of rows is not equal to the number of columns, then we have to deal it in a different way.
We can use general case for both purposes but you will notice that it is slightly
insufficient. Here is the code of the function.
const Matrix & Matrix::transpose()
{
if(numRows == numCols){ // Square matrix
double temp;
for(int i = 0;i < numRows; i++){
for(int j = i+1; j < numCols; j++){
temp = elements[i][j];
elements[i][j] = elements[j][i];
elements[j][i] = temp;
}
}
}
else // not a square matrix
{
Matrix temp(numCols, numRows);
for(int i = 0; i < numRows; i++){
for(int j = 0; j < numCols; j++){
temp.elements[j][i] = elements[i][j];
}
}
*this = temp;
}
return *this;
}
In the beginning, we checked the case of square matrix i.e. if the number of rows is equal
to number of columns. Here we are dealing with the square matrix. We have to change
the rows into columns. For this purpose, we need a temporary variable. In this case, it is a
variable of type double because we are talking about a double matrix. Look at the loop
conditions carefully. The outer loop runs for i = 0 to i < numRows and the inner loop
runs from j = i+1 to j < numCols. Then we have standard swap functionality. We have
processed one triangle of the matrix. If you start the inner loop from zero, think logically
what will happen. You will interchange a number again and again, but nothing will
happen in the end, leaving no change in the matrix. This is the case of the square matrix.
But in case of non-square matrix i.e. the code in the else part, we have to define a new
matrix. Its rows will be equal to the columns of the calling matrix and its columns will be
equal to the number of rows. So we have defined a new Matrix temp with the number of
rows and columns interchanged as compared to the calling matrix. Its code is
straightforward. We are doing the element to element copy. The difference is, in the loop
we are placing the x row, y col element of the calling matrix to y row, x col of the temp
matrix. It is an interchange of the rows and columns according to the definition of the
Page 574
transpose. When we have all the values copied in the temp. We do our little magic that is
*this = temp
. Which means whatever this points to, now assigned the values of the matrix
temp
. Now our horizontal matrix becomes vertical and vice versa. In the end, we return
this
. This is the basic essence of transpose code.
We will continue the discussion on the code in the next lecture. We will look at the
assignment operator, stream operator and try to recap the complete course.
Code of the Program

The complete code of the matrix class is:
#include <iostream.h>
#include <iomanip.h>
#include <stdlib.h>
#include <stdio.h>
#include <fstream.h>
class Matrix
{
private:
int numRows, numCols;
double **elements;
public:
Matrix(int=0, int=0); // default constructor
Matrix(const Matrix & ); // copy constructor
~Matrix(); // Destructor
int getRows(void) const; // Utility fn, returns no. of rows
int getCols(void) const; // Utility fn, returns no. of columns
const Matrix & input(istream &is = cin); // Read matrix from istream
const Matrix & input(ifstream &is); // Read matrix from istream
void output(ofstream &os) const; // Utility fn, prints matrix with graphics
void output(ostream &os = cout) const; // Utility fn, prints matrix with graphics
const Matrix& transpose(void); // Transpose the matrix and return a ref
const Matrix & operator = (const Matrix &m); // Assignment operator
Matrix operator+( Matrix &m) const; // Member op + for A+B; returns matrix
Matrix operator + (double d) const;
const Matrix & operator += (Matrix &m);
friend Matrix operator + (double d, Matrix &m);
Matrix operator-( Matrix & m) const; // Member op + for A+B; returns matrix
Matrix operator - (double d) const;
const Matrix & operator -= (Matrix &m);
Page 575
friend Matrix operator - (double d, Matrix& m);
Matrix operator*(const Matrix & m);
Matrix operator * (double d) const;
friend Matrix operator * (const double d, const Matrix& m);
Matrix operator/(const double d);
friend ostream & operator << ( ostream & , Matrix & );
friend istream & operator >> ( istream & , Matrix & );
friend ofstream & operator << ( ofstream & , Matrix & );
friend ifstream & operator >> ( ifstream & , Matrix & );
};
Matrix::Matrix(int row, int col) //default constructor
{
numRows = row;
numCols = col;
elements = new (double *) [numRows];
for (int i = 0; i < numRows; i++){
elements[i] = new double [ numCols];
for(int j = 0; j < numCols; j++)
elements[i][j] = 0; // Initialize to zero
}
}
Matrix::Matrix(const Matrix &m)
{
numRows = m.numRows;
numCols = m.numCols;
elements = new (double *) [numRows];
for (int i = 0; i < numRows; i++){
elements[i] = new double [ numCols];
for(int j = 0; j < numCols; j++)
elements[i][j] = m.elements[i][j];
}
}
Matrix::~Matrix(void)
{
delete [] elements;
}
int Matrix :: getRows ( ) const
{
return numRows;
Page 576
}
int Matrix :: getCols ( ) const
{
return numCols;
}
void Matrix::output(ostream &os) const
{
// Print first row with special characters
os.setf(ios::showpoint);
os.setf(ios::fixed,ios::floatfield);
os << (char) 218;
for(int j=0; j<numCols; j++)
os << setw(10) << " ";
os << (char) 191 << "\n";
// Print remaining rows with vertical bars only
for (int i=0; i<numRows; i++){
os << (char) 179;
for(int j=0; j<numCols; j++)
os << setw(10)<< setprecision(2) << elements[i][j];
os << (char) 179 << "\n";
}
// Print last row with special characters
os << (char) 192;
for(int j=0; j<numCols; j++)
os << setw(10) << " ";
os << (char) 217 << "\n";
}
void Matrix::output(ofstream &os) const
{
os.setf(ios::showpoint);
os.setf(ios::fixed,ios::floatfield);
os << numRows << " " << numCols << "\n";
for (int i=0; i<numRows; i++){
for(int j=0; j<numCols; j++)
os << setw(6) << setprecision(2) << elements[i][j];
os << "\n";
}
}
const Matrix & Matrix::input(istream &is)
{
cout << "Input Matrix size: " << numRows << " rows by " << numCols << "
Page 577
columns\n";
for(int i=0; i<numRows; i++){
cout << "Please enter " << numCols << " values separated by spaces for row
no." << i+1 << ": ";
for(int j=0; j<numCols; j++){
cin >> elements[i][j];
}
}
return *this;
}
const Matrix & Matrix::input(ifstream &is)
{
int Rows, Cols;
is >> Rows;
is >> Cols;
if(Rows>0 && Cols > 0){
Matrix temp(Rows, Cols);
*this = temp;
for(int i=0; i<numRows; i++){
for(int j=0; j<numCols; j++){
is >> elements[i][j];
}
}
}
return *this;
}
const Matrix & Matrix::transpose()
{
if(numRows == numCols){ // Square matrix
double temp;
for(int i=0; i<numRows; i++){
for(int j=i+1; j<numCols; j++){
temp = elements[i][j];
elements[i][j] = elements[j][i];
elements[j][i] = temp;
}
}
}
else
{
Matrix temp(numCols, numRows);
for(int i=0; i<numRows; i++){
for(int j=0; j<numCols; j++){
temp.elements[j][i] = elements[i][j];
Page 578
}
}
*this = temp;
}
return *this;
}
const Matrix & Matrix :: operator = ( const Matrix & m )
{
if( &m != this){
if (numRows != m.numRows || numCols != m.numCols){
delete [] elements;
elements = new (double *) [m.numRows];
for (int i = 0; i < m.numRows; i++)
elements[i]=new double[m.numCols ];
}
numRows = m.numRows;
numCols = m.numCols;
for ( int i=0; i<numRows; i++){
for(int j=0; j<numCols; j++){
elements[i][j] = m.elements[i][j];
}
}
}
return *this;
}
Matrix Matrix::operator + ( Matrix &m ) const
{
// Check for conformability
if(numRows == m.numRows && numCols == m.numCols){
Matrix temp(m);
for (int i = 0; i < numRows; i++){
for (int j = 0; j < numCols; j++){
temp.elements[i][j] += elements[i][j];
}
}
return temp ;
}
}
Matrix Matrix::operator + ( double d ) const
{
Matrix temp(*this);
for (int i = 0; i < numRows; i++){
for (int j = 0; j < numCols; j++){
Page 579
temp.elements[i][j] += d;
}
}
return temp ;
}
const Matrix & Matrix::operator += (Matrix &m)
{
*this = *this + m;
return *this;
}
Matrix Matrix::operator - ( Matrix &m ) const
{
// Check for conformability
if(numRows == m.numRows && numCols == m.numCols){
Matrix temp(*this);
for (int i = 0; i < numRows; i++){
for (int j = 0; j < numCols; j++){
temp.elements[i][j] -= m.elements[i][j];
}
}
return temp ;
}
}
Matrix Matrix::operator - ( double d ) const
{
Matrix temp(*this);
for (int i = 0; i < numRows; i++){
for (int j = 0; j < numCols; j++){
temp.elements[i][j] -= d;
}
}
return temp ;
}
const Matrix & Matrix::operator -= (Matrix &m)
{
*this = *this - m;
return *this;
}
Matrix Matrix::operator* ( const Matrix& m)
{
Matrix temp(numRows,m.numCols);
Page 580
if(numCols == m.numRows){
for ( int i = 0; i < numRows; i++){
for ( int j = 0; j < m.numCols; j++){
temp.elements[i][j] = 0.0;
for( int k = 0; k < numCols; k++){
temp.elements[i][j] += elements[i][k] * m.elements[k][j];
}
}
}
}
return temp;
}
Matrix Matrix :: operator * ( double d) const
{
Matrix temp(*this);
for ( int i = 0; i < numRows; i++){
for (int j = 0; j < numCols; j++){
temp.elements[i][j] *= d;
}
}
return temp;
}
Matrix operator * (const double d, const Matrix& m)
{
Matrix temp(m);
temp = temp * d;
return temp;
}
Matrix Matrix::operator / (const double d)
{
Matrix temp(*this);
for(int i=0; i< numRows; i++){
for(int j=0; j<numCols; j++){
temp.elements[i][j] /= d;
}
}
return temp;
}
Matrix operator + (double d, Matrix &m)
{
Matrix temp(m);
for(int i=0; i< temp.numRows; i++){
Page 581
for(int j=0; j<temp.numCols; j++){
temp.elements[i][j] *= d;
}
}
return temp;
}
Matrix operator - (double d, Matrix& m)
{
Matrix temp(m);
for(int i=0; i< temp.numRows; i++){
for(int j=0; j<temp.numCols; j++){
temp.elements[i][j] = d - temp.elements[i][j];
}
}
return temp;
}
ostream & operator << ( ostream & os, Matrix & m)
{
m.output();
return os;
}
istream & operator >> ( istream & is, Matrix & m)
{
m.input(is);
return is;
}
ofstream & operator << ( ofstream & os, Matrix & m)
{
m.output(os);
return os;
}
ifstream & operator >> ( ifstream & is, Matrix & m)
{
m.input(is);
return is;
}
int main()
{
// declaring two matrices
Matrix m(4,5), n(5,4);
Page 582
// getting input from keyboard
cout << "Taking the input for m(4,5) and n(5,4) \n";
m.input();
n.input();
// displaying m and taking its transpose
cout << "Displaying the matrix m(4,5) and n(5,4)\n";
m.output();
n.output();
cout << "Taking the transpose of matrix m(4,5) \n";
m.transpose();
cout << "Displaying the matrix m(5,4) and n(5,4) \n";
m.output();
cout << "Adding matrices n into m \n";
m = m + n;
m.output();
cout << "Calling m + m + 4 \n";
m = m + m + 4;
m.output();
cout << "Calling m += n \n";
m += n;
m.output();
cout << "Calling m = m - n \n";
m = m - n;
m.output();
cout << "Calling m = m - 4 \n";
m = m - 4;
m.output();
cout << "Calling m -= n \n";
m -= n;
m.output();
m.transpose();
Matrix c;
cout << "Calling c = m * n \n";
Page 583
c = m * n;
c.output();
cout << "Calling c = c * 4.0 \n";
c = c * 4.0;
c.output();
cout << "Calling c = 4.0 * c \n";
c = 4.0 * c ;
c.output();
cout << "Testing stream extraction \n";
// cin >> c;
cout << "Testing stream insertion \n";
// cout << c;
cout << "Writing into the file d:\\junk.txt \n" ;
ofstream fo("D:/junk.txt");
fo << c;
fo.close();
cout << "Reading from the file d:\\junk.txt \n";
ifstream fi("D:/junk.txt");
fi >> c;
fi.close();
cout << c;
system("PAUSE");
return 0;
}
The output of the program is:
Taking the input for m(4,5) and n(5,4)
Input Matrix size: 4 rows by 5 columns
Please enter 5 values separated by spaces for row no.1: 1.0 2.0 3.0 4.0 5.0
Please enter 5 values separated by spaces for row no.2: 7.0 5.5 2.3 2.0 1.0
Please enter 5 values separated by spaces for row no.3: 3.3 2.2 1.1 4.4 5.5
Please enter 5 values separated by spaces for row no.4: 9.9 5.7 4.3 2.3 1.5
Input Matrix size: 5 rows by 4 columns
Please enter 4 values separated by spaces for row no.1: 11.25 12.25 13.25 14.25
Please enter 4 values separated by spaces for row no.2: 25.25 50.50 75.75 25.50
Please enter 4 values separated by spaces for row no.3: 15.15 5.75 9.99 19.90
Page 584
Please enter 4 values separated by spaces for row no.4: 25.50 75.75 10.25 23.40
Please enter 4 values separated by spaces for row no.5: 50.50 75.50 25.25 15.33
Displaying the matrix m(4,5) and n(5,4)
┌ ┐
│ 1.00 2.00 3.00 4.00 5.00│
│ 7.00 5.50 2.30 2.00 1.00│
│ 3.30 2.20 1.10 4.40 5.50│
│ 9.90 5.70 4.30 2.30 1.50│
└ ┘
┌ ┐
│ 11.25 12.25 13.25 14.25│
│ 25.25 50.50 75.75 25.50│
│ 15.15 5.75 9.99 19.90 │
│ 25.50 75.75 10.25 23.40│
│ 50.50 75.50 25.25 15.33│
└ ┘
Taking the transpose of matrix m(4,5)
Displaying the matrix m(5,4) and n(5,4)
┌ ┐
│ 1.00 7.00 3.30 9.90│
│ 2.00 5.50 2.20 5.70│
│ 3.00 2.30 1.10 4.30│
│ 4.00 2.00 4.40 2.30│
│ 5.00 1.00 5.50 1.50│
└ ┘
┌ ┐
│ 12.25 19.25 16.55 24.15│
│ 27.25 56.00 77.95 31.20│
│ 18.15 8.05 11.09 24.20 │
│ 29.50 77.75 14.65 25.70│
│ 55.50 76.50 30.75 16.83│
└ ┘
Calling m + m + 4
┌ ┐
│ 28.50 42.50 37.10 52.30 │
│ 58.50 116.00 159.90 66.40│
│ 40.30 20.10 26.18 52.40 │
│ 63.00 159.50 33.30 55.40 │
│ 115.00 157.00 65.50 37.66│
└ ┘
Calling m += n
┌ ┐
│ 39.75 54.75 50.35 66.55 │
│ 83.75 166.50 235.65 91.90│
│ 55.45 25.85 36.17 72.30 │
Page 585
│ 88.50 235.25 43.55 78.80 │
│ 165.50 232.50 90.75 52.99│
└ ┘
Calling m = m - n
┌ ┐
│ 28.50 42.50 37.10 52.30 │
│ 58.50 116.00 159.90 66.40│
│ 40.30 20.10 26.18 52.40 │
│ 63.00 159.50 33.30 55.40 │
│ 115.00 157.00 65.50 37.66│
└ ┘
Calling m = m - 4
┌ ┐
│ 24.50 38.50 33.10 48.30 │
│ 54.50 112.00 155.90 62.40│
│ 36.30 16.10 22.18 48.40 │
│ 59.00 155.50 29.30 51.40 │
│ 111.00 153.00 61.50 33.66│
└ ┘
Calling m -= n
┌ ┐
│ 13.25 26.25 19.85 34.05 │
│ 29.25 61.50 80.15 36.90 │
│ 21.15 10.35 12.19 28.50 │
│ 33.50 79.75 19.05 28.00 │
│ 60.50 77.50 36.25 18.33 │
└ ┘
Calling c = m * n
┌ ┐
│ 5117.55 8866.42 4473.54 3066.94 │
│ 7952.36 15379.14 7884.15 5202.50 │
│ 4748.18 8540.74 7566.73 3570.75 │
│ 3386.23 5949.35 4280.89 2929.51 │
└ ┘
Calling c = c * 4.0
┌ ┐
│ 20470.19 35465.70 17894.15 12267.75 │
│ 31809.46 61516.55 31536.59 20810.01 │
│ 18992.71 34162.97 30266.91 14283.00 │
│ 13544.91 23797.41 17123.54 11718.05 │
└ ┘
Calling c = 4.0 * c
┌ ┐
│ 81880.76 141862.80 71576.62 49071.00 │
│ 127237.84 246066.20 126146.34 83240.04 │
│ 75970.86 136651.88 121067.65 57132.02 │
Page 586
│ 54179.64 95189.64 68494.16 46872.18 │
└ ┘
Testing stream extraction
Testing stream insertion
Writing into the file d:\junk.txt
┌ ┐
│ 81880.76 0.81 0.62 0.00 │
│ 127237.84 0.20 0.35 0.04 │
│ 75970.86 0.88 0.66 0.02 │
│ 54179.65 0.65 0.16 0.18 │
└ ┘
Press any key to continue . . .