在析构函数中删除单链接列表的正确方法是什么?

问题描述 投票:2回答:2

你好,我在编写适当的析构函数方面有些含糊:

class SLLst 
{
public:
    SLLst() = default;
    SLLst(const SLLst&);
    SLLst& operator=(SLLst);
    ~SLLst();

    void insert(int);
    void remove(int);

private:
    SLLst* next = nullptr;
    int data = 0;
    friend void swap(SLLst&, SLLst&);
    friend std::ostream& print(std::ostream&, const SLLst&);
};

SLLst::SLLst(const SLLst& rhs) : 
    next(rhs.next ? new SLLst() : nullptr),
    data(rhs.data)
{
    cout << "cpy-ctor" << endl;
}

SLLst& SLLst::operator=(SLLst rhs)
{
    cout << "operator=(SLLst)" << endl;
    using std::swap;
    swap(*this, rhs);
    return *this;
}

void swap(SLLst& lhs, SLLst& rhs)
{
    cout << "operator=(SLLst)" << endl;
    using std::swap;
    swap(lhs.next, rhs.next);
    swap(lhs.data, rhs.data);
}

SLLst::~SLLst()
{
    cout << "dtor" << endl;
    delete next;// is this enough?

    // or should I use this code?
    //SLLst* cur = next;
    //SLLst* n = nullptr;

    //while (cur != NULL) {
    //  n = cur->next;
    //  cur->next = nullptr;
    //  delete cur;
    //  cur = n;
    //}

}


void SLLst::insert(int x)
{
    SLLst* tmp = new SLLst();
    tmp->data = x;
    if (!next)
    {
        next = tmp;
        return;
    }

    tmp->next = next;
    next = tmp;
}

std::ostream& print(std::ostream& out, const SLLst& lst)
{
    auto tmp = lst.next;
    while (tmp)
    {
        out << tmp->data << ", ";
        tmp = tmp->next;
    }
    return out;
}

您可以看到我是否仅在析构函数中使用delete next;,然后得到与列表中节点一样多的调用,但是为什么许多实现都使用循环来释放像析构函数中的注释代码这样的节点?

  • 因为如果仅在delete上调用next,则将递归调用析构函数,因此,我认为我不需要循环来释放析构函数中的节点?正确吗?

  • 我应该何时使用循环释放析构函数中的节点?谢谢!

*如果运行我的代码,我会得到:

81,77,57,23,16,7,5,

done
dtor
dtor
dtor
dtor
dtor
dtor
dtor
dtor
  • 您可以看到dtor被调用了8次;这是否意味着它已正确释放了所有节点?
c++ linked-list destructor
2个回答
3
投票

您可以看到我是否接下来仅使用删除;在析构函数中,然后得到与列表中的节点一样多的名称

因为如果我仅在下一个调用delete,那么析构函数将被递归调用,因此我认为我不需要循环来释放析构函数中的节点?正确吗?

我何时应该使用循环来释放析构函数中的节点?

需要时。

但是为什么许多实现都使用循环来释放节点,如析构函数中的注释代码?

因为许多实现都是“类似C的”,并且不使用析构函数。因此,他们需要。

您将充分利用C ++的对象管理功能来“为您做循环”。是的!

(尽管,老实说,我仍然会循环执行,因为您的方式可能会占用大量堆栈。)

现在进一步前进并切换到std::list(或std::forward_list)。 😏


0
投票

刚开始,std :: unique_ptr会帮您解决这个问题,但是如果您想自己做,那么看起来会像这样,请记住,这是一个最小的示例。

RAII是C ++中非常重要的原理,它基本上意味着在需要时进行分配,而在使用完它后销毁。

因此,如果指向一个节点,并且删除指向该节点的节点,那么该节点还应该销毁它指向的对象,因为它对该节点拥有所有权。

class List {
    Node* first_node;

    ~List() {
        delete first_node;
    }

};

class Node {
    ~Node() {
        delete next; // will then in turn destroy the one it points to untill one is nullptr, deleting nullptr is well defined in C++ nowadays
    }
    Node* next;

};

带有std :: unique_ptr的示例

class List {
    std::unique_ptr<Node> first_node;

    // default dtor
};

class Node {
    std::unique_ptr<Node> next;
    // default dtor

};
© www.soinside.com 2019 - 2024. All rights reserved.