我有一些链表代码,一切正常,直到我在析构函数中创建 pop_front 函数。该代码在其他任何地方都可以使用,我已经尝试打印链接列表以查看它是否正确创建并且确实如此。这是我的代码中唯一调用 pop_front 的部分,也是我释放任何内容的唯一部分
template <typename T>
class DList {
Node<T>* head;
Node<T>* tail;
public:
DList() {
head = nullptr;
tail = nullptr;
}
void push_front(T newData) {
Node<T>* newNode = new Node<T>(newData, head, nullptr);
if (head) {
head->prev = newNode;
}
else {
tail = newNode;
};
head = newNode;
}
void push_front(DList<T> newList) {
newList.tail->next = head;
head->prev = newList.tail;
head = newList.head;
}
void pop_front() {
if (head) {
Node<T>* pop = head;
head = pop->next;
if (head) {
head->prev = nullptr;
}
else {
tail = nullptr;
}
delete pop;
}
}
~DList() {
while (head) {
pop_front();
}
}
};
最佳答案
你的 DList
类(也可能还有模板类 Node
,我们看不到它的定义)不遵循 rule of three/five .您定义了析构函数,但没有定义复制构造函数或复制赋值运算符。
这意味着复制 DList
完全(调用 DList::push_front(DList<T>)
几乎肯定会)将导致析构函数中的释放后使用。
请记住,编译器生成的复制构造函数和复制赋值运算符只是执行值的成员复制。在这种情况下,您的值是指针。因此,一份 DList
object 将简单地将原始指针复制到新对象。
当其中一个对象被破坏时,列表将被拆除,但是另一个对象仍将持有指向已释放的内存分配的指针。
如果您希望您的类型是可复制的,您必须实现复制构造函数和复制赋值运算符,因为编译器生成的版本会做错事。如果您使用的是 C++11,您还应该定义移动构造函数和移动赋值运算符。
在 C++11 中,您还可以删除这些构造函数和运算符,然后在您尝试制作拷贝的任何地方都会遇到编译时错误:
DList(DList const &) = delete;
DList(DList &&) = delete;
DList & operator=(DList const &) = delete;
DList & operator=(DList &&) = delete;
没有Node
的定义模板类我不能建议复制构造函数或复制赋值运算符的实现。但是,我可以建议移动变体的实现:
void swap(DList & other) {
std::swap(head, other.head);
std::swap(tail, other.tail);
}
DList(DList && other) : head(nullptr), tail(nullptr) {
swap(other);
}
DList & operator=(DList && other) {
swap(other);
return *this;
}
另一个错误:
void push_front(DList<T> newList)
应该是:
void push_front(DList<T> & newList)
因为您修改了“新列表”——修改拷贝没有多大意义。但是,这的语义不匹配 push_front(T)
!
单值重载获取一个值并将其插入这个列表。
列表重载采用另一个列表并将this 列表插入other 列表。列表重载将 other 列表推到 this 列表上更有意义,因为它会反射(reflect)单值重载的行为(“将这个东西添加到这个列表,不管它是什么意思”)。
这个方法的实现也有缺陷,因为它没有将任何一个列表的指针设置为空,这意味着您正在设置另一个释放后使用。
关于c++ - 写访问冲突 this->head 在 pop_front 中是 0xDDDDDDDD,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58384169/