C++:是否绝对有必要手动分配内存

C++: Is it ever absolutely necessary to allocate memory manually?

本文关键字:分配 内存 是否 C++      更新时间:2023-10-16

我对C++编程的一些更高级的方面相对较新,我很难理解是否真的有必要在C++中分配内存(无论是通过malloc,new等(。例如,在 C 中,我知道您需要分配内存才能拥有动态大小的数组或其他任务。C++,在我看来并非如此,你可以使用 std::vector、std::string 或其他已经通过设计动态调整大小的内置方法。我也明白访问分配的内存比堆栈慢。

因此,鉴于此,是否有时间必须C++分配内存,如果是这样,其中一个时间的示例是什么?当然,这不包括您的C++代码必须与 C 程序交互的时间。让我们假设程序纯粹是用C++编写的。

编辑:为了减轻混乱,我知道向量和其他结构正在分配自己的内存,但这是幕后发生的事情,不需要程序员使用new,malloc等,它会自动清理。所以我真正想知道的是,是否有必要在 C++

反映您的编辑:事实上,它并不是真正需要的,或者更确切地说,它通常被抽象出来,就像vectorstring一样。您有各种容器,例如vector可以为您处理这些容器。当您设计自己的类时,我们鼓励您使用资源分配即初始化 (RAII( 技术,该技术抽象出典型的手动内存管理。事实上,在某些情况下,尤其是在处理 C 代码时,我们鼓励您在使用 RAII 的 C++ 类包装器中或使用C++智能指针(如 C++11:shared_ptrunique_ptr(它们本身使用 RAII(中引入的那些(来管理该内存。

例如,在 C 中,我知道您需要分配内存才能拥有动态大小的数组或其他任务。在C++中,在我看来并非如此,您可以使用 std::vector 或其他已经通过设计动态调整大小的内置方法。

不过,std::vector并不是建立在魔法和仙尘之上的:它在内部分配记忆。

您是对的,C++您很少需要手动分配内存。在某些情况下,这是最简单的方法,尽管1.关键是C++使得手动释放完全没有必要,因为析构函数会处理这个问题。


1 非常非常罕见。 大多数写得很好的代码根本不需要这个。在处理较低级别的详细信息时(例如,在实现容器时(,它偶尔很有用。

如果你的问题是,">动态分配的内存在C++中是不必要的吗?答案是,这是非常必要的,而且非常重要。

如果您的问题是,">使用现代 C++11 类和功能,我的代码多久需要我手动使用'new'和'delete'">

答案是"很少"。大多数新建和删除调用将隐藏在容器(std::vector,std::map等(和智能指针(stdshared_ptrstd::unique_ptr(中,并通过对std::make_shared((等函数的调用。

在极少数情况下,您是否需要手动调用"新建"和"删除"?这取决于您正在制作的程序类型,但如果它们是视频游戏,我会说是的,可能是 1% 的时间(我的经验,但 YMMV(,您需要使用手动 new 并通过 std::make_shared(( 和 std:shared_ptr 删除 - 尽管同样,这取决于项目。

一般来说,首选在堆栈上分配的局部变量和成员变量(忽略它们是否在内部分配动态内存(,接下来更喜欢 C++11 托管动态内存(智能指针(,最后诉诸 new删除作为最后的手段,如果性能需要它(但不要预先优化 - 分析并找到真正的瓶颈(。

请注意,仅仅因为您使用智能指针管理内存,并不意味着您应该禁止代码中的所有原始指针 - 即使您的所有内存都在管理中,与内存生命周期管理无关的原始指针仍然非常实际。

正如你所观察到的,永远没有必要将new与标准容器一起使用(vectormap等(。

使用智能指针时,使用make_shared总是比new shared_ptr更好;因为unique_ptr缺少make_unique是一个缺陷(make_unique和完美的转发(,应该尽快解决;复制粘贴和使用make_unique比自己new unique_ptr更好, 出于清晰和异常安全的原因。

如果您正在编写自己的容器,请在可能的情况下尝试编写标准库工具(现有容器和智能指针(,这样您就不必担心生存期管理。

有一个标准工具需要使用new;默认情况下,采用Facet *std::locale的模板化构造函数期望由new分配Facet *,因为一旦它不再被std::locale实例使用,它就会被delete static_cast<locale::facet*>(f)处置。 (请注意,std::locale::facet有一个虚拟析构函数。 如果这让您感到不舒服,您可以通过在构造分面时将1作为refs参数传递来安排自己管理分面的生命周期:在区域设置(std::locale(中拥有/删除分面。

你不需要自己。我一直在做下面这样的事情

void foo(MyStruct&v) { v.bar=1234 }
vector<MyStruct> v;
v.push_back(...);

本质上,单个值在堆栈上,而集合在堆栈上,但将所有内容放在一个堆中。我从不在大多数应用程序中使用新的关键字或指针。