|
|||||
CS201
Introduction to Programming
Lecture
Handout
Introduction
to Programming
Lecture
No. 44
Reading
Material
Lecture
25 - Lecture 43
Summary
·
Matrix
Class
·
Definition
of Matrix Constructor
·
Destructor of
Matrix Class
·
Utility
Functions of Matrix
·
Input
Function
·
Transpose
Function
·
Code of
the Program
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
561
CS201
Introduction to Programming
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
variables-
numRows
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 two-
dimension
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
562
CS201
Introduction to Programming
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
563
CS201
Introduction to Programming
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
564
CS201
Introduction to Programming
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
565
CS201
Introduction to Programming
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
Let's
start with the default
constructor. Its prototype is as
under:
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
566
CS201
Introduction to Programming
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
567
CS201
Introduction to Programming
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
568
CS201
Introduction to Programming
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
569
CS201
Introduction to Programming
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
570
CS201
Introduction to Programming
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
571
CS201
Introduction to Programming
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
572
CS201
Introduction to Programming
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
573
CS201
Introduction to Programming
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
574
CS201
Introduction to Programming
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
575
CS201
Introduction to Programming
}
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
576
CS201
Introduction to Programming
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
577
CS201
Introduction to Programming
}
}
*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
578
CS201
Introduction to Programming
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
579
CS201
Introduction to Programming
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
580
CS201
Introduction to Programming
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
581
CS201
Introduction to Programming
// 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
582
CS201
Introduction to Programming
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
583
CS201
Introduction to Programming
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│
└
┘
Adding
matrices n into m
┌
┐
│ 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
584
CS201
Introduction to Programming
│ 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
585
CS201
Introduction to Programming
│ 54179.64
95189.64 68494.16
46872.18
│
└
┘
Testing
stream extraction
Testing
stream insertion
Writing
into the file
d:\junk.txt
Reading
from 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 . .
.
Page
586
Table of Contents:
|
|||||