1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
Consider a standard two-dimensional array. It has tt(nRows) rows and tt(nCols)
columns. Such an array is a generalization of a one-dimensional array: each
row consists of an array of tt(nCols) elements. If a type tt(DoubleArray) is
available, defined like the tt(IntArray) of the previous section, but
containing tt(double) values instead of tt(int) values, then designing a class
tt(Matrix) could start as follows:
verb(
class Matrix
{
size_t d_nRows;
size_t d_nCols;
DoubleArray *d_row;
public:
Matrix(size_t nRows, size_t nCols);
...
};)
and its constructor allocates tt(nRows DoubleArrays), each having
tt(nCols) columns, with each tt(DoubleArray) being initialized to a tt(nCols)
elements, initialized to 0 by the tt(DoubleArray's) default constructor:
verb(
Matrix::Matrix(size_t nRows, size_t nCols)
:
d_nRows(nRows),
d_nCols(nCols),
d_row(new DoubleArray[nRows])
{})
Traditionally accessing elements of a tt(Matrix) could be realized in three
ways (plus optionally corresponding tt(const) variants):
itemization(
it() A member tt(double &element(size_t row, size_t col)), returning the
value of element tt([row, col]);
it() A member tt(DoubleArray &row(size_t row)), returning tt(row's
DoubleArray);
it() A member tt(DoubleArray &operator[](size_t row)), also returning
tt(row's DoubleArray);
)
The members tt(element) and tt(row) work fine, but at the disadvantage that
the standard syntax for referring to matrix elements is not used. E.g, to
access element tt(matrix[3, 4]) we would write tt(matrix.element(3, 4)) or
tt(matrix.row(3)[4]), whereas the third element requires us to use two index
operators: tt(matrix[3][4]). Moreover, with the second and third members data
hiding of tt(DoubleArray row) is abandoned if the only reason for these
members is to access tt(Matrix) elements.
hi(index operator: multiple arguments)ti(operator[])
The overloaded index operator, however, can also be defined having multiple
arguments, allowing the use of the (standard mathematical) syntax
tt(matrix[row, col]) (and in general: the overloaded index operator can also
have more arguments, which might come in useful when defining, e.g., arrays of
arrays).
Providing tt(Matrix) with an index operator accepting two arguments is simple:
just add a member tt(double &operator[](size_t row, size_t col)) (and
optionally a comparable tt(const) member) to the class's interface. Its
implementation may then directly return the requested array element:
verb(
double &Matrix::operator[](size_t row, size_t col)
{
return d_row[row][col];
})
As an aside: note that this implementation does not check whether the provided
indices are valid. Traditionally index operators don't perform such checks,
improving the efficiency of programs when it's em(known) that indices
cannot be invalid. E.g., to initialize all elements of a tt(Matrix) with
subsequent integral values the following function could be used:
verb(
void init(Matrix &matrix, size_t value)
{
for (size_t row = 0; row != matrix.nRows(); ++row)
for (size_t col = 0; col != matrix.nCols(); ++col)
matrix[row, col] = value++;
})
hi(at: multiple arguments)
If, on the other hand, checking the validity of the indices em(is) necessary
then a multi-argument tt(at) member like the following can be defined:
verb(
double &Matrix::at(size_t row, size_t col)
{
if (row >= d_nrows or col >= d_nCols)
throw runtime_error("Matrix::at: invalid indices");
return d_row[row][col];
})
|