Many people probably use brackets ([]) in their code without realizing that this is an operator. Mostly, it is seen as a way to get hold of one element in an array.
In fact, it is an operator, although the notation is a bit unlike that of most operators. It has two arguments: first, a pointer to the first element in the array, and second, an integer which is the subscript.
If we have a class which is a collection of elements of some kind, kept in order, then it is reasonable to ask for the first element (subscript = 0), the second element (subscript = 1), etc. Thus we may want to define the brackets operator for the class. This does not actually have to be an array; it could be, for instance, a linked list.
Unlike some other operators, the brackets operator does not do any calculations for us; it just gives us back a variable. We actually use it in two different ways (as we do with any variable). Suppose we have an array named A.
B = A[N];
Here we are not trying to change the value of A[N], and all we need is its value. We are treating the array as a constant.
A[N] = B;
Here we want to change the value of A[N], so we need a reference to it. We are not treating the array as a constant.
Of course, we can use A[N] in lots of other ways, but in each case we need either the value of A[N] or a reference to it.
To distinguish between these two cases, we normally define the brackets operator twice for a class:
Remember that some methods are declared as constant, and a constnt method cannot make use of a non-constant method. The compiler pays attention to whether it needs a constant method or a non-constant method at any given moment, so it will make use of both of these.
Example of the brackets operator
Suppose we have the following class:
class IntList { private: int * A; //Points to the array's 1st element int Size; //Size of the array public: IntList(); //basic constructor ~IntList(); //destructor ... various methods ... };
This is a dynamically-allocated array of ints. We would like to be able to declare
IntList Q;
and then refer to Q[0], Q[1], etc., so we need to redefine the brackets operator. (Notice that we can't just refer to A[0], A[1], etc., because A is private.) We will make them methods of the class.
The prototype statements will look like this:
int operator [](const int &) const; int & operator [](const int %);
The code for the two is much alike:
int IntList::operator [](const int & N) const { return A[N]; } int & IntList::operator [](const int & N) { return A[N]; }
With the first version, we are returning a copy of A[N]; this is suitable for statements such as:
D = Q[3];
or, in general, for uses that do not want to change the value of Q[3], which would include constant methods.
With the second version, we are returning a reference to A[N]; this is suitable for statements such
Q[3] = 17;
or, in general, for uses that might want to change the value of Q[3] (which would include non-constant methods).
Comments
Bear in mind that the class we are using here does not actually have to be an array. It could be a linked list, or possibly some other kind of structure. The person using the class and the brackets operator does not necessarily know anything about the internal design of the class; he simply writes Q[3] and gets back the fourth element in order.
Some people do not like to have subscript values start at 0. We could arrange to use some other starting point such as 1.
For that matter, the subscript for an array does not have to be an int. We could have an enumerated data type with values such as {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}, and we could use it as the set of subscript values. (Go read about the key word enum.) In that case, we could write something like
cout << X[Tuesday] << endl;
and if we redefined the brackets operator properly, it should make sense. (There are programming languages which have built-in support for this kind of thing.)