8

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

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

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

4

5 回答 5

8

堆栈的大小有限,有些东西不能可靠地放入其中。动态分配的内存也具有动态特性,有时在程序执行的某个时刻之前,您不确定数组中需要多少对象或元素。

看看这个问题,它很好地描述了每个可能的用例:

当您想要比上述更灵活时,堆分配(动态分配的内存)很有用。通常,调用函数来响应事件(用户单击“创建框”按钮)。正确的响应可能需要分配一个新对象(一个新的 Box 对象),该对象在函数退出后应该保留很长时间,因此它不能在堆栈上。但是你不知道在程序开始时你想要多少个盒子,所以它不能是静态的。

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

于 2013-03-17T23:50:21.450 回答
7

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

std::vector但是,它不是建立在魔法和仙尘之上的:它在内部分配内存。

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


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

于 2013-03-17T23:56:43.340 回答
6

如果您的问题是,“ C++ 中是否不需要动态分配的内存? ”答案是非常必要且非常重要。

如果您的问题是“使用现代 C++11 类和功能,我的代码需要多久手动使用‘ new ’和‘ delete ”?

答案是“很少”。大多数 new 和 delete 调用将隐藏在容器(std::vectorstd::map等)和智能指针(std::shared_ptrstd::unique_ptr)中,并通过调用std:: :make_shared()

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

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

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

于 2013-03-18T00:33:12.477 回答
3

正如您所观察到的,永远不需要new与标准容器(vector,map等)一起使用。

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

如果您正在编写自己的容器,请尽可能尝试编写标准库设施(现有容器和智能指针),这样您就不必担心生命周期管理。

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

于 2013-03-18T11:15:29.193 回答
1

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

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

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

于 2013-03-18T00:09:02.100 回答