Литмир - Электронная Библиотека
A
A

Если ваши проблемы слишком велики, чтобы решить их с помощью таких простых средств, освойте профессиональные средства отладки; они называются детекторами утечек (leak detectors). В идеале, разумеется, следует не устранять утечки, а программировать так, чтобы они вообще не возникали. 

18.4. Доступ к элементам вектора

До сих пор (см. раздел 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.size(); ++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.size(); ++i) { // работает!

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

 cout << v[i];

}

Мы обеспечили традиционные обозначения: выражение

v[i]
интерпретируется как вызов оператора
v.operator[](i)
и возвращает ссылку на элемент вектора
v
с номером
i
.

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

Функция

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; // для константных векторов

};

244
{"b":"847443","o":1}