graph — Частный массив адресов соседних узлов в переполнении стека

//// РЕДАКТИРОВАТЬ # 2: Удалил всю предыдущую информацию и просто опубликовать рабочий код сейчас. Предыдущий вопрос стал слишком длинным:

#include <iostream>
#include <vector>
using namespace std;

template<class T>
class Node{
T data;
vector<Node<T>*> adjacent;
friend class Graph;
public:
int n;
Node(T initData) : data(initData), n(0){}
void addAdjacent(Node<T>& other){
adjacent.push_back(&other);
n++;
}
T getData(){
return data;
}
Node<T>* getEdge(int edgeNum){
return adjacent[edgeNum];
}
};
template<class T>
class GraphCl{
int n;
vector<Node<T>*> nodes;
T input;
public:
GraphCl(int size): n(size){
for (int i=0;i<n;i++){
cout << "Enter data for node " << i << ": ";
cin >> input;
nodes.push_back(new Node<T>(input)) ;
}
}
void addEdge(int baseNode, int edgeNode){
nodes[baseNode]->addAdjacent(*nodes[edgeNode]);
}
void printGraph(){
for (int i=0;i<n;i++){
Node<T> *base = nodes[i];
cout << "Data of node " << i <<": "<< base->getData() <<endl;
for (int j=0;j<base->n;j++){
cout << "Edge #"<< j+1 << " of node " << i << ": " << base->getEdge(j) <<endl;
}
}
}
};

int main(){
GraphCl<int> *myGraph = new GraphCl<int>(5);
myGraph->addEdge(0,1);
myGraph->addEdge(0,2);
myGraph->addEdge(0,3);
myGraph->addEdge(0,4);
myGraph->addEdge(3,1);
myGraph->addEdge(3,0);
myGraph->printGraph();
return 0;
}

Выход:

Enter data for node 0: -34
Enter data for node 1: 12
Enter data for node 2: 56
Enter data for node 3: 3
Enter data for node 4: 23
Data of node 0: -34
Edge #1 of node 0: 0x7fbeebd00040
Edge #2 of node 0: 0x7fbeebd00080
Edge #3 of node 0: 0x7fbeebe00000
Edge #4 of node 0: 0x7fbeebd000d0
Data of node 1: 12
Data of node 2: 56
Data of node 3: 3
Edge #1 of node 3: 0x7fbeebd00040
Edge #2 of node 3: 0x7fbeebd00000
Data of node 4: 23

Как видите, эта простая реализация работает. Я решил просто вырезать все сложные вещи и сохранить их простыми с динамически меняющимися векторами. Очевидно, менее эффективно, но я могу работать с этого момента. Поскольку я новичок в C ++, предыдущая реализация заставила меня развернуться на 360 градусов, размышляя о том, куда делись все указатели на указатели, даже не задумываясь о распределении памяти. Приведенный выше код в основном представляет собой ориентированный граф, который очень чувствителен к ошибкам ввода, поэтому я все еще работал над ним.
Спасибо за помощь!

1

Решение

Отвечая на обновленный вопрос

Есть несколько проблем с положением Nodeнепосредственно в std::vector в твоем случае.

Используя std::vector отлично подходит для многих вещей, но если вы делаете это, вы должны убедиться, что указатели на векторы не используются. Помните, что указатели ссылаются на точные адреса в памяти, где хранится объект. vector это растущий контейнер элементов. Чтобы хранить элементы непрерывно, вектор выделяет кучу памяти, помещает туда объекты, и если он должен расти, он выделяет больше памяти и перемещает объекты вокруг. По сути, это делает что-то похожее на то, что вы делаете в своем Node и расти (за исключением, в случае его явного уничтожения объектов перед освобождением старой памяти).

Обратите внимание, что ваш Grow Функция выделяет новую память и копирует указатели. Одновременно векторы могут выделять новую память и копировать данные. Это означает, что удерживать указатели на данные в векторе плохо. Единственная гарантия vector дает вам понять, что его данные будут по-прежнему доступны с использованием индексации в стиле массива, поиска, итерации и т. д., а не то, что данные будут существовать в одном и том же месте памяти навсегда.


Объясняя точную ошибку, которую вы видите

