我正在使用双向链表。每个函数运行良好,但是在main()的末尾,它停顿了几秒钟并返回了意外的随机值。
起初,我认为这是由report()函数引起的,因此我在末尾再添加了一个add()函数,但没有得到解决。我怀疑这是内存分配问题,但我看不到某些对象在哪里进行了预分配。 (使用Code :: Blocks 17.12编译)。
这是我的.cpp文件(全部合为一体):
#include <iostream>
using namespace std;
typedef struct element {
element(){
data = 0;
next = 0;
prev = 0;
}
~element(){
delete next;
delete prev;
cout << "element destructed" << endl;
}
int data;
element* next;
element* prev;
} elem;
typedef struct doublylinkedlist{
doublylinkedlist(){
head = 0; tail = 0;
}
~doublylinkedlist(){
while(head!=0) {
head = head->next;
delete head->prev;
}
delete tail;
cout << "list destructed" << endl;
}
elem* head;
elem* tail;
} doublyll;
doublyll ls;
void add(){
elem* temp = new elem;
cout << "Enter an integer: ";
cin >> temp->data;
if(ls.head == 0) {//empty
ls.head = new elem;
ls.head = temp;
} else{
if(ls.tail == 0){ //1-item list
ls.tail = new elem;
ls.tail = temp;
ls.head->next = ls.tail;
ls.tail->prev = ls.head;
}
else{
temp->prev = ls.tail;
ls.tail->next = temp;
ls.tail = temp;
}
}
}
void report(){
if(ls.head == 0) cout << "List is empty!" << endl;
else{
elem *temp = ls.head;
do{
cout << temp->data << endl;
temp = temp->next;
} while (temp != 0);
}
}
int main(){
report();
add();
add();
add();
report();
add();
return 0;
}
有人可以指出错误的出处以及如何解决?我希望main()不会像往常一样停滞并返回0,而不是相反。 这是the program when executed,这是我的build message
答案 0 :(得分:0)
第一点:元素将由类doublylinkedlist
释放,因此在类element
中取消分配元素将导致双重释放。
因此,应该从delete
的析构函数中删除两个element
语句。
~element(){
/* remove them */
//delete next;
//delete prev;
cout << "element destructed" << endl;
}
第二点:在doublylinkedlist
的析构函数中,head->prev
在head = head->next;
之后被读取,而不检查head
是否为NULL
。
head
可以由任务分配NULL
,因此应进行检查。
~doublylinkedlist(){
while(head!=0) {
head = head->next;
if (head!=0) /* add this */
delete head->prev;
}
delete tail;
cout << "list destructed" << endl;
}
最后一个元素将由delete tail;
释放,因此此代码看起来很棘手,但应该可以。
加点:这些代码段
ls.head = new elem;
ls.head = temp;
和
ls.tail = new elem;
ls.tail = temp;
通过分配元素并将其立即丢弃而导致内存泄漏。 您应该删除多余的分配。
/* remove this */
//ls.head = new elem;
ls.head = temp;
和
/* remove this */
//ls.tail = new elem;
ls.tail = temp;
答案 1 :(得分:0)
除非您使用std::shared_ptr
或类似的构造,否则每个对象都需要拥有另一个对象,该对象是它的所有者并负责取消分配它。您的代码需要具有清晰的语义来转移所有权(例如,函数createNode()
希望其调用者破坏该节点)。
在您的代码节点中,列表和每个元素均将其删除。这意味着所有内容都会被删除两次(或更多)。在您的特定情况下,这是doublylinkedlist
被破坏时的事件序列:
doublylinkedlist
删除其第一个元素。此无限循环最终导致堆栈溢出。请注意,这不能保证是确切的事件顺序,因为两次删除对象是未定义的行为,因此有可能发生任何事情。
简单的解决方法是删除element
析构函数,并让列表负责所有元素的生命周期。
您还应该修改doublylinkedlist
析构函数,因为它将尝试取消引用最后一个元素上的空指针,并且您也不需要删除tail
,因为它应该已经被删除了。例如:
~doublylinkedlist(){
while(head!=0) {
auto temp = head;
head = head->next;
delete temp;
}
}
您还应确保遵守rule of three/five)。一种实现方法是利用智能指针,例如使用unique_ptr
s来使代码看起来像这样:
#include <iostream>
#include <memory>
using namespace std;
typedef struct element {
element() {
data = 0;
next = nullptr;
prev = nullptr;
}
~element() {
cout << "element destructed" << endl;
}
int data;
std::unique_ptr< element > next;
element* prev;
} elem;
typedef struct doublylinkedlist {
doublylinkedlist() {
head = 0; tail = 0;
}
~doublylinkedlist() {
std::cout << "list destructed\n";
}
std::unique_ptr< elem > head;
elem* tail;
} doublyll;
doublyll ls;
void add() {
std::unique_ptr<elem> temp(new elem());
cout << "Enter an integer: ";
cin >> temp->data;
if (ls.head == nullptr) {//empty
ls.head = std::move(temp);
}
else {
if (ls.tail == nullptr) { //1-item list
ls.head->next = std::move(temp);
ls.tail = ls.head->next.get();
ls.tail->prev = ls.head.get();
}
else {
temp->prev = ls.tail;
ls.tail->next = std::move(temp);
ls.tail = ls.tail->next.get();
}
}
}
void report() {
if (ls.head == 0) cout << "List is empty!" << endl;
else {
elem *temp = ls.head.get();
do {
cout << temp->data << endl;
temp = temp->next.get();
} while (temp != 0);
}
}
int main() {
report();
add();
add();
add();
report();
add();
return 0;
}
现在element
的所有权是明确的,列表拥有头,head拥有其next
节点,节点拥有其next
节点,依此类推。销毁列表会自动破坏第一个节点自动销毁第二个节点等。在此代码中,您实际上可以完全省略析构函数。这也应该有助于防止内存泄漏,例如,如果您决定对add
添加一些错误检查,则未使用的temp
元素将被自动删除:
void add() {
std::unique_ptr<elem> temp(new elem());
cout << "Enter an integer: ";
cin >> temp->data;
if (!cin || temp->data > 100) {
cout << "invalid input value\n";
return; // temp is automatically deleted here
}
...
}