Читаем Программирование полностью

До сих пор (см. раздел 17.6) для доступа к элементам вектора мы использовали функции-члены set() и get(). Но этот способ слишком громоздок и некрасив. Мы хотим использовать обычную индексацию: v[i]. Для этого следует определить функцию-член с именем operator[]. Вот ее первая (наивная) версия.

class vector {

  int sz;         // размер

  double* elem;   // указатель на элементы

public:

  // ...

  double operator[](int n) { return elem[n]; } // возвращаем

                                               // элемент

};

Все выглядит хорошо и просто, но, к сожалению, слишком просто. Разрешив оператору индексирования (operator[]()) возвращать значение, мы разрешили чтение, но не запись элементов.

vector v(10);

int x = v[2]; // хорошо

v[3] = x;     // ошибка: v[3] не может стоять в левой

              // части оператора =

Здесь выражение v[i] интерпретируется как вызов оператора v.operator[](i), который возвращает значение элемента вектора v с номером i. Для такого слишком наивного варианта класса vector значение v[3] является числом с плавающей точкой, а не переменной, содержащей число с плавающей точкой.

ПОПРОБУЙТЕ

Создайте вариант класса vector, скомпилируйте его и посмотрите на сообщение об ошибке, которое ваш компилятор выдаст для инструкции v[3]=x;.

В следующей версии мы разрешим оператору operator[] возвращать указатель на соответствующий элемент:

class vector {

  int sz;        // размер

  double* elem;  // указатель на элемент

public:

  // ...

  double* operator[](int n) { return &elem[n]; } // возвращаем

                                                 // указатель

};

При таком определении мы можем записывать элементы.

vector v(10);

for (int i=0; i

                                 // некрасиво

  *v[i] = i;

cout << *v[i];

}

Здесь выражение v[i] интерпретируется как вызов оператора v.operator[](i) и возвращает указатель на элемент вектора v с номером i. Проблема в том, что теперь мы должны написать оператор *, чтобы разыменовать указатель, ссылающийся на этот элемент. Это так же некрасиво, как и функции set() и get(). Проблему можно устранить, если вернуть из оператора индексирования ссылку.

class vector {

  // ...

  double& operator[ ](int n) { return elem[n]; } // возвращаем

                                                 // ссылку

};

Теперь можем написать следующий вариант.

vector v(10);

for (int i=0; i

  v[i] = i;        // v[i] возвращает ссылку на элемент с номером i

 cout << v[i];

}

Мы обеспечили традиционные обозначения: выражение v[i] интерпретируется как вызов оператора v.operator[](i) и возвращает ссылку на элемент вектора v с номером i.

<p>18.4.1. Перегрузка ключевого слова const</p>

Функция operator[](), определенная выше, имеет один недостаток: ее нельзя вызвать для константного вектора. Рассмотрим пример.

void f(const vector& cv)

{

  double d = cv[1]; // неожиданная ошибка

  cv[1] = 2.0;      // ожидаемая ошибка

}

Причина заключается в том, что наша функция vector::operator[]() потенциально может изменять объект класса vector. На самом деле она этого не делает, но компилятор об этом не знает, потому что мы забыли сообщить ему об этом. Для того чтобы решить эту проблему, необходимо предусмотреть функцию-член со спецификатором const (см раздел 9.7.4). Это легко сделать.

class vector {

  // ...

  double& operator[](int n);      // для неконстантных векторов

  double operator[](int n) const; // для константных векторов

};

Очевидно, что мы не могли бы вернуть ссылку типа double& из версии со спецификатором const, поэтому возвращаем значение типа double. С таким же успехом мы могли бы вернуть ссылку типа const double &, но, поскольку объект типа double невелик, не имеет смысла возвращать ссылку (см. раздел 8.5.6), и мы решили вернуть значение. Теперь можно написать следующий код:

void ff(const vector& cv, vector& v)

{

  double d = cv[1]; // отлично (использует константный вариант [ ])

  cv[1] = 2.0;      // ошибка (использует константный вариант [ ])

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже