是Foo * f =新的Foo好的C ++代码

时间:2010-01-11 06:07:44

标签: c++ memory-management memory-leaks

通过旧的C ++期刊阅读,我注意到了一些事情。

其中一篇文章断言

Foo *f = new Foo();

几乎是不可接受的专业C ++代码,并且自动内存管理解决方案是合适的。

是这样吗?

编辑:改述:直接内存管理对于新的C ++代码是不可接受的,在一般中? auto_ptr(或其他管理包装器)应该用于大多数新代码吗?

10 个答案:

答案 0 :(得分:19)

这个例子非常像Java 在C ++中,如果需要,我们只使用动态内存管理 一个更好的选择就是声明一个局部变量。

{
    Foo    f;

    // use f

} // f goes out of scope and is immediately destroyed here.

如果必须使用动态内存,请使用智能指针。

// In C++14
{
    std::unique_ptr<Foo>  f = std::make_unique<Foo>(); // no need for new anymore
}

// In C++11
{
    std::unique_ptr<Foo>  f(new Foo);  // See Description below.
}

// In C++03
{
    std::auto_ptr<Foo>    f(new Foo);  // the smart pointer f owns the pointer.
                                       // At some point f may give up ownership to another
                                       // object. If not then f will automatically delete
                                       // the pointer when it goes out of scope..

}

有一大堆os智能指针提供给int std ::和boost ::(现在有些在std :: tr1中)选择合适的一个并用它来管理对象的生命周期。

请参阅Smart Pointers: Or who owns you baby?

从技术上讲,您可以使用new / delete进行内存管理 但在真正的C ++代码中,它几乎从未完成。手动进行内存管理几乎总有一种更好的选择。

一个简单的例子是std :: vector。在封面下它使用new和delete。但是你永远无法从外面说出来。这对班级用户完全透明。用户知道的所有内容都是向量将获取对象的所有权,并且在向量被销毁时它将被销毁。

答案 1 :(得分:8)

我认为,所有这些“...最佳实践......”问题的问题在于,他们都在没有上下文的情况下考虑代码。如果你问“一般”,我必须承认直接内存管理是完全可以接受的。它在语法上是合法的,并且不违反任何语言语义。

对于替代方案(堆栈变量,智能指针等),它们都有其缺点。而且他们都没有灵活性,直接内存管理也有。您需要为这种灵活性支付的价格是您的调试时间,您应该了解所有风险。

答案 2 :(得分:7)

没有

在某些情况下,有充分的理由不使用自动内存管理系统。 这些可能是由于周期性参考等导致的性能,数据结构的复杂性等。

但是,如果你有充分的理由不使用更聪明的东西,我建议只使用带有new / malloc的原始poiner。看到无保护的分配让我害怕,让我希望编码员知道他们在做什么。

像boost :: shared_ptr这样的智能指针类,boost :: scoped_ptr将是一个好的开始。 (这些将是C ++ 0x标准的一部分,所以不要害怕它们;))

答案 3 :(得分:6)

使用某种智能指针方案,您可以获得自动内存管理,引用计数等,只需很少的开销。你为此付出了代价(内存或性能),但为它支付费用可能是值得的,而不是一直担心它。

答案 4 :(得分:6)

如果您使用的是异常,那么实际上可以保证某种代码会导致资源泄漏。即使您禁用了异常,在手动将新配置与删除配对时也很容易进行清理。

答案 5 :(得分:4)

这完全取决于我们的意思。

  • 永远不会用new来分配内存吗?当然应该,我们别无选择。 new 在C ++中动态分配对象的方法。当我们需要动态分配类型为T的对象时,我们会new T(...)
  • 当我们想要实例化一个新对象时,默认情况下应该new调用 吗? 即可。在java或C#中,new用于创建新对象,因此您可以在任何地方使用它。在C ++中,它仅用于堆分配。几乎所有对象都应该进行堆栈分配(或作为类成员就地创建),以便语言的作用域规则帮助我们管理它们的生命周期。 new通常不是必需的。通常,当我们想要在堆上分配新对象时,您可以将其作为较大集合的一部分进行操作,在这种情况下,您应该将对象推送到STL容器中,并让它担心分配和释放内存。如果您只需要一个对象,通常可以将其创建为类成员或局部变量,而不使用new
  • 您的业务逻辑代码中是否应该new?很少,如果有的话。如上所述,它通常可以并且通常应该隐藏在包装类中。例如std::vector动态分配所需的内存。因此vector用户无需关心。我只是在堆栈上创建一个向量,它为我处理堆分配。当向量或其他容器类不合适时,我们可能想要编写自己的RAII包装器,它在构造函数中使用new分配一些内存,并在析构函数中释放它。然后该包装器可以进行堆栈分配,因此该类的用户永远不必调用new
  

