TL; DR: у меня есть связанная структура данных, и я решил использовать не указатели, а индексы в контейнере для выражения этих ссылок. Могу ли я моделировать отдельные элементы как отдельные объекты для более удобочитаемого кода без затрат на хранение нескольких ссылок на массив?
Предположим, у меня есть связанная структура данных. Для простоты, давайте в качестве примера рассмотрим двусвязный список с операцией удаления узла. Классический способ моделирования это будет использовать указатели:
struct Node {
Node *prev, *next;
void remove() { next->prev = prev; prev->next = next; }
};
Но у указателей есть ряд недостатков. Они могут тратить пространство, так как размер указателя обычно не может быть выбран в соответствии с вариантом использования. Они делают для плохого формата провода. Если я сохраню узлы в векторе, изменение размера может сделать недействительными указатели. Копирование структур данных становится сложнее. Так что вместо этого я мог бы перейти на индексы в некоторый массив:
struct Node {
int prev, next;
};
struct LinkedList {
std::vector<Node> nodes;
void remove(int i) {
Node& n = nodes[i];
nodes[n.next].prev = n.prev;
nodes[n.prev].next = n.next;
}
};
Но теперь операция, которая ранее была методом отдельного узла, стала методом класса контейнера. Этот семантический сдвиг затрудняет чтение кода. Чтобы избежать этой проблемы, я мог бы пойти на представление, основанное на паре контейнера и индекса узла.
struct Node { int prev, next; };
struct LinkedList;
struct NodeRef {
int i;
LinkedList& l; // This reference here is what's worrying me
NodeRef(int index, LinkedList& list) : i(index), l(list) {}
NodeRef prev() const;
NodeRef next() const;
void remove();
};
struct LinkedList {
std::vector<Node> nodes;
NodeRef root() { return NodeRef(0, *this); }
};
NodeRef NodeRef::prev() const { return NodeRef(l.nodes[i].prev, l); }
NodeRef NodeRef::next() const { return NodeRef(l.nodes[i].next, l); }
void NodeRef::remove() {
Node& n = l.nodes[i];
l.nodes[n.next].prev = n.prev;
l.nodes[n.prev].next = n.next;
}
Так что теперь я могу использовать NodeRef для выражения моего алгоритма в приятной объектно-ориентированной манере, с узлом в качестве сущности, с которой я могу работать, и в то же время используя индексы вместо указателей за кулисами.
Но когда у меня есть какой-то сложный алгоритм, работающий с несколькими узлами одновременно, тогда у меня будет несколько объектов NodeRef, ссылающихся на один и тот же базовый объект LinkedList. Это кажется расточительным, как с точки зрения потребления памяти, так и работы по их копированию. Я бы предположил , что компилятор может сможет обнаружить некоторую избыточность и избавиться от нее. Но могу ли я что-нибудь сделать, чтобы помочь этому оптимизировать использование только одной ссылки, хотя семантически у меня есть несколько?