我刚接触C++,我想知道为什么我需要使用new和delete?这可能会引起问题(内存泄漏),我不明白为什么我不能只是使用初始化变量而不用new运算符。有人能向我解释一下吗?我很难在Google上找到针对这个特定问题的答案。
我刚接触C++,我想知道为什么我需要使用new和delete?这可能会引起问题(内存泄漏),我不明白为什么我不能只是使用初始化变量而不用new运算符。有人能向我解释一下吗?我很难在Google上找到针对这个特定问题的答案。
出于历史和效率原因,C++(以及C)的内存管理是显式和手动的。
有时候,您可能会在调用堆栈上分配内存(例如使用VLA或alloca(3))。然而,这并不总是可能的,因为
new
和delete
(例如通过使用标准容器和智能指针)。
请注意,在管理内存时最困难的情况是任意的、可能是循环的引用图。
std :: unique_ptr
不同,std :: shared_ptr
并不真正实现RAII;它实现了一种非常特定的生命周期管理策略,这只有在某些情况下才是合适的。 - James Kanze在许多情况下,其实不需要使用new
和delete
,可以只使用标准容器,把分配/释放管理交给它们。
你可能需要显式地使用分配的原因之一是对于需要重视身份的对象(即它们不仅仅是可以复制的值)。
例如,如果你有一个 GUI “窗口”对象,那么复制它可能没有意义,因此你基本上排除了所有标准容器(它们设计用于可以复制和赋值的对象)。在这种情况下,如果对象需要在创建它的函数之后继续存在,则最简单的解决方案可能就是将其显式地分配到堆上,可能使用智能指针以避免泄漏或删除后使用。
在其他情况下,可能很重要避免复制,不是因为复制是非法的,而是因为效率不高(大对象),显式处理实例生命周期可能是更好(更快)的解决方案。
另一种可能最佳的显式分配/释放选项是复杂数据结构,它们无法由标准库表示(例如每个节点也是双向链表的树)。
new
和delete
,这并不是因为堆栈/自动存储足够使用,而是因为RAII智能资源所有者(无论是容器、共享指针还是其他什么)几乎使所有直接内存操作都变得不必要。由于内存管理问题往往容易出错,这使得你的代码更加健壮、易于阅读,并且有时会更快(因为高级资源所有者可以使用你可能不会在所有地方都费心使用的技术)。make_unique
)来摆脱调用new
的倒数第二个借口。new
和delete
(通常是delete this
,因为对象本身会对事件做出反应)确实是唯一的方法。 - James Kanzeunique_ptr
中。不再需要时使用 .reset()
。这清楚地表明了哪个指针拥有,使泄漏几乎不可能(包含对象必须泄漏才能泄漏资源,如果递归应用,则包含对象未泄漏),并且几乎没有开销(仅在销毁和清零时可能进行冗余检查,如果编译器无法证明它是冗余的)。这可能不是您的风格,但我并没有声称每种风格都是正确的。 - Yakk - Adam Nevraumontshared_ptr
经常是泄漏的源头(因为存在循环引用)。而且_没有_指针是所有权的。否则:泄漏非常非常罕见,因为我们只动态分配需要程序逻辑定义生命周期的东西。在很大程度上,动态分配的对象是自包含的,并且根本没有“所有者”,除非是暂时的(例如通过交易)。 - James Kanzeunique_ptr
,而不是 shared_ptr
。它们都是智能指针,因为它们像指针一样运作并管理生命周期,但是它们在其他方面非常不同。shared_ptr
有开销,并且通常使得关于生命周期的推理更加困难。unique_ptr
几乎没有开销,并且使得关于生命周期的推理更加容易。混淆这两者意味着你应该更多地尝试使用 unique_ptr
:我永远不会推荐全面地使用 shared_ptr
。 - Yakk - Adam Nevraumont首先,如果你不需要动态分配内存,就不要使用它。
最常需要动态分配内存的原因是对象的生命周期由程序逻辑而非词法作用域决定。 new
和 delete
运算符旨在支持显式管理的生命周期。
另一个常见原因是“对象”的大小或结构在运行时确定。 对于简单情况(如数组等),有标准类(std::vector
)可以为您处理,但对于更复杂的结构(例如图和树),您必须自己处理。(通常的技术是创建一个表示图或树的类,并让它管理内存。)
还有一种情况,即对象必须是多态的,实际类型直到运行时才知道。(在最简单的情况下,有一些棘手的方法可以处理此问题,而无需动态分配内存,但通常情况下,您需要动态分配内存。)在这种情况下,可能需要使用 std::unique_ptr
来处理 delete
,或者如果对象必须共享,则使用 std::shared_ptr
(尽管通常必须共享的对象属于上述第一类,因此智能指针并不合适)。
可能还有其他原因,但这些是我最常遇到的三个原因。
只有在简单的程序中,您才能预先知道需要使用多少内存。通常情况下,您无法预见需要使用多少内存。
然而,使用现代C++11,您通常依赖于标准库,如vector
和map
进行内存分配,并且使用智能指针可以帮助您避免内存泄漏,因此您不需要手动显式地使用new
和delete
。
static
对象来解决这个问题。但是,通过void*
传递的方式需要小心谨慎。常见的做法是将回调函数强制转换为Base*
,以便调用虚成员函数;如果被调用者直接将new Derived
传递给void*
,则会导致未定义的行为。 - James Kanzepthread_create
的情况下,这显然会带来问题。(实际上,你可以通过使用pthread_cond_t
来解决这个问题,只有在子线程将值复制到本地变量后才能在起始线程中前进。例如,Boost线程就是这样做的,以确保您可能已经传递给构造函数的任何临时对象的生命周期。) - James Kanze