One of the reasons that user implementations of arrays are not aesthetically pleasing is that there is no way to implement the subscript operator. Most operators including new, delete, ->, the subscript operator [], the comparison operators (!= and ==), assignment operator (=, +=, etc.), and the arithmetic can be overloaded for a new c++ class. While you will seldom want to overload new or delete, you may frequently want to define arithmetic operators for a class.
The most important operator overload for our array class is the subscript operator. When
we overload this operator we encounter another problem not found in most other languages.
We would like to write
myArray[i] = 10;
If we think of an operator as a function with a special type of syntax (which is what it is), how
can we have a value assigned to a function call? Normally functions return values (referred to as
right-hand sides (rhs) because like the right hand side of an assignment they return a value in
the stack. Here the value of the function is on the left-hand side (lhs) of the assignment, so the
function must return a lhs value - that is an alias of an address.
We also want to be able to write
x = myArray[i];
Here the value returned is used on the right-hand side, but if we are returning a lhs, the compiler
generates code to retrieve the value. This is the same type of code generated for the assignment
y = x;
The code for the version of [ ] that returns a lhs follows.
template <class Type>
Type& Array < Type > ::operator [] (int i)
{
return sub(i);
}
template <class Type>
Type& Array < Type >::sub(int i)
{
static Type errorDump;
if (i < 0)
{
cerr <<"The subscript " << i<< " is out of range\n";
return errorDump;
}
else if ((i >= data->_size)&&(data->sizeChange>0))
reSize(i);
else if (i>=data->_size)
{
cerr<<"Subscript "<<(i)<<" This array cannot be resized beyond "<<(data->_size-1)<<"\n";
if(i>=data->_size)
throw "This array cannot be resized";
return errorDump;
}
return data->rep[i];
}
Class functions which are declared to be const guarantee that they will not directly or indirectly alter the object calling them. If a const function calls on another function, and that function is not const, then the compiler will issue a warning. It is very dangerous to ignor warnings, so we want to remove the cause of any warnings.
In the case of the call to a non const function we have three choices.
In the case of an array, users frequently want to retrieve information from
an array without altering that array. If the array is passed to a function as
a const parameter, then access to items in the array must be through a
const version of []. What differences are needed in this version of []?
We will not need to return a reference since we are retrieving values. So we might
get away with the following definition:
template <class Type>
Type Array < Type >::operator[](int i)const
{
return sub(i);
}
The problem with this is that sub is not a const function, so we get a warning
from the compiler. To avoid this we declare a second const version of sub.
template < class Type> Type ArrayNotice because this is a const version of the function that no resizing is allowed.::sub(int i)const { Type errorDump; if(i>=0 && i< data->_size) return data->rep[i]; else { cerr << "Subscript " << i << " out of range\n"; return errorDump; } }
C++ allows the user to extend the types of numerical classes. There is no type rational built into c++, but it is often handy to have such a type. Click here to see a complete discusion of the construction of such a class.