Раньше я составлял несколько связанных списков, и все концепции имеют для меня смысл, но для проекта мне нужно создать шаблонный список на C ++, который я не очень часто использовал, и у меня возникли некоторые проблемы. Пожалуйста помоги. Я потратил слишком много времени на такую ​​простую вещь.

У меня уже есть некоторые из классов списка, но проблема где-то здесь. Когда я создаю три узла в тестовом примере и связываю их все, если я вызываю

node1.getNext().show();

Это нормально работает, но если я

node1.getNext().getNext().show();

Получаю segfault (дамп ядра). Что здесь не так? Я пытался изменить указатели на возвращаемые значения getNext () и getPrev () несколько раз, но безуспешно. Я чувствую себя глупо, задавая этот вопрос, но у меня серьезные проблемы. Ниже приведен мой класс узла, за которым следует пример тестового примера, который дает segfault.

Node.h :

template <class T> class Node
{
 public:
  Node();
  Node(T value);
  void setPrev(Node<T> node);
  void setValue(T value);
  void setNext(Node<T> node);
  Node<T> getPrev();
  T getValue();
  Node<T> getNext();
  void show();
  ~Node() { }

 private:
  Node<T> *prev;
  Node<T> *next;
  T value;
};

//default construct                                                                                                                                                     
template <class T> Node<T>::Node() {
  this->prev = NULL;
  this->value = NULL;
  this->next = NULL;
};

//overloaded construct                                                                                                                                                  
template <class T> Node<T>::Node(T value) {
  this->prev = NULL;
  this->value = value;
  this->next = NULL;
}

template <class T> void Node<T>::setPrev(Node<T> node) {
  this->prev = &node;
}

template <class T> void Node<T>::setValue(T value) {
  this->value = value;
}

template <class T> void Node<T>::setNext(Node<T> node) {
  this->next = &node;
}

template <class T> Node<T> Node<T>::getPrev() {
  return this->prev;
}

template <class T> T Node<T>::getValue() {
  return this->value;
}

template <class T> Node<T> Node<T>::getNext() {
  return this->next;
}

template <class T>
void Node<T>::show() {
  cout << value << endl;
}

Прецедент:

int main(int argc, char **argv) {

  typedef Node<int> IntNode;

  IntNode head(NULL);
  IntNode node1(23);
  IntNode node2(45);
  IntNode node3(77);
  IntNode tail(NULL);
  node1.setPrev(head);
  node1.setNext(node2);
  node2.setPrev(node1);
  node2.setNext(node3);
  node3.setPrev(node2);
  node3.setNext(tail);

  node1.show();
  node2.show();
  node3.show();

  cout << node1.getNext().getValue() << endl;
  cout << node1.getNext().getNext().getValue() << endl;
}
1
Jay Elrod 12 Мар 2012 в 19:56

3 ответа

Лучший ответ

Вам нужно передавать значения по ссылке, а не по значению.

Когда ваш node1.setNext(node1) выполняется, setNext() получает копию node1 в стеке, а не переменную, которую вы определяете в main(). После выхода из setNext() адрес, хранящийся в next, больше не действителен.

Для начала переопределите функции setPrev и setNext как

template <class T> void Node<T>::setPrev(Node<T> &node) {
  this->prev = &node;
}

template <class T> void Node<T>::setNext(Node<T> &node) {
  this->next = &node;
}

И это исправит ваши немедленные ошибки. Однако вам действительно нужно больше думать о дизайне вашего кода и о том, что происходит, когда вы передаете его по ссылке или по значению, чтобы написать это правильно и избежать дальнейших проблем.

4
George Skoptsov 12 Мар 2012 в 16:05

Вы должны хранить своих следующих / предыдущих членов как указатели. Когда вы устанавливаете предыдущий и следующий узлы, вы берете параметры по значению и, следовательно, копируете.

Это означает, что когда вы перейдете ко второму .getNext() в node1.getNext().getNext().getValue(), вы встретите висящий указатель, поэтому ваш код не работает.

Добавление узлов вручную является трудоемким делом. Что-то вроде std::list работает с помощью указателя begin и end на принадлежащую ему коллекцию. Когда новый узел помещается в начало или конец списка, коллекция создает узел, сохраняет значение и связывает указатели, готовые для следующего узла и / или обхода.

2
Konrad 12 Мар 2012 в 16:09

Большая проблема в том, что ваши функции setNext и setPrev принимают свои параметры по значению, поэтому вы получаете копию узлов, которые объявляете в main.

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

1
Mark B 12 Мар 2012 в 16:00