【问题标题】:How to deal with dynamic allocation when implementing list of objects?实现对象列表时如何处理动态分配?
【发布时间】:2023-09-08 09:57:01
【问题描述】:

我必须实现一个看起来像这样的函数:

MyList * sum (MyList * l1, MyList * l2) {
    MyList * newlist = new MyList();
    //Adds two objects and place the result in a third new list
    return newlist;
}

该函数获取两个列表并将每个对象的总和放入一个新列表中。 MyList 类的节点带有指向 next 变量的指针,并且列表中的对象是用户定义的。

这让我开始思考——我应该如何处理对象和列表本身的内存动态分配?因为我必须为新列表的每个对象创建内存。

有什么方法可以将对象总和的值放入新列表中,而不必依赖动态分配?也许通过这样做:

Object result(node1->content + node2->content);
Node->content = &result; // will this object be erased when the function ends?

而不是这个:

Node->content = new Object(node1->content + node2->content);

我应该如何处理函数内部创建的新列表的生命周期与函数结束后将保存内存的变量相关?返回新列表时我可以这样做吗?

MyList & sum (MyList * l1, MyList * l2) {
    //Create variable without allocating memory and return it's reference 
}

简而言之,我的主要疑问是如何处理在函数内部创建并由其他对象持有的对象的生命周期。

【问题讨论】:

  • 你考虑过std::shared_ptr/std::unique_ptr吗?
  • 使用std::liststd::list 自动为您管理内存。
  • 呃,为什么会有人坚持那个函数签名......
  • 我还不知道如何使用智能指针。我会调查的。我已经在我的实现中使用了 std::list 并且我知道这些建议不是在您不需要时处理指针。我只是想了解对象内部变量的生命周期,而不是假装重新实现 std::list。感谢您的回答。

标签: c++ pointers memory-management dynamic-allocation dangling-pointer


【解决方案1】:
Node->content = &result; // will this object be erased when the function ends?

是的,因为它是一个局部变量。一旦其函数终止,result 的生命周期也会终止。


MyList & sum (MyList * l1, MyList * l2) {
    //Create variable without allocating memory and return it's reference 
}

由于与上述类似的原因,这也会失败。


我建议你使用std::shared_ptrstd::unique_ptr。如果您愿意,请阅读Differences between unique_ptr and shared_ptr

【讨论】:

    【解决方案2】:
    Object result(node1->content + node2->content);
    Node->content = &result; // will this object be erased when the function ends?
    

    是的,如果result是在函数体内按照上面所示的语法创建的,函数结束时会被销毁。

    这就是为什么你应该使用new 和动态分配的原因:这样,你的对象是在(而不是本地函数堆栈)上创建的,并且它们“存活”创建它们的函数的结尾。您可以将返回的指针传回给调用者。当然,调用者必须正确地delete 返回的指针,否则最终会导致内存(或其他资源)泄漏。

    在现代 C++ 中,您应该使用已经定义的 智能指针 类,例如 std::shared_ptrstd::unique_ptr,并且您的代码几乎不应该使用 explicit @987654327 @ 和 delete (除非您正在开发一些自定义的高度优化的数据结构,并且您需要对内存分配进行低级更细粒度的控制 - 即使在这种情况下,显式代码调用 newdelete应该安全地包裹在 RAII 类边界内)。

    但是,在我看来,您的代码更像是一个编程练习,并且这个练习的目的可能是学习使用显式 newdelete 进行动态内存分配。事实上,在生产质量代码中,您可以使用已经可用的标准链表类模板,而不是滚动您自己的列表类,例如 std::list

    【讨论】:

      最近更新 更多