Вектор вызывает конструктор копирования. Конструктор копирования по умолчанию копирует каждое поле по одному. Это не то, что вы хотите в случае Nodeпотому что тогда у вас есть два вектора, которые думают, что они владеют Node** adjacent место в памяти. Когда первый узел (старая копия) уничтожается, он освобождает смежные узлы (что совпадает с соседним узлом копии). Когда новая копия уничтожается, она попытается освободить ту же самую область памяти, но она уже освобождена. У вас также есть проблема в том, что, если вы попытаетесь получить доступ к памяти после того, как она была уничтожена на первом узле, у вас будут проблемы.

Почему эта ошибка появлялась, когда вы добавляли только узлы?

Когда вектор увеличивается до определенной величины, его необходимо изменить. В большинстве случаев процесс примерно такой:

  1. Выделите кучу больше памяти (обычно вдвое больше старой емкости)
  2. Вызовите конструктор копирования, чтобы скопировать элементы из старого местоположения в новое местоположение
  3. Уничтожить элементы в старом месте (скажем, явно вызвав деструктор)
  4. Вставьте новый элемент в новом месте

Ваша ошибка появляется из-за шагов 2 и 3, в основном.

Исправление этой конкретной ошибки

Для вашего случая конструктор копирования по умолчанию не годится, потому что копирование узла должно соответствовать глубоко копия всех данных. Обычная копия в C ++ скопирует все данные в классе или самой структуре. Если данные являются указателем, то указатель копируется, а не объект, на который он указывает.

Переопределите конструктор копирования и оператор присваивания:

    Node(const Node<T>& other) : data(other.data), capacity(other.capacity), n(other.n) {
adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
memcpy(adjacent, other.adjacent, capacity * sizeof(Node**));
}

Node<T>& operator= (const Node<T>& other) {
data = other.data;
capacity = other.capacity;
n = other.n;
adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
memcpy(adjacent, other.adjacent, capacity * sizeof(Node**));
}

Большая проблема

Большая проблема с вашим кодом заключается в том, что использование std::vector а также указатели на его элементы. Выберите один из:

  • Используйте массив фиксированного размера (который стабилен в памяти) и укажите на эти объекты
  • В общем, забудьте об указателях и сделайте из соседнего списка список индексов для вектора (он менее производительный, так как вам нужно каждый раз проходить через вектор, но это, скорее всего, пока не будет вашим узким местом)
0

Другие решения

доступность

Относительно доступности массива к Graphближайшая вещь к текущей реализации — объявить Graph как друг Node, Просто добавьте:

friend Graph;

До конца Node объявление класса.

Тем не менее, делая класс как friend иногда это признак того, что API, который вы определили, не совсем верен, если классы должны знать слишком много о деталях реализации друг друга. В качестве альтернативы вы можете предоставить интерфейс для Node такие как:

void AddAdjacent(Node* other);

Управление смежными узлами

Если вы хотите, чтобы ваш adjacent массив указателей должен быть расширяемым, тогда вы в основном воссоздаете std::vectorтак что я бы предложил использовать std::vector<Node*>, Инициализация вектора с помощью конструктора по умолчанию (пустого) позаботится об этом, и nodes[baseNode]->adjacent.push_back(...) будет все, что вам нужно в addEdges,

Если память не учитывается, и у вас есть максимальное количество узлов в графе, вы можете создать экземпляр массива постоянного размера.

Если вы действительно не хотите использовать std::vector, но вы на самом деле хотите растущий массив указателей, тогда вам придется управлять своими собственными malloc а также free звонки. Я напишу что-нибудь на этот счет, но мой совет — просто продолжать vector,

Если вам интересно, подход с использованием массива будет выглядеть примерно так:

template<class T>
class Node : public Graph{
Node **adjacent; //pointer to array of POINTERS TO adjacent Nodes
int n;
size_t capacity;
T data;
friend Graph;

public:
Node(T initData) : data(initData), capacity(8) {
n = 0;
adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
}
~Node() {
free(adjacent);
}
void Grow() {
size_t new_cap = base.capacity * 2;
Node<int> **copy = reinterpret_cast<Node<int>**>(malloc(new_cap * sizeof(Node**)));
memcpy(copy, base.adjacent, base.capacity); // copy and adjacent are non-overlapping, we can use memcpy
free(base.adjacent);
base.adjacent = copy;
base.capacity = new_cap;
}
};

И вставка:

Node<T>& base = nodes[baseNode];
Node<T>* edge = &(nodes[edgeNode]);
if (base.capacity == base.n) base.Grow();
base.adjacent[base.n++] = edge;
1

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector