Professional C__ - Marc Gregoire [320]
Here is a small example of how you could use this class:
Array myArray;
for (size_t i = 0; i < 10; i++) {
myArray.setElementAt(i, 100);
}
for (size_t i = 0; i < 10; i++) {
cout << myArray.getElementAt(i) << " ";
}
Code snippet from SubscriptOperator\ArrayTest.cpp
As you can see, you never have to tell the array how much space you need. It allocates as much space as it requires to store the elements you give it. However, it’s inconvenient to use the setElementAt() and getElementAt() methods. It would be nice to be able to use conventional array index notation like this:
Array myArray;
for (size_t i = 0; i < 10; i++) {
myArray[i] = 100;
}
for (size_t i = 0; i < 10; i++) {
cout << myArray[i] << " ";
}
Code snippet from SubscriptOperator\ArrayTest.cpp
This is where the overloaded subscripting operator comes in. You can replace getElementAt() and setElementAt() in your class with an operator[] like this:
class Array
{
public:
Array();
virtual ~Array();
int& operator[](size_t x)
// Omitted for brevity
};
Code snippet from SubscriptOperator\Array.h
The preceding code using array index notation on the array now compiles. The operator[] can replace both setElementAt() and getElementAt() because it returns a reference to the element at location x. This reference can be an lvalue, so it can be used to assign to that element. Here is the implementation of the operator:
int& Array::operator[](size_t x)
{
if (x < 0) {
throw out_of_range("");
}
if (x >= mSize) {
// Allocate kAllocSize past the element the client wants.
resize(x + kAllocSize);
}
return mElems[x];
}
Code snippet from SubscriptOperator\Array.cpp
When operator[] is used on the left-hand side of an assignment statement, the assignment actually changes the value at location x in the mElems array.
Providing Read-Only Access with operator[]
Although it’s sometimes convenient for operator[] to return an element that can serve as an lvalue, you don’t always want that behavior. It would be nice to be able to provide read-only access to the elements of the array as well, by returning a const value or const reference. Ideally, you would provide two operator[]s: One returns a reference and one returns a const reference. You might try to do this as follows:
int& operator[](size_t x);
const int& operator[](size_t x); // BUG! Can't overload based on return type
Code snippet from SubscriptOperator\Array.h
However, there is one small problem: You can’t overload a method or operator based only on the return type. Thus, the preceding code doesn’t compile. C++ provides a way around this restriction: If you mark the second operator[] with the attribute const, then the compiler can distinguish between the two. If you call operator[] on a const object, it will use the const operator[], and, if you call it on a non-const object, it will use the non-const operator[]. Here are the two operators with the correct prototypes:
int& operator[](size_t x);
const int& operator[](size_t x) const;
Code snippet from SubscriptOperator\Array.h
Here is the implementation of the const operator[]. It throws an exception if the index is out of range instead of trying to allocate new space. It doesn’t make sense to allocate new space when you’re only trying to read the element value:
const int& Array::operator[](size_t x) const
{
if (x < 0 || x >= mSize) {
throw out_of_range("");
}
return mElems[x];
}
Code snippet from SubscriptOperator\Array.cpp
The following code demonstrates these two forms of operator[]:
void printArray(const Array& arr, size_t size);
int main()
{
Array myArray;
for (size_t i = 0; i < 10; i++) {
myArray[i] = 100; // Calls the non-const operator[] because
// myArray is a non-const object.
}
printArray(myArray, 10);
return 0;
}
void printArray(const Array& arr, size_t size)
{
for (size_t i = 0; i < size; i++) {
cout << arr[i] << " "; // Calls the const operator[] because arr is
// a const object.
}
cout << endl;
}
Code snippet from SubscriptOperator\ArrayTest.cpp
Note that the const operator[] is called