|
Subscript Operator []
The subscript operator [] in C++ is normally used to reference a specific item in a group of items.
Since the operator doesn't logically map to any operations of our complex class, we will define another
class to illustrate how to overload the subscript operator.
class IntArray {
private:
int *ptr;
public:
IntArray(size_t size) { ptr = new int[size]; }
~IntArray() { delete [] ptr; }
};
int main(void) {
IntArray iArray(10); // Declare a 10 element array
return 0;
}
Now we can overload the [] operator to reference elements within the IntArray object. Hence, we declare
a member function within the class:
class IntArray {
private:
int *ptr;
public:
IntArray(size_t size) { ptr = new int[size]; }
~IntArray() { delete [] ptr; }
int& operator[](int index);
};
If you are wondering why this function returns a reference, note that there are two common ways to use
the subscript operator:
cout << iArray[0]; // This is a read operation.
iArray[0] = 2; // This is a write operation.
As you can see, the second line above changes the value of the object and thus a reference should be
returned for this scenario to work. We don't always need to return a reference though, as we shall see
later on in this section. For now, we can implement the member function as follows:
int& IntArray::operator[](int index){
return ptr[index];
}
We can now test out this code:
int main(void) {
IntArray iArray(10); // Declare a 10 element array
// Write Operations
iArray[0] = 5;
iArray[1] = 3;
// Read Operations
cout << iArray[0] << endl;
cout << iArray[1] << endl;
return 0;
}
Running this produces the expected output.
The subscript operator overload takes only one argument, but it can be of any type. For instance, you could do this:
const int SomeClass::operator[](const std::string& sArg)
and get it to lookup a table for an entry that contains sArg in it. In the next section, we will study another operator
that can handle multiple arguments of different types.
In certain situations, we don't necessarily want to return a reference. For instance, there may be a need
to make the object read-only (i.e.) you can only read a value, but not assign to it. In this case, we can
overload the subscript operator a little differently:
class IntArray {
private:
int *ptr;
public:
IntArray(size_t size) {
ptr = new int[size];
for (size_t i=0; i < size; i++)
ptr[i] = i;
}
~IntArray() { delete [] ptr; }
const int operator[](int index);
};
const int IntArray::operator[](int index) {
return ptr[index];
}
int main(void) {
IntArray iArray(10);
cout << iArray[0] << endl;
cout << iArray[1] << endl;
// iArray[0] = 5; // Causes a compile error.
return 0;
}
Note that the body of the function is identical, but since we declared our subscript operator to return a value
instead of a reference, any attempt to assign a value to it will cause a compile error.
Exercises
1. Our overloaded subscript operators do not check if the index requested is outside the array bounds or not. To fix this,
add a member variable and note down the size of the array in the constructor. Then, in the overloaded functions,
make sure that index is less than the array size. Throw an exception if this is not the case. Another variation of this
could resize the array instead of throwing an exception.
2. The default copy constructor generated by the compiler has a problem in it. It copies the pointer directly from
one instance of an object to another, instead of making a copy of the data. This causes a problem because now there are two
pointers pointing to the same memory and when one is destroyed, the other will point to freed memory. The solution
is to write your own copy constructor that makes a copy of the data properly. Write a copy constructor
to handle the data appropriately (or if you don't feel like doing this, be aware that a problem exists and don't use
the above code in production!)
|
|