2017-01-09 4 views
0

В настоящее время я читаю книгу о структурах данных и изучаю C++ на стороне. Я пытаюсь реализовать простой связанный список. Ниже приведен код для списка, который может принимать максимум два элемента (чтобы изолировать мою проблему). Неправильно это объявление указателя на следующий узел в списке. Когда я создаю новый экземпляр Node и создаю указатель на него, указатель остается неизменным при каждом вызове метода, поэтому все элементы в списке указывают на один и тот же узел. Однако, если я создаю указатель напрямую, все работает так, как ожидалось.Локальная переменная, переписанная при каждом вызове метода

Я предполагаю, что у меня есть фундаментальное непонимание указателей, ссылок и ключевое слово new.

Не стесняйтесь запускать приведенный ниже код. Рабочий код закомментирован.

#include <iostream> 
using namespace std; 

template <typename T> class Node { 
public: 
    Node(T nvalue) { 
     this->value = nvalue; 
     this->next = NULL; 
    } 
    T value; 
    Node *next; 
}; 

template <typename T> class LinkedList { 
    public: 
     Node<T> *head; 
     LinkedList() { 
      this->head = NULL; 
     } 
     void append(T newVal) { 
      // Correct 
      // Node<T>* newNode_ptr = new Node<T>(newVal); // newNode_ptr is different on each call 
      // Incorrect!? 
      Node<T> newNode = Node<T>(newVal); 
      Node<T> * newNode_ptr = &newNode; // newNode_ptr is the same on each call 
      cout << "New Node Address: " << newNode_ptr << endl; 
      if (!(this->head)) { 
       this->head = newNode_ptr; 
       cout << "Value 0: " << this->head->value << endl; 
      } else { 
       this->head->next = newNode_ptr; 
       cout << "Value 0: " << this->head->value << endl; 
       cout << "Value 1: " << this->head->next->value << endl; 
      } 
     } 
}; 

int main() { 
    LinkedList<int> list = LinkedList<int>(); 
    list.append(21); 
    cout << "..." << endl; 
    list.append(42); 
} 

Обратите внимание, что этот код не совсем хорошо продуман (некоторые вещи должны быть частными, using namespace std следует избегать). Я знаком с python, поэтому этот материал указателя немного ошеломляет. Заранее спасибо за помощь!

+2

*** Узел * newNode_ptr = & newNode; *** Будет определено поведение, когда 'newNode' выходит из области видимости. – drescherjm

+0

@drescherjm они выходят из области видимости вместе, поэтому я не вижу проблемы – user

+2

Небольшой совет - при создании структуры данных сначала создайте ее для определенного типа или типа. Только когда у вас есть эта работа и тестирование, превратите ее в шаблон. –

ответ

2
Node<T>* newNode_ptr = new Node<T>(newVal); 

Это более правильный путь. Это нормально, что адрес newNde_ptr отличается, это то, что вы хотите. Каждый узел является другим узлом, два разных объекта не могут иметь один и тот же адрес! Версия без new дает тот же адрес, потому что вы создаете объекты в стеке. Это не сработает, каждый узел будет уничтожен в конце функции append. Вы увидите необычные результаты (если это не сбой), если вы переместите часть печати append в другую функцию. Поскольку все ваши указатели указывают на один и тот же адрес (в вашем случае) и в тот момент, когда вы печатаете значения, которые адресуют , так происходит, чтобы быть действительным узлом, вы не видите сбой. Однако это неопределенное поведение и может измениться по ряду причин.

Разница между свободным хранилищем (куча для malloc/free) и стеком является фундаментальной концепцией C++. Вы должны прочитать об этом here.

Причина, по которой я видел , более правильный способ двух состоит в том, что вы все равно должны помнить delete своих узлов. Лучшим способом было бы использовать std::unique_ptr вместо необработанных указателей, чтобы избежать ошибок (и может быть других), которые поощряют использование указателей raw.

// Node * next; becomes 
std::unique_ptr<Node> next; 

// Node<T> newNode = Node<T>(newVal); becomes 
newNode = std::make_unique<T>(newVal); 
+1

Лучше даже, 'std :: unique_ptr >' – SergeyA

+0

Спасибо за ваш ответ! Я знаю, что это правильный путь, мне просто интересно, почему другое решение не сработало.Не могли бы вы исправить мое второе решение, когда я сначала инициализирую объект «Node», а затем получаю связанный указатель? – Nico

+2

@Nico Нет, это решение невозможно заставить работать. Вам нужно будет сделать какое-то распределение памяти. Если объект создается в стеке, его нельзя заставить выжить в конце его области. Ближайшим решением было бы переместить его в другой экземпляр ([see move constructors] (http://en.cppreference.com/w/cpp/language/move_constructor)). Это не будет работать в вашем случае. Это подразумевает, что тип 'Node' содержит другой« узел »и значение. Тогда это означало бы, что 'sizeof (Node) = sizeof (Node) + sizeof (T)'. –

Смежные вопросы