115
Foo* foo = array[Index(17, 29)];
// Работает
С чтением массива проблем нет. Если индекс существует, возвращается содержимое массива по
данному индексу. Если индекс в массиве отсутствует, значение
NULL
полностью соответствует идее
предварительной инициализации массива значениями
NULL
. Минутку, но как добавить в массив новую
ячейку или изменить уже существующую? Значение, возвращаемое операторной функцией
operator[]
, не является ни левосторонним выражением (lvalue), ни классом с перегруженным
оператором
=
и по нему нельзя выполнить присваивание.
array[Index(31, 37)] = foo; // Не работает
Ваш компилятор не спит ночами и ждет, когда же у него появится такая замечательная возможность
забить поток
сеrr
сообщениями об ошибках. Можно было бы создать интерфейс на базе функций, но
тогда у клиента нарушится иллюзия того, что он имеет дело с нормальным, честным массивом.
Существует ли способ использовать оператор
[]
в левой части операции присваивания для индексов,
которых еще нет? Оказывается, существует, но для этой цели нам потребуется новая идиома — курсор.
Курсоры и разреженные массивы
Итак, вторая попытка. Наша основная цель — чтобы операторная функция
operator[]
возвращала
нечто, обладающее следующими свойствами:
1. Оно должно преобразовываться к типу содержимого массива.
2. Оно может использоваться в левой части операции присваивания для изменения содержимого
соответствующей ячейки.
Это «нечто» представляет собой особый класс, который называется курсором (cursor). Ниже показан
уже знакомый разреженный массив с курсором в операторной функции
operator[]
:
class ArrayCursor;
class SparseArray {
friend class ArrayCursor;
private:
struct Node {
Index
index;
Foo*
content;
Node*
next;
Node(Index i, Foo* c, Node* n) : index(i), content(c), next(n) {};
};
Node*
cells;
public:
SparseArray() : cells(NULL) {}
ArrayCursor operator[](Index i);
};
class ArrayCursor {
friend class SparseArray;
private:
SparseArray&
array;
//
Обратный указатель на массив-владелец
Index
index;
//
Элемент, представленный курсором
SparseArray::Node*
node;
//
Если существует индекс, отличный от NULL
//
Конструкторы объявлены закрытыми, поэтому пользоваться ими
//
может только SparseArray. Первый конструктор используется, когда
//
индекс еще не существует, а второй – когда индекс уже присутствует
//
в массиве.
116
ArrayCursor(SparseArray& arr, Index i)
: array(arr), index(i), node(NULL) {}
ArrayCursor(SparseArray& arr, SparseArray::Node* n)
: array(arr), node(n), index(n->index) {}
public:
//
Следующий оператор = позволяет преобразовать присваивание курсору в
//
присваивание соответствующему элементу массива.
ArrayCursor& operator=(Foo* foo);
};
ArrayCursor& ArrayCursor::operator=(Foo* foo) {
if (node == NULL) {
// Индекс не существует
node = new SparseArray::Node(index, foo, array.cells);
array.cells = node;
}
else
//
Индекс уже существует, изменить значение элемента
node->content
=
foo;
return
*this;
}
ArrayCursor SparseArray::operator[](Index i)
{
SparseArray::Node* n = cells;
while (n != NULL)
if (n->index = i)
return
ArrayCursor(*this,
n); //
Существует
else
n
=
n->next;
return
ArrayCursor(*this,
i);
//
Еще не существует
}
Ого! Что же происходит в этом хитроумном коде? Все волшебство заключено в двух операторных
функциях,
SparseArray::operator[]()
и
ArrayCursor::operator=()
.
SparseArray::
operator[]()
возвращает
ArrayCursor
независимо от того, существует индекс или нет (об этом
ArrayCursor
узнает по тому, какой конструктор был выбран).
ArrayCursor::operator=(Foo*)
делает одно из двух: если индекс уже существует, элемент изменяется, а если не существует — он
динамически добавляется в массив. В этом проявляется вся суть курсорности (курсоризма?):
перегруженный оператор
=
выполняет присваивание не для самого курсора, а для структуры данных,
от которой происходит курсор. Теперь присваивание работает независимо от того, существует индекс
или нет.
array[Index(17, 29)] = new Foo;
// Добавляет индекс
array[Index(17, 29)] = new Foo;
// Изменяет значение с заданным индексом
Неплохо для часовой работенки, не правда ли? Наш массив работает совсем как настоящий. Почти.
Операторы преобразования и оператор ->
Осталось добавить еще пару штрихов. Во-первых, оператор
[]
в правой части операции присваивания
работает уже не так, как было написано, поскольку он возвращает
ArrayCursor
, а не
Foo*
или
Foo*&
.
Но причин для беспокойства нет, потому что
Foo*()
в случае необходимости автоматически
преобразует
ArrayCursor
к
Foo*
. Вторая проблема заключается в том, что оператор
[]
не может
использоваться слева от оператора
->
; на помощь приходит
operator->()
!
Do'stlaringiz bilan baham: |