我以前做过一些链表,这些概念对我来说都很有意义,但对于一个项目,我必须用C++做一个模板化的链表,我没有用太多,我遇到了一些麻烦。请帮忙。我在这么简单的事情上花了太多时间。
我已经有了一些列表类,但问题就在这里。当我在一个测试用例中创建三个节点并将它们全部连接起来时,如果我调用
node1.getNext().show();
这很好,但如果我
node1.getNext().getNext().show();
我得到了一个segfault(核心转储)。这里怎么了?我已经尝试过多次更改getNext()和getPrevi()返回值上的指针,但都没有成功。问这个问题我觉得很愚蠢,但我遇到了一些严重的麻烦。下面是我的节点类,后面是一个给出segfault的示例测试用例。
节点.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;
}
您需要通过引用而不是通过值传递值。
当执行node1.setNext(node1)
时,setNext()
在堆栈上获得node1
的副本,而不是您在main()
中定义的变量。一旦setNext()
退出,存储在next
中的地址将不再有效。
对于初学者,将函数setPrevi和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;
}
这将修复您的即时segfault。然而,您确实需要更多地考虑代码的设计,以及当您通过引用或值传递时会发生什么,以正确地编写代码并避免进一步的问题。
您应该将下一个/上一个成员存储为指针。设置上一个和下一个节点时,将按值获取参数,从而进行复制。
这意味着,当您进入node1.getNext().getNext().getValue()
中的第二个.getNext()
时,您将遇到一个悬空指针,这就是代码失败的原因。
手动添加节点是很费力的,类似std::list
的工作方式是使用指向其拥有的集合的begin
和end
指针。当一个新节点被推到列表的前面或后面时,集合会创建节点,存储值并链接指针,为下一个节点和/或遍历做好准备。
最大的问题是setNext
和setPrev
函数按值获取参数,因此您将获得在main中声明的节点的副本。
如果要继续执行此操作,请通过引用或指针传递这些参数。传递指针可能会更有语义,并防止这种意外的错误。