我对C ++编程的一些更高级的方面相对较新,而且我很难理解是否真的有必要在C ++中分配内存(无论是通过malloc,new等)。例如在C中,我理解您需要分配内存以具有动态大小的数组或其他任务。在C ++中,在我看来并非如此,你可以使用std :: vector,std :: string或其他已经按设计动态调整大小的内置方法。我也明白访问分配的内存比堆栈慢。
因此,有时你必须在C ++中分配内存,如果是这样,那么其中一个时代的例子是什么?这当然不包括C ++代码必须与C程序交互的时间。我们假设该程序纯粹是用C ++编写的。
编辑:为了减轻混乱,我理解向量和其他结构正在分配自己的内存,但这是在幕后发生的事情,并且不需要程序员使用new,malloc等,并且它被清理自动启动。所以我真正想知道的是,是否有必要在C ++中手动执行内存管理答案 0 :(得分:8)
堆栈的大小有限,有些东西不能可靠地适应它。动态分配的内存也具有动态特性,有时您不确定在执行程序的某个时刻之前您需要多少个对象或元素。
查看this question,它可以很好地描述每个可能的用例:
当您希望比上述更灵活时,堆分配(动态分配的内存)非常有用。通常,调用函数来响应事件(用户单击“创建框”按钮)。正确的响应可能需要分配一个新对象(一个新的Box对象),该对象应该在函数退出后很长时间内保持不变,因此它不能在堆栈中。但是你不知道在程序开始时你想要多少个盒子,所以它不能是静态的。
反映您的编辑:事实上,它并非真正需要,或者更确切地说,它通常被抽象出来,就像vector
和string
一样。您有各种容器,如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
与标准容器(vector
,map
等一起使用。)
使用智能指针时,最好使用make_shared
而不是new
进入shared_ptr
;对于unique_ptr
,make_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(...);
基本上单个值在堆栈上,而集合在堆栈上但将所有内容放在堆中。我不会在我的大多数应用程序中使用新的关键字或指针。