【问题标题】:What all should be deleted in the destructor of a class在类的析构函数中应该删除什么
【发布时间】:2017-01-11 00:09:22
【问题描述】:

所以我已经有一段时间没有进行任何 c++ 编码了,我只是想知道应该在析构函数中删除基本链表中的哪些变量,不幸的是我目前无法查阅我的 c++ 手册关于此事.链表类如下所示:

#include <string>
#include <vector>

class Node
{
    Node *next;
    string sName;
    vector<char> cvStuff;

    Node(string _s, int _i)
    {
        next = nullptr;
        sName = _s;

        for (int i = 0; i < _i; i++)
        {
            cvStuff.insert(cvStuff.end(), '_');
        }
    }

    ~Node()
    {
         //since sName is assigned during runtime do I delete?
         //same for cvStuff?
    }
};

我也很好奇,如果在我调用的析构函数中

delete next;

是否会转到链表的下一个节点并删除该节点,从而从该点递归删除整个列表?另外,如果是这种情况并且我出于某种原因选择实现它,我是否必须在删除它之前检查 next 是否为 nullptr 或者它不会产生影响?

谢谢。

【问题讨论】:

  • 这试图同时成为 ListNode 和 List。谁拥有这份名单?如果它们相同,您是否共享列表的尾部?您打算删除元素,还是只删除整个列表?
  • @lorro,是的,这在技术上是一个节点类,并已进行了相应的调整。定义共享尾部?另外,删除整个列表。
  • 在这种情况下,不要删除!否则,您将无法编写会擦除列表中任何给定元素的快速擦除。共享前向链表的尾部意味着,如果您知道两个列表的背面应该具有相同的元素,则您共享节点。对于一旦构建并变为 const 的列表非常有用。

标签: c++ vector destructor


【解决方案1】:

基本上你应该只是

delete next 

这就是你应该做的一切:

删除它就可以了。

【讨论】:

  • 嗯,这仅适用于 const 非尾共享列表。对于非常量列表,您不能删除下一个:这将不允许擦除元素 O(1)。对于尾部共享列表,情况更糟:您最终会删除所有其他元素。
  • @lorro 对于递归数据结构,你真的应该使用像shared_ptr这样的引用计数智能指针;其他任何事情都将是一场噩梦。节点共享数据结构要少得多,也许您下次可以在问题中提及这一点。
  • @AmiTavory: lorro 不是提问者
  • @Ami:不是我的问题 :),但每当我们实现(非学校)列表时,它们至少是 const tail-sharing:否则只需使用 std::list&lt;&gt;。另请注意,如果您不共享,则甚至无法删除,但允许删除中间的元素(在 OP 的解决方案中)。
  • @lorro 哦,很抱歉混淆了(我认为没有造成任何伤害,但还是很抱歉)。你真的在 C++ 中经常遇到这种情况吗?它在 Haskell、Scheme 等中很常见,但我个人从未在生产中见过它。尽管如此,基于shared_ptr 的管理似乎是可行的方法。
【解决方案2】:

你的类析构函数将被调用(它是空的),然后成员对象的析构函数被调用。

如果成员不是对象,则不调用析构函数。

在你的例子中:

- List *next: pointer on List: no destructor called
- string sName: string object: destructor called
- vector<char> cvStuff: vector object: destructor called

好消息:你无事可做。析构函数声明在这里甚至没有用。

如果您在析构函数中删除 next,那么删除一个项目将删除您列表中的所有其他项目:不是很有用。

(并且您的 List 对象应该被称为 Node)。 List 是所有节点的链接结果,由您创建的第一个节点持有。

