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

时间:2013-03-17 23:49:15

标签: c++ memory memory-management

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

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

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

5 个答案:

答案 0 :(得分:8)

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

查看this question,它可以很好地描述每个可能的用例:

  

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

反映您的编辑:事实上,它并非真正需要,或者更确切地说,它通常被抽象出来,就像vectorstring一样。您有各种容器,如vector,可以为您处理。当您设计自己的类时,我们鼓励您使用Resource Allocation is Initialization(RAII)技术来抽象出典型的手动内存管理。事实上,在某些情况下,尤其是在处理C代码时,我们鼓励您在使用RAII的C ++类包装器或C ++ 11中引入的C ++智能指针管理该内存:{{3} }和shared_ptr(他们自己使用RAII)。

答案 1 :(得分:7)

  

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

std::vector并非建立在魔法和仙尘之上:它在内部分配内存。

你在C ++ you rarely need to allocate memory manually中是对的。有些情况下,这是 1 最简单的方法。关键是C ++使手动 deallocation 完全不必要,因为析构函数会处理这个问题。


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

答案 2 :(得分:6)

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

如果您的问题是“使用现代C ++ 11类和功能,我的代码需要多长时间才能手动使用''和'删除 的“?

答案是“不经常”。大多数新调用和删除调用都将隐藏在容器中( std :: vector std :: map ,et al)和智能指针( std :: shared_ptr std :: unique_ptr ),并通过调用 std :: make_shared()等函数/强>

在极少数情况下,您是否需要手动调用“新”和“删除”?这取决于你正在制作的节目类型,但如果它们是电子游戏,我会说是的,可能是1%的时间(我的体验,但是YMMV),你需要使用手册 删除 而不是 std :: make_shared() std :: shared_ptr - 尽管如此,它还取决于项目。

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

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

答案 3 :(得分:3)

正如您所观察到的,永远不必将new与标准容器(vectormap等一起使用。)

使用智能指针时,最好使用make_shared而不是new进入shared_ptr;对于unique_ptrmake_unique的缺失是一个缺陷(make_unique and perfect forwarding),应尽快解决;为了清晰和异常安全的原因,最好自己复制粘贴并使用make_unique而不是new加入unique_ptr

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

有一个标准设施,预计会使用new; std::locale的模板化构造函数默认情况下Facet *需要Facet *分配new,因为一旦不再使用它将被delete static_cast<locale::facet*>(f)处理通过std::locale个实例。 (请注意,std::locale::facet有一个虚拟析构函数。)如果这让您感到不舒服,可以通过在构造构面时将1作为refs参数来安排自己管理构面的生命周期: Ownership/delete'ing the facet in a locale (std::locale)

答案 4 :(得分:1)

你不需要自己。我总是做类似下面的事情

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

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