其中一篇文章声称Foo *f = new Foo();几乎是不可接受的专业C ++代码,并且自动内存管理解决方案是合适的。

如果他们的意思是我认为他们的意思,那么他们是对的。正如我上面所说,new通常应隐藏在包装类中,其中自动内存管理(以作用域生命周期的形式和在超出范围时调用其析构函数的对象)可以为您处理它。本文没有说“永远不会在堆上分配任何东西”或者从不使用new“,而只是”当你使用new时,不要只存储指向已分配内存的指针。将它放在某种类中,当它超出范围时可以将它释放出来。

而不是Foo *f = new Foo();,您应该使用其中之一:

Scoped_Foo f; // just create a wrapper which *internally* allocates what it needs on the heap and frees it when it goes out of scope
shared_ptr<Foo> f = new Foo(); // if you *do* need to dynamically allocate an object, place the resulting pointer inside a smart pointer of some sort. Depending on circumstances, scoped_ptr, or auto_ptr may be preferable. Or in C++0x, unique_ptr
std::vector<Foo> v; v.push_back(Foo()); // place the object in a vector or another container, and let that worry about memory allocations.

答案 6 :(得分:2)

我不久前停止编写这样的代码。有几种选择:

基于范围的删除

{
    Foo foo;
    // done with foo, release
}

scoped_ptr用于基于范围的动态分配

{
    scoped_ptr<Foo> foo( new Foo() );
    // done with foo, release
}

shared_ptr表示应该在很多地方处理的事情

shared_ptr<Foo> foo;
{ 
    foo.reset( new Foo() );
} 
// still alive
shared_ptr<Foo> bar = foo; // pointer copy
...
foo.reset(); // Foo still lives via bar
bar.reset(); // released

基于工厂的资源管理

Foo* foo = fooFactory.build();
...
fooFactory.release( foo ); // or it will be 
                           // automatically released 
                           // on factory destruction

答案 7 :(得分:1)

一般来说,没有,但一般情况并非常见。这就是为什么像RAII这样的自动方案首先被发明的原因。

从我写的答案到另一个问题:

  

程序员的工作就是表达   用他的语言优雅的东西   选择。

     

C ++具有非常好的语义   建设和破坏   堆栈上的对象。如果是资源   可以在a的持续时间内分配   范围块,然后一个优秀的程序员   可能会走最少的路   抵抗性。对象的生命周期是   由大括号划分,可能是   反正已经存在了。

     

如果没有好办法的话   对象直接在堆栈上,也许吧   可以放在另一个对象中作为   会员。现在它的寿命有点   更长,但C ++仍然很多   自动。对象的生命周期   由父对象分隔 -   问题已被委派。

     

但可能没有一位家长。   接下来最好的事情是一系列的   领养父母。这是什么   auto_ptr是为了。还不错,   因为程序员应该知道   什么特定的父母是所有者。   对象的生命周期由分隔   它的序列的生命周期   拥有者。迈进了一步   决定论和本身的优雅是   shared_ptr:生命分隔   所有者联盟。

     

<强>&GT;但也许这个资源不是   与任何其他对象并发设置   对象,或控制流程   系统。它是在某些事件中创建的   发生并摧毁另一个人   事件。虽然有很多   用于划分生命周期的工具   代表团和其他生命,他们   不足以计算任何   任意功能。所以程序员   可能决定写一个函数   几个变量来判断是否   一个物体正在形成或存在   消失,并致电new和   delete

     

最后,编写函数可以   硬。也许管理的规则   对象需要花费太多时间   内存实际计算!它   可能只是很难表达   他们优雅地回到我的身边   原点。所以我们有   垃圾收集:对象   生命是由你想要的时间界定的   它,当你没有。

答案 8 :(得分:0)

首先,我认为它应该是Foo *f = new Foo();

我之所以不喜欢使用这种语法,是因为很容易忘记在代码末尾添加delete并让你的记忆保持畅通。

答案 9 :(得分:0)

通常,您的示例不是异常安全的,因此不应使用。如果该线直接跟随新抛出?堆栈展开,你刚刚泄露内存。作为堆栈展开的一部分,智能指针将为您处理它。如果您倾向于不处理异常,那么在RAII问题之外没有任何退缩。