Assignment 3: Matrix Template
Objectives
- to create a class template
- to manage dynamically allocated memory in objects using raw pointers
- to create proxy classes and iterators to manage access to other objects
Assignment
Create a class template calledMatrix
in the
cs427527
namespace so that instantiations
of that class represent 2-D matrices (rectangular arrays) in which each
element is of the same type. The template should have one parameter
to determine that type, which must be default-constructable,
copy-constructable, and assignable.
Instantiations of your Matrix
template must provide the
following operations (T
here represents the element type).
- A constructor
Matrix(int h, int w)
that creates a matrix of the given size. That size is then fixed for the life of the matrix. - A copy constructor
Matrix(const Matrix& other)
that creates a deep copy of the given matrix, assuming that the elements' copy constructor and assignment operator create deep copies of the elements. - A move constructor
Matrix(Matrix&& other)
that runs in O(1) time. - A copy assignment operator to create a deep copy of an object, assuming that the elements' copy constructor and assignment operator create deep copies of the elements.
- A move assignment operator that runs in O(1) time when T has a trivial (do-nothing) destructor (for example, when T is a primitive type).
- Methods
int height()
andint width()
to return the size in each dimension of a matrix. - A method
T& at(int r, int c)
that returns a reference to the element at the given location in the matrix, or throws astd::out_of_range
exception if the row index or column index are invalid. There must also be a version of this method that works onconst
matrices, returningconst
references to the elements.
const
where appropriate.
To support iterating through rows and columns of matrices, there must
be four nested types slice
, const_slice
,
iterator
and const_iterator
.
The slice types represent 1-D views of the matrix, where the elements
accessed through the view are the actual elements in the matrix, so
modifying an element through the view modifies
what is in the matrix. The iterator types are then movable views of
single elements in the slices.
The Matrix
class template must have the following
methods to create slices:
- an operator
slice operator[](int row)
that takes a row index and returns a slice representing that row, or throws astd::out_of_range
exception if the index is invalid; - a method
slice column(int col)
that takes a column index and returns a slice representing that column, or throws astd::out_of_range
exception if the index is invalid; and - versions of both of the above that work on
const
matrices and returnconst_slice
s.
The slice types must then have the following methods to access elements and create iterators:
- an operator
T& operator[](int i)
that takes an index into the slice and returns a reference to the element at that index, or throws astd::out_of_range
exception if the index is invalid; - a method
iterator begin()
to return an iterator positioned at the beginning of the slice; - a method
iterator end()
to return an iterator positioned past the last element in the slice; - versions of the above that work on
const
slices (forconst_slice
s, these versions may be be only ones provided) with the appropriate return types so that the underlying matrix cannot be modified through aconst
slice (which is not the same thing as aconst_slice
); and - whatever is necessary to make the slices copy-constructable.
Finally, the iterator types must have the following methods
- an operator
T& operator*()
to return the element the iterator is currently positioned at; - a version of the above that works on
const
iterators so that the underlying matrix cannot be modified through aconst iterator
(nor through aconst iterator
, which is not the same thing); - an operator
iterator& operator++()
to advance an iterator and return a reference to it, provided that it is not already positioned past the last element in the slice; - operators
bool operator==(const iterator& rhs)
andbool operator!=(const iterator& rhs)
to compare two iterators to check whether they are positioned at the same element in the same matrix or both positioned past the last element in the same slice (both iterators must be the same type – bothiterator
s or bothconst_iterator
s – but see below for graduate credit requirements); - whatever is necessary to make iterators copy-constructable and copy-assignable.
Note that the behavior of const slice
s
and const iterator
s is intentionally different than the
corresponding behavior in the STL – STL const iterator
s
may still allow modification of the underlying container (but a
const iterator
cannot be moved).
Graduate Credit
The comparison operators for iterators must allow for comparisons betweeniterator
s and const_iterator
s and vice versa.
Other Requirements and Non-requirements
- Users of your template should only have to include
matrix.hpp
. - You must use a dynamically allocated array and raw pointers to hold the elements in the matrices. So no smart pointers, STL containers, or anything else that defeats the objectives of the assignment.
- Proper use of your class template should not result in memory leaks, uninitialized reads, invalid reads, or other memory errors reported by valgrind.
- Aside from the required exceptions, you are not required to prevent users from misuing your slices and iterators. In particular, if a slice or iterator outlives the underlying matrix, it is fine for operations on those objects to crash (which matches the typical behavior of the STL containers).
- The slice objects themselves are not required to be assignable.
Unit Tests
There is also file matrix_unit.cpp
in
/c/cs427/hw3/Required
that contains a
main
function that runs unit tests on your Matrix
template. Your makefile should build an executable called Unit
from that file.
Your makefile must assume that
the matrix_unit.cpp
file is in the current directory; do not
hard-code the directory containing that file. There will be private
unit tests added to matrix_unit.cpp
for grading.
Example
The following is the output when run with the given tests.[jrg94@monkey Matrix]$ for N in `seq 0 15`; do ./Unit $N; done 2x2 IDENTITY MATRIX 1 0 0 1 2x2 IDENTITY MATRIX 1 0 0 1 ORIGINAL 99 0 0 0 0 98 0 0 0 0 0 0 COPY 99 0 0 0 0 98 0 0 0 0 0 0 ORIGINAL 0 0 0 0 0 0 0 0 0 0 0 0 MODIFIED COPY 99 0 0 0 0 98 0 0 0 0 0 0 RESET COPY TO ORIGINAL 0 0 0 0 0 0 0 0 0 0 0 0 3x3 MULTIPLICATION TABLE 0 0 0 0 1 2 0 2 4 middle row, last column: 33 PRINT USING SLICES 99 0 0 0 0 98 2 3 0 2 4 6 PRINT TRANSPOSED USING COLUMN SLICES 99 0 0 0 98 2 0 2 4 0 3 6 ORIGINAL WITH ITERATORS 99 0 0 0 0 98 2 3 0 2 4 6 COPY AFTER MODIFYING LAST COLUMN 99 0 0 42 0 98 2 42 0 2 4 42 SUM OF ORIGINAL BOTTOM ROW 12 SUM OF BOTTOM ROW OF CONST REFERENCE 12 SUM OF ENTIRE ORIGINAL 18 ITERATOR COMPARISONS begin == begin: 1 iterator == const iterator: 1 same position, different matrices: 0 same position through row and column: 1 FINAL is defined
Submissions
Submit whatever source code (.cpp and .hpp) files you created and a makefile that builds theUnit
executable when
make is run with no arguments or with target Unit
.