在学习不同的语言时,我经常看到动态分配的对象,通常是Java和C#,如下所示:
functionCall(new className(initializers));
我知道这在内存管理语言中是完全合法的,但这种技术可以在C ++中使用而不会导致内存泄漏吗?
答案 0 :(得分:12)
你的代码是有效的(假设functionCall()实际上保证指针被删除),但是它很脆弱,并且会在大多数C ++程序员的头脑中发出警报。
您的代码存在多个问题:
functionCall(new className(initializers), new className(initializers));
想象一下第一个被成功分配,但第二个抛出异常(可能是内存不足,或者类构造函数引发异常)。函数调用永远不会被调用,不能释放内存。简单(但仍然很混乱)的解决方案是先分配内存,然后存储指针,然后将其释放到与声明相同的范围内(因此调用函数拥有内存):
className* p = new className(initializers);
functionCall(p);
delete p;
但这仍然是一团糟。如果functionCall抛出异常怎么办?那么p将不会被删除。除非我们在整个事情中添加一个try / catch,但是sheesh,那太乱了。 如果函数变得有点复杂,并且可能在functionCall之后但在删除之前返回怎么办?哎呀,内存泄漏。无法维持。错误的代码。
所以一个很好的解决方案是使用智能指针:
boost::shared_ptr<className> p = boost::shared_ptr<className>(new className(initializers));
functionCall(p);
现在处理内存的所有权。 shared_ptr
拥有内存,并保证它将被释放。当然,我们可以使用std::auto_ptr
,但shared_ptr
实现了您通常期望的语义。
请注意,我仍然在单独的行上分配内存,因为在进行函数调用的同一行上进行多次分配的问题仍然存在。其中一个人可能仍在扔,然后你就泄露了记忆。
智能指针通常是处理内存管理所需的绝对最小值。 但通常, nice 解决方案是编写自己的RAII类。
应该在堆栈上分配 className
,并在其构造函数中,使用new
进行哪些分配是必要的。在它的析构函数中,它应该释放那些记忆。这样,您可以确保不会发生内存泄漏,并且您可以使函数调用如此简单:
functionCall(className(initializers));
C ++标准库的工作原理如下。 std::vector
就是一个例子。您永远不会使用new
分配矢量。你在堆栈上分配它,让它在内部处理它的内存分配。
答案 1 :(得分:6)
是的,只要您释放函数内部的内存即可。但这绝不是C ++的最佳实践。
答案 2 :(得分:5)
取决于。
这将内存的“所有权”传递给functionCAll()。它需要释放对象或保存指针,以便以后可以释放它。传递像这样的原始指针的所有权是在代码中构建内存问题的最简单方法之一 - 泄漏或双重删除。
答案 3 :(得分:3)
在C ++中,我们不会像那样动态地创建内存 相反,您将创建一个临时堆栈对象。
如果您希望对象的生命周期大于对函数的调用,则只需要通过new创建堆对象。在这种情况下,您可以将new与智能指针结合使用(有关示例,请参阅其他答案)。
// No need for new or memory management just do this
functionCall(className(initializers));
// This assumes you can change the functionCall to somthing like this.
functionCall(className const& param)
{
<< Do Stuff >>
}
如果你想传递一个非const引用,那就这样做:
calssName tmp(initializers);
functionCall(tmp);
functionCall(className& param)
{
<< Do Stuff >>
}
答案 4 :(得分:1)
如果您调用的函数具有接受所有权语义,则是安全的。我不记得我需要这个时间,所以我认为这很不寻常。
如果函数以这种方式工作,它应该将其参数作为智能指针对象,以便意图清除;即。
void functionCall(std::auto_ptr<className> ptr);
而不是
void functionCall(className* ptr);
这使得所有权的转移是显式的,并且当函数的执行超出范围时,调用函数将处理ptr指向的内存。
答案 5 :(得分:0)
这适用于在堆栈上创建的对象,但不适用于C ++中的常规指针。
一个自动指针可能能够处理它,但我没有弄乱它们足以知道。
答案 6 :(得分:0)
通常,不,除非您想要泄漏内存。实际上,在大多数情况下,由于
的结果,这不起作用new T();
C ++中的是T *,而不是T(在C#中,new T()返回T)。
答案 7 :(得分:0)