什么时候应该在C ++中使用new运算符

时间:2011-02-22 19:08:15

标签: c++ new-operator

假设我有一个名为Money的课程,其中包含参数DollarsCents

我可以通过以下两种方式初始化它:

  1. 钱a(3,15);
  2. Money * b = new Money(3,15);
  3. 我的问题是我何时应该使用(1)以及何时应该使用(2)

9 个答案:

答案 0 :(得分:8)

尽可能使用1,必要时使用2。 “当你必须”时基本上转换为“当你创建一个生命周期不是/不能与”范围“联系在一起的对象时 - 也就是说,它必须在创建它的函数退出后保持存在。你通常想要避免这种情况,例如通过返回有问题的对象的副本,而不是在函数返回后使该对象(本身)持续。

过去,(不幸的是)没有真正坚硬的指导方针可以确保你做得尽可能好。

答案 1 :(得分:6)

第一个在堆栈上创建一个Money对象,其生命周期在创建时的范围内。当你点击}时它意味着超出范围并返回内存。如果要在一个函数中创建对象,请使用此选项。

第二个在堆上创建一个Money对象,它的生命周期与你想要的一样长,即直到你delete为止。当您希望将对象传递给不同的函数时使用此

答案 2 :(得分:3)

Money a(3,15);

在本地范围内分配Money个对象。

Money* b=new Money(3,15);

将指针变量分配给本地作用域,并使指针“指向”驻留在免费存储中的Money对象(假设分配成功完成,否则抛出std::bad_alloc()

示例1:

假设下一个场景:

Money * b = initialize();

,其中

Money* initialize()
{
      Money x(2 , 15);
      return &x;
}

这将失败,因为在initialize()到达执行结束后x被销毁,现在b指向一个无效的位置,如果你使用了,则会调用Undefined Behavior它。所以你应该用指针

分配它
Money* initialize()
{
      return new Money(2,15);
}

当您想要存储和使用大小的数组时,也会使用免费商店。

正如您在示例中注意到的那样,两者之间存在差异,那就是在本地范围x中您不需要delete该对象。但是在使用new时,您必须手动执行delete x;。否则会发生内存泄漏(占用内存空间而不再使用,因此会占用内存)。

除了这篇文章之外,请参阅Martin York对更深层次知识的回答。

答案 3 :(得分:2)

这是一个比它看起来要复杂得多的问题。简单的答案是

1)当你想要堆栈存储和作用域绑定资源管理时,当保留作用域时,将调用该对象上的析构函数并弹出堆栈中的存储。

注意不要将指针传递给调用堆栈中的其中一个作用域绑定对象(返回,输出参数),这是一种简单的段错误方法。

2)当您希望在免费商店中分配对象时,必须删除此指针,否则将发生内存泄漏。

看看shared_ptr,scoped_ptr,auto_ptr等。对于一些使#2在某些方面起作用的替代方案,如#1。

另外,请查看this question以获取有关C ++中内存管理的一些指示。

答案 4 :(得分:1)

从技术上讲,你更喜欢你从未做过(2)直接但更喜欢使用智能指针:

std::auto_ptr<Money> b(new Money(3,15));  // auto_ptr is just an example of a smart pointer

但总体问题仍然存在 当对象的生命周期不超过使用它的函数或对象时,使用(1)。当对象的生命周期延长的时间长于编译时预测的时间时,请使用(2)。

  1. 被称为自动存储持续时间对象。这意味着它会被编译器生成的代码自动创建和销毁(重要位)。

  2. 被称为动态存储持续时间对象。这意味着您有责任手动创建和销毁对象。销毁对象需要我们维护与对象关联的所有权概念,并且只允许所有者销毁它(否则我们会有多个源试图销毁对象)。为了帮助进行所有权跟踪,我们引入了拥有指针的智能指针。然后它成为智能指针的责任来完成破坏对象的实际工作。这使得使用指针构建类和函数变得更加容易。

  3. 如果你的对象很便宜,可以创建一个副本(它看起来像是这样)。然后你几乎不需要动态创建对象。将对象传递给函数或返回结果都可以非常正常地完成:

    Money CalcInterest(Money const& m, double interest)
    {
        Money result(m.doallas * interest, m.cent * interest);
        return result; // Return a copy quite happily.
    }
    

    如果你正在构建一个动态表达式,那么你可以使用智能指针来保存指针。

    struct Expression
    {
        char   op;
        std::auto_ptr<Money>   lhs;
        std::auto_ptr<Money>   rhs;
    };
    
    std::auto_ptr<Expression> getExpressionFromUserInput()
    {
         std::auto_ptr<Expression>  result(new Expressions(/* etc */);
         return result;
    }
    

答案 5 :(得分:1)

表格1最简单;尽可能使用它。

表单2会为您购买以下内容:

  • 能够在运行时确定是否要创建对象。
  • 能够在运行时确定要创建的这些对象的数组大小
  • 能够在运行时确定要创建的子类(例如Money* b应指向GoodMoney还是BadMoney
  • 能够在运行时确定对象的生命周期

表单2引入了可能性或资源泄漏,因为使用new创建的对象最终必须使用delete销毁。正如其他人所说,使用智能指针可以消除或减轻这个问题。

简而言之,当您需要上面列出的内容之一时,请使用表单2,然后将其放入智能指针中;否则使用表格1。

答案 6 :(得分:0)

完全不同。

  1. 您有一个在堆栈上构建的对象。它的生命范围将持续到代码块。

  2. 您在堆中分配的某个内存地址处初始化了一个对象。在致电delete b之前,它不会被销毁。

答案 7 :(得分:0)

通常,当对象具有有限的生命周期(在块的上下文中)时,您将使用表单1;当对象必须在声明的块中存活时,使用表单2.让我举几个例子:

int someFunc() {
    Money a(3,15);
    ...
} // At this point, Money is destroyed and memory is freed.

或者,如果你想让对象在函数中存活,你可以使用new,如下所示:

Money *someOtherFunc() {
    Money *a = new Money(3,15);
    ....
    return a;
} // we have to return a here or delete it before the return or we leak that memory.

希望这有帮助。

答案 8 :(得分:-3)

如果需要指向对象的指针,则应使用选项2;如果需要值,则应使用选项1。