114
Курсоры
В предыдущем разделе мы говорили о присваивании элементам массива. Для массива
Foo*
все
прекрасно работало, но попытка присвоить что-нибудь «элементу» строковой
ассоциации кончается
неудачей.
association[String(“Hello”)] = String(“Good looking”);
Дело в том, что левая часть не является ни левосторонним выражением (lvalue), ни классом с
перегруженным оператором
=
. В этом случае можно сконструировать аргумент с использованием
интерфейса вставки в коллекцию на
базе функций класса, поскольку это все-таки не
настоящий
массив, а нечто загримированное под него с помощью оператора
[]
. Многие классы, перегружающие
оператор
[]
, с точки зрения семантики
являются массивами, но используют хитроумные структуры
данных для оптимизации. Давайте рассмотрим конкретный пример (разреженные массивы), а затем
вернемся к более общим коллекциям (таким как ассоциации).
Простой класс разреженного массива
Разреженный массив относится к числу основных структур данных. Он представляет собой матрицу, у
которой большинство ячеек в любой момент времени остается пустым. Возможно, вы принадлежите к
числу счастливчиков с 256 гигабайтами
памяти на компьютере, но большинству из нас просто не
хватит места для хранения всех ячеек матрицы 1000х1000х1000. Да и не хочется выделять память под
миллиард ячеек, если в любой момент из них используется не более 1000. Несомненно, в вашем мозгу
всплывают различные структуры данных, знакомые по начальному курсу программирования в
колледже:
связанные списки, бинарные деревья, хеш-таблицы и все прочее, что упоминает Кнут. На
самом деле не так уж важно, какая структура данных лучше подойдет для низкоуровневой реализации.
Прежде всего необходимо понять, как же использовать эти низкоуровневые средства и одновременно
создать для клиентских объектов впечатление, что они имеют дело с самым обычным массивом?
В следующей реализации «методом грубой силы» для хранения данных используются связанные
списки. Структура
Index
уже встречалась нам выше.
class SparseArray {
private:
struct Node {
Index
index;
//
Индекс массива
Foo*
content;
//
Содержимое массива по данному индексу
Node*
next;
//
Следующий элемент списка
Node(Index i, Foo* f, Node* n) : index(i), content(f), next(n) {};
};
Node*
cells;
//
Связанный список элементов
public:
SparseArray() : cells(NULL) {}
Foo* operator[](Index i);
};
inline Foo* SparseArray::operator[](Index i)
{
SimpleSparseArray::Node* n = cells;
while (n != NULL) {
if (n->index == i) // Использует перегруженный оператор ==
return
n->content;
n = n->next;
}
return
NULL;
}