Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
284
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Overloading C++ Operators

Here are the implementations for operator<< and operator>> for the SpreadsheetCell class:

ostream& operator<<(ostream& ostr, const SpreadsheetCell& cell)

{

ostr << cell.mString; return (ostr);

}

istream& operator>>(istream& istr, SpreadsheetCell& cell)

{

string temp; istr >> temp; cell.set(temp); return (istr);

}

The trickiest part of these functions is that, in order for mValue to be set correctly, operator>> must remember to call the set() method on the SpreadsheetCell instead of setting mString directly.

Overloading the Subscripting Operator

Pretend, for a few minutes, that you have never heard of the vector template class in the STL, and so you have decided to write your own dynamically allocated array class. This class would allow you to set and retrieve elements at specified indices, and would take care of all memory allocation “behind the scenes.” A first stab at the class definition for a dynamically allocated integer array might look like this:

class Array

{

public:

// Creates an array with a default size that will grow as needed. Array();

~Array();

//Returns the value at index x. If index x does not exist in the array,

//throws an exception of type out_of_range.

int getElementAt(int x) const;

//Sets the value at index x to val. If index x is out of range,

//allocates more space to make it in range.

void setElementAt(int x, int val);

protected:

static const int kAllocSize = 4; void resize(int newSize);

int* mElems; int mSize;

private:

// Disallow assignment and pass by value. Array(const Array& src);

Array& operator=(const Array& rhs);

};

443

Chapter 16

In order to present only the salient points, we have omitted exception throw lists and have not made this class a template. The interface supports setting and accessing elements. It provides random-access guarantees: a client could create an array and set elements 1, 100, and 1000 without worrying about memory management.

Here are the implementations of the methods:

#include “Array.h”

const int Array::kAllocSize;

Array::Array()

{

mSize = kAllocSize; mElems = new int[mSize];

}

Array::~Array()

{

delete [] mElems;

}

int Array::getElementAt(int x) const

{

if (x < 0 || x >=mSize) { throw out_of_range(“”);

}

return (mElems[x]);

}

void Array::setElementAt(int x, int val)

{

if (x < 0) {

throw out_of_range(“”);

}

if (x >= mSize) {

// Allocate kAllocSize past the element the client wants resize (x + kAllocSize);

}

mElems[x] = val;

}

void Array::resize(int newSize)

{

int* newElems = new int[newSize]; // Allocate the new array of the new size.

// The new size is always bigger than the old size. for (int i = 0; i < newSize; i++) {

// Copy the elements from the old array to the new one. newElems[i] = mElems[i];

}

mSize = newSize; // Store the new size.

delete [] mElems; // Free the memory for the old array. mElems = newElems; // Store the pointer to the new array.

}

444

Overloading C++ Operators

Here is a small example of how you could use this class:

Array arr; int i;

for (i = 0; i < 10; i++) { arr.setElementAt(i, 100);

}

for (i = 0; i < 10; i++) {

cout << arr.getElementAt(i) << “ “;

}

cout << endl;

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() functions. It would be nice to be able use real array index notation like this:

Array arr; int i;

for (i = 0; i < 10; i++) { arr[i] = 100;

}

for (i = 0; i < 10; i++) { cout << arr[i] << “ “;

}

cout << endl;

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();

~Array();

int& operator[](int x) protected:

static const int kAllocSize = 4; void resize(int newSize);

int* mElems; int mSize;

private:

// Disallow assignment and pass by value. Array(const Array& src);

Array& operator=(const Array& rhs);

};

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:

445

Chapter 16

int& Array::operator[](int 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]);

}

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.

class Array

{

public:

Array();

~Array();

int& operator[](int x);

const int& operator[](int x); // BUG! Can’t overload based on return type protected:

static const int kAllocSize = 4; void resize(int newSize);

int* mElems; int mSize;

private:

// Disallow assignment and pass by value. Array(const Array& src);

Array& operator=(const Array& rhs);

};

However, there is one small problem: you can’t overload a method or operator based only on return type. The above code doesn’t compile. Luckily, C++ provides a way around this restriction: if you mark the second operator[] 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 signatures:

int& operator[](int x);

const int& operator[](int x) const;

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.

446