【问题标题】:Dynamic allocation and large data structures in C++C++中的动态分配和大数据结构
【发布时间】:2020-02-13 11:59:12
【问题描述】:

最近,我了解到 C++ 程序员之间的共识是,应尽可能避免使用 newdeletedelete[] 运算符,正如已经讨论过的 hereherehere .在搜索时,我什至偶然发现了一个April Fools' joke,指出这些运算符将在 C++20 中被弃用。

我碰巧编写和维护了一个用这种语言编写的 C/C++ 程序,以便继承其他程序员编写的有用库和类。由于它必须在非常有限的环境中运行(即,程序最少的旧 Linux 发行版),我不能依赖 C++11 和更高版本(例如smart pointers)带来的功能,我坚持到目前为止,在扩展我的程序时混合了 C 和 Java 编程习惯。其中,我经常使用newdelete 的动态分配——这听起来当然是个问题。

为了方便未来的程序员维护我的代码,我想在我的代码中使用所述关键字来最小化动态分配。问题是我的程序必须管理一些用于(几乎)整个执行的相当大的数据结构。因此,我很难弄清楚为什么在这些情况下应该避免动态分配。

为简化起见,假设我有一个价值 10 兆字节的数据结构(建模为一个对象),它用于程序的整个执行,并且内存中的大小会随着时间的推移而增加。我的问题如下:

  • 在这种特殊情况下,使用new 动态分配对象仍然是一种不好的做法吗?有哪些更好的选择?

  • 1234563如果使用自动分配(如 here 所述),它是如何工作的?

非常感谢。

【问题讨论】:

  • 向量已经在语言中使用了很长一段时间,并且通常可以很好地替代new[]delete[]。标准库的许多部分仅在头文件中实现,这意味着一旦您构建它,它们就是您程序的一部分,并且不依赖于外部库。
  • 不幸的是,如果您生活在 C++03 世界中,您仍然会拥有 newdelete。不过,您可以将它们最小化。如果您需要集合,请使用向量而不是新建数组。您还可以编写自己的 RAII 智能指针,或查找已经这样做的库。 new 的大问题是您必须手动验证内存是否在每个控制路径中都已释放。这很痛苦,而且容易出错。以 RAII 方式隐藏资源分配和释放使您的代码不太可能出现这些错误。
  • 不是问题的答案,而是您问题的替代解决方案。有时,如果值得麻烦,您可以为旧平台构建开发链以支持更新的语言标准。例如,我们正在为旧版 linux 目标(由客户强制要求)维护一个完全兼容 c++11 的构建环境,其原生开发环境仅部分兼容 c++11。
  • @NathanOliver 只是为了确保(也许我应该在我的声明中提到vector;也许我稍后会编辑):假设我有一个价值几莫的数组(就是这种情况在实践中)。我将其替换为向量,而不使用动态分配(因此,在我的情况下,不使用new),它是另一个类的成员元素。如果我动态分配此类的一个实例,然后填充我的向量以在实践中与我以前的数组等效(同样,不使用元素的动态分配),所有内容都会存储在堆中,就像我手动管理数组一样?
  • @je 是的。这是一张非常漂亮的“图片”:stackoverflow.com/questions/55478523/…

标签: c++ memory-management dynamic-memory-allocation


【解决方案1】:

您可以尝试使用 boost 智能指针。

https://www.boost.org/doc/libs/1_71_0/libs/smart_ptr/doc/html/smart_ptr.html

它们与 C++ 11 智能指针非常相似,但可作为库提供,该库应在 C++ 11 之前的环境中工作。如果你决定走这条路,你可能还想看看这个

How to include only BOOST smart pointer codes into a project?

现在,我想知道您是否将新/删除函数与一般的动态分配混淆了。智能指针仍然是动态分配的,但是它们能够自行清理,这样您就不必记住了。这就是为什么它们优于使用 new/delete(malloc/free 等)的原因,它们导致内存泄漏的可能性要小得多。

当对象的生命周期允许时,自动分配很好,但如果您需要对象在声明它的函数之外持久存在,则需要动态分配内存。

【讨论】:

  • 对于我的情况,这看起来是个不错的选择。我不能保证我会把它作为主要答案,这取决于其他人可能会提供什么,但这对我来说仍然是一个很好的答案。
【解决方案2】:

在这种特定情况下,使用new 动态分配对象仍然是一种不好的做法吗?

是的,在您的代码中包含 new 很糟糕,主要是因为如果不调用 deletedelete[] 会导致内存泄漏,尤其是在遇到异常时。

什么是更好的选择?

编写智能指针并让它们自己调用new 或使用new 表达式调用:

myUniquePtr<myObj> obj_ptr(new myObj);

然后使用 RAII 让它们的析构函数调用 deletedelete[]

让你的智能指针以其他方式表现得像裸指针(重载operator-&gt; )。然后它将在您的代码中使用与原始指针相同的语法在您的代码中工作,而无需担心堆对象是否在其生命周期结束时被删除,无论代码可以采用什么路径。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-22
    • 2014-08-22
    • 1970-01-01
    • 2010-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多