链表中的 C++ 析构函数

c++ destructor in linked lists

本文关键字:析构函数 C++ 链表      更新时间:2023-10-16

我已经在 c++ 中使用链表构建了一个堆栈,但我被告知整个事情毫无价值,因为我的析构函数没有有效地完成它的工作,所以任何人都可以告诉我如何为这段代码构建析构函数。我的理解是构造函数工作是删除未使用的内存,我提到的析构函数还有其他功能吗? 任何帮助将不胜感激。

#include <iostream>
#include <string>
#include <limits>
using namespace std;
class Stack
{
private:
struct node
{
int data;
node* next;
node()
{
int data = 0;
next = nullptr;
}
};
node* top;
public:
Stack()
{
top = nullptr;
}
~Stack()
{
delete top;
}
void push(int n)
{
node* temp = new node;
temp->data = n;
temp->next = top;
top = temp; 
}
int pop()
{
node* temp = top;
top = top->next;
return temp->data;
}
int peek()
{
return top-> data;
}
bool isEmpty()
{
return top == 0;
}
};
int main()
{
Stack stack;
std::string command;
while (true)
{
std::cout << "stack>";
std::cin >> command;
try
{
if (command == "pop")
{
if (stack.isEmpty())
{
throw std::runtime_error("error: stack is empty");
}
std::cout << stack.pop() << std::endl;
}
else if (command == "push")
{
int n;
if (!(std::cin >> n))
{
throw std::runtime_error("error: not a number");
}
stack.push(n);
}
else if (command == "peek")
{
if (stack.isEmpty())
{
throw std::runtime_error("error: stack is empty");
}
std::cout << stack.peek() << std::endl;
}
else if (command == "end")
{
while (!(stack.isEmpty()))
{
std::cout << stack.pop() << std::endl;
}
return 0;
}
else
{
throw std::runtime_error("error: invalid command");
}
}
catch (std::runtime_error& e)
{
std::cin.clear();
std::cin.ignore(numeric_limits<streamsize>::max(), 'n');
std::cerr << std::endl << e.what() << std::endl;
}
}
return 0;
}

Stack::pop()方法中有内存泄漏,并且Stack()析构函数只会删除top因此,如果您有 1 个以上的元素,您将有另一个泄漏。当你处理动态分配的内存时,你最好使用智能指针:

class Stack
{
struct node;
using node_ptr = std::unique_ptr<node>;
private:
struct node
{
int data;
node_ptr next;
node(int n, node_ptr nxt) : data(n), next( std::move(nxt) )
{ // your old ctor has a bug, you "initialized" local variable
}
};
node_ptr top;
Stack() = default;
~Stack()
{
while(top)
top = std::move( top->next );
}
int pop()
{
node_ptr temp = std::move(top);
top = std::move(temp->next);
return temp->data;
}
void push(int n)
{
top = std::make_unique<node>(n,std::move(top));
}
int peek() const
{
return top->data;
}
bool isEmpty() const
{
return !top;
}
};

您的代码变得更小,更易于理解,并且没有以前的泄漏。您不必提供显式Stack析构函数,但它效率低下(执行递归调用但仍然正确(。提供的析构函数只是更有效。

您只删除顶部元素。

尝试向node结构中添加~node() { delete next; }

并确保它NULL终止,以便停止!

您需要为 while 链接列表 :

~Stack()
{
while(top)
{
struct node* curr = top;
top = top->next;
delete curr;
}
top = nullptr;
}

你有这样的列表:

top---->node->node->node->node->...

Stack的析构函数中,删除top指向的对象。你剩下的就是:

node->node->node->...

请注意,大多数节点尚未被删除,并且由于不再有指向它们的内容,因此不再有删除它们的方法。这称为内存泄漏。

若要修复析构函数,必须删除列表中的每个节点。提示:使用循环;复制下一个指针,删除当前节点并继续下一个指针。


析构函数并不是堆栈泄漏的唯一位置。pop无法在从列表中取消链接之前删除顶部节点。

此外,分配或复制Stack对象将导致两个对象具有相同的列表,并因此尝试将其删除两次(在相应对象的每个析构函数中一次(。行为将是不确定的。每当在析构函数中管理资源时,始终需要定义一个复制(和移动(构造函数和分配运算符来管理资源。这称为 3 法则(移动操作为 5(。如果您不希望类可复制(或可移动(,则可以声明删除它们。

拥有裸指针是一个糟糕的设计。更好的设计是使用std::unique_ptr

node的构造函数使data成员未初始化。我不认为您的堆栈会读取默认的初始化节点,因此这可能不是问题,但是您可能对编写的构造函数有不同的期望。