11.7. operator[ ]

Review

Understanding and using the index operator rests on concepts and terminology introduced previously. Please review the following as needed:

Perhaps the most surprising aspect of overloading the index operator is that it is an operator in the first place. It is easiest to illustrate the basic index operator and its use with a simple example.

char message[100]; char c = message[10]; message[10] = 'X';
(a)(b)(c)
The fundamental or built-in index operator: (b) and (c).
  1. Defines a one-dimensional character array named message with a size of 100
  2. Accesses element 10 of the message array; message[10] is an r-value
  3. Accesses element 10 of the message array; message[10] is an l-value
In examples (b) and (c), message is the name of an array; an array name is the address of the array. The index operator, [ ], takes one operand, 10 in this example, and operates on the address denoted by the array name: When evaluated, the expression is the address of the 11th element, counting from 0, in the array. When the program uses the address to get the value stored at that memory location, the address is an r-value. But if the program uses the address to store a new value at that memory location, the address is an l-value.

So, the square brackets, [ and ], form a unary operator that operates on a memory address! Furthermore, it's an operator that may be overloaded. It's only possible to overload an operator in the context of a new class that we are creating. So, overloading operator[] makes the most sense in cases where our new class has some array structure. The following example assumes that we are creating a new class named Pstring.

class Pstring
{
    private:
	char array[128];

    public:
		. . .
	char& operator[](int index);
};
char& Pstring::operator[](int index)
{
    if (index > 0 && index <= array[0])
        return array[index];
    else
        throw "Index out of bounds exception";
}
 
 
(a)(b)
Overloading operator[ ]. We initially created the Pstring example, which implements Pascal-like strings, in the Classes and Objects chapter. It is relatively easy to add an overloaded index operator to the example.
  1. A partial Pstring class: array is the name of an array of characters storing the string's characters. The first byte or character of array (that is, array[0]) is reserved to store the string's length.
  2. The overloaded [ ] operator: if the index values are within bounds, then a reference to the indexed element is returned. If the index is out of bounds, the function throws an exception. Take note that the function return must be by reference as this is what allows the operator to form an l-value.
Pstring::Pstring(const char* s)
{
		. . .
	array[i + 1] = s[i];
		. . .
}
Pstring	message;

char x = message[5];
message[5] = 'A';


(a)(b)
Fundamental vs. overloaded index operators. The symbols that make up the fundamental index operator, [ and ], are the same symbols that form overloaded index operators, and it's important that we are able to distinguish between them.
  1. The Pstring functions rely heavily on the fundamental index operator, which is illustrated by the Pstring conversion constructor.
  2. Once the Pstring class overloads the index operator, any program using Pstrings can use it.
Programs using the Pstring class now have two different versions of the index operator: the original or fundamental version and the programmer-created overloaded version. We can tell which operator the brackets represent by looking at the object to the operator's left. C-string s and the the character array array are indexed with the fundamental index operator (coral). Pstring message is indexed with the overloaded Pstring index operator (light blue).