【讨论】:

    【解决方案3】:

    理想情况下,什么都不用:使用智能指针:std::unique_ptr&lt;&gt;std::smart_ptr&lt;&gt;boost::scoped_ptr&lt;&gt; 等。

    否则,您将删除拥有的本机指针。 next 拥有吗?

    • 您打算删除列表中间的内容吗?如果是,则不能在析构函数中删除。
    • 你打算分享尾巴吗?如果是,您需要引用计数的智能指针。

    删除 nullptr 没关系(什么都不做)。在示例中,您不应删除 sName 和 cvStuff,因为它们是作用域的,因此会自动销毁。

    另外,如果这将是一个可能会变大的列表,您可能需要手动销毁和释放*next。这是因为您不想通过递归耗尽堆栈空间。

    此外,我建议将其分隔为List,意思是数据结构和ListNode,意思是一个元素。您的问题实际上表明了这种模棱两可,您不知道您是要删除析构函数中的ListNode 还是List。将它们分开可以解决这个问题。

    【讨论】:

    • 我不确定 next 是否拥有。 1:考虑功能我永远不会只删除一个元素,而是会删除整个列表,这就是为什么我想了解整个“递归析构函数”部分的原因。 2:不知道共享tails是什么意思,是不是有多个指针指向一个对象的时候? 3:列表永远不会大于六个,但是关于由于堆栈空间不足而导致的手动销毁,我假设您的意思是手动删除包含这些节点的类中的节点? 4:是的,这个类应该被称为“节点”
    • 1.如果你永远不想擦除,这取决于你。即使那样,我也会强烈考虑 unique_ptr - 这样,您就不必做出这个选择。 2. 完全正确 - 如果共享,请使用 shared_ptr 或 sg。类似 3. 在这种情况下,忘记列表并存储元素 ptrs 的小向量(如果可以的话,甚至是元素)。除此之外,是的,我的意思是迭代。 4. 好的;仍然,我建议有一个删除的List 和一个不删除的Node(最不奇怪) - 但选择是开放的。
    【解决方案4】:

    具有自动生命周期的对象在超出范围时会调用其析构函数:

    {  // scope
        std::string s;
    }  // end scope -> s.~string()
    

    除非在其上调用 delete,否则不会调用动态对象(用 new 分配)。

    对于成员变量,作用域是对象的生命周期。

    struct S {
        std::string str_;
        char* p_;
    };
    
    int main() {  // scope
        {  // scope
            S s;
        }  // end scope -> s.~S() -> str_.~string()
    }
    

    请注意,上面的 p_ 并没有发生什么特别的事情:它是一个指针,是一个简单的标量类型,因此代码不会对它自动执行任何操作。

    因此,在您的列表类中,您唯一需要担心的是您的next 成员:您需要确定它是否是“拥有”指针。如果它是一个“拥有”指针,那么您必须在析构函数中的对象上调用 delete

    或者,您可以利用“RAII”(资源获取是初始化)并使用 object 来包装指针并提供一个析构函数,该析构函数将为您调用 delete

    {  // scope
        std::unique_ptr<Node> ptr = std::make_unique<Node>(args);
    }  // end scope -> ptr.~unique_ptr() -> delete -> ~Node()
    

    unique_ptr 是一个纯粹拥有的指针,另一种选择可能是shared_ptr,它使用引用计数,因此当你没有任何剩余的shared_ptrs 时,底层对象只有deleted对象。

    如果您将Nodes 的实际地址保存在其他地方,您会认为您的next 指针是一个非拥有指针:

    std::vector<Node> nodes;
    populate(nodes);
    
    list.insert(&nodes[0]);
    list.insert(&nodes[1]);
    // ...
    

    在上述情况下,vector 拥有节点,你绝对不应该在 Node 析构函数中调用 delete,因为 Nodes 不是你要删除的。

    list.insert(new Node(0));
    list.insert(new Node(1));
    

    这里,列表/节点是唯一具有指向节点的指针的东西,所以在这个用例中,我们需要Node::~Node 来调用删除,否则我们就会有泄漏。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-31
      • 1970-01-01
      • 2016-09-24
      • 2015-01-12
      • 2012-07-24
      • 2017-05-18
      • 2018-09-09
      • 2012-08-11
      相关资源
      最近更新 更多