10

据我所知,new运营商做了以下事情:(如果我错了,请纠正我。)

  1. 分配内存,然后返回分配内存的第一个块的引用。(显然,内存是从堆中分配的。)
  2. 初始化对象(调用构造函数。)

运算符也new[]以类似的方式工作,只是它对数组中的每个元素都这样做。

谁能告诉我这两个运算符在 C++ 和 Java 中有何不同:

  1. 就他们的生命周期而言。
  2. 如果他们无法分配内存怎么办。
4

5 回答 5

12
  • 在 C++ 中,T * p = new T;...
  1. 为类型的对象分配足够的内存T

  2. 在该内存中构造一个类型的对象T,可能会对其进行初始化,并且

  3. 返回一个指向对象的指针。(指针与标准分配的内存地址具有相同的值new,但数组形式不必如此new[]。)

如果内存分配失败,std::bad_alloc则抛出类型异常,不构造对象,也不分配内存。

如果对象构造函数抛出异常,则(显然)不构造任何对象,立即自动释放内存,并传播异常。

否则,一个动态分配的对象已经被构建,用户必须手动销毁该对象并释放内存,通常说delete p;.

实际的分配和释放功能可以在 C++ 中控制。如果没有其他内容,::operator new()则使用全局的预定义函数,但这可以由用户替换;如果存在静态成员函数 T::operator new,则将使用该静态成员函数。

  • 在 Java 中它非常相似,只是 的返回值new可以绑定到 Java 类型的变量T(或其基类,例如Object),并且您必须始终有一个初始化程序(所以您会说T x = new T();)。对象的生命周期是不确定的,但保证至少只要任何变量仍然引用该对象,并且没有办法(也没有必要)手动销毁该对象。Java 没有明确的内存概念,您无法控制分配的内部。

此外,C++ 允许许多不同形式的new表达式(所谓的放置形式)。它们都创建了必须手动销毁的动态存储对象,但它们可以是相当随意的。据我所知,Java 没有这样的设施。


最大的区别可能在于使用方面:在 Java 中,您一直使用new所有时间,而且您必须这样做,因为它是创建(类类型)对象的唯一方法。相比之下,在 C++ 中,您几乎不应该new在用户代码中使用裸 s。C++ 具有不受约束的变量,因此变量本身可以是对象,这就是 C++ 中通常使用对象的方式。

于 2013-06-09T22:33:15.520 回答
2

在您的“声明”中,我不认为“返回对分配的第一个内存块的引用是完全正确的。new返回一个指针(指向分配的对象的类型)。这与引用略有不同,尽管在概念上相似.

回答您的问题:

  1. 在 C++ 中,一个对象保留在内存中(见注),直到它被显式删除deleteor delete [](并且您必须使用与您分配的内容匹配的对象,所以 a ,尽管它与无法删除new int[1];的内存量相同(反之亦然,不能用于 a )。在 Java 中,一旦“没有对内存的引用”,内存就会在将来的某个时候被垃圾收集器释放。new int;deletedelete []new int
  2. 两者都抛出异常(C++ 抛出异常std::bad_alloc,Java 类似于 OutOfMemoryError),但在 C++ 中您可以使用new(std::nothrow) ...,在这种情况下,new如果没有足够的可用内存来满足调用,则返回 NULL。

注意:根据评论,技术上可以在不释放内存的情况下“销毁”对象。这是一个相当不寻常的情况,除非您对 C++ 非常有经验并且您有充分的理由这样做,否则您不应该这样做。典型的用例是在删除操作符内部,对应于新的位置(new使用已经存在的内存地址调用该位置以执行对象的构造)。同样,placement new 是 new 的非常特殊的用法,而不是您可以期望在普通 C++ 代码中看到的大部分内容。

于 2013-06-09T22:33:53.797 回答
2

我不知道 Java 中的细节,但这里是C++中的内容new和操作:new[]

  1. 分配内存

    当你有一个表达式new Tornew T(args)时,编译器会决定调用哪个函数来获取内存

    • 如果该类型T有一个适当的成员operator new,则调用该成员
    • 否则,如果用户提供了适当的全局operator new,则调用该全局。

      如果operator new无法分配请求的内存,则它会调用一个新的处理函数,您可以使用set_new_handler. 该函数可能会释放一些空间以便分配成功,它可能会终止程序,或者它可能会引发类型异常std::bad_alloc或派生异常。默认的新处理程序只是抛出std::bad_alloc.

      new T[n]除了operator new[]调用内存分配之外,也会发生同样的情况。

  2. 分别构造对象。新分配的内存中的对象。

    对象的new T(args)相应构造函数被调用。如果构造函数抛出异常,则通过调用相应的方法释放内存operator delete(可以在与 相同的地方找到operator new

    因为new T它取决于是否T是 POD(即内置类型或基本上是 C 结构/联合)。如果 T 是 POD,则什么也不会发生,否则将被视为new T()

    因为new T[n]它还取决于是否TPOD。同样,POD 未初始化。对于非 POD,依次为每个对象调用默认构造函数。如果一个对象的默认构造函数抛出,则不会调用其他构造函数,但已经构造的对象(不包括构造函数刚刚抛出的对象)会以相反的顺序被破坏(即调用析构函数)。然后使用适当的operator delete[].

  3. 返回指向新创建对象的指针。请注意,new[]指针可能不会指向已分配内存的开头,因为在构造对象之前可能会有一些关于已分配对象数量的信息,这些信息用于delete[]确定要销毁的对象数量。

在所有情况下,对象都会一直存在,直到它们被销毁delete ptr(对于使用 normal 分配的对象new)或delete[] ptr(对于使用 array 创建的对象new T[n])。除非添加了第三方库,否则 C++ 中没有垃圾收集。

请注意,您也可以直接调用operator newandoperator delete来分配原始内存。operator new[]和也是如此operator delete[]。但是请注意,即使对于那些低级函数,您也不能混合调用,例如通过使用分配的内存来释放operator delete内存operator new[]

您还可以使用所谓的placement new 在分配的内存中构造一个对象(无论您是如何获得的)。这是通过将指向原始内存的指针作为参数提供给 来完成的,如下所示newnew(pMem) T(args)要析构这样一个显式构造的对象,可以直接调用对象的析构函数p->~T()

通过调用operator new将指针作为附加参数并仅返回它的 an 来放置新作品。同样的机制也可用于为operator new采用相应附加参数的重载提供其他信息。但是,虽然您可以定义相应operator delete的 ,但这些仅用于在对象在构造过程中引发异常时进行清理。没有“放置删除”语法。

C++ 已经提供的placement new 语法的另一种用途不是新的。那一个接受一个额外的参数std::nothrow,与正常的不同之处new仅在于如果分配失败,它会返回一个空指针。

另请注意,这new不是 C++ 中唯一的内存管理机制。一方面,有 C 函数mallocfree. 虽然通常只是打电话,但这并不能保证operator new。因此,您不能混合使用这些形式(例如,通过调用指向用 分配的内存的指针)。另一方面,STL 容器通过分配器处理它们的分配,分配器是管理对象的分配/解除分配以及容器中对象的构造/销毁的对象。operator new[]mallocfreeoperator new

最后,还有那些生命周期由语言直接控制的对象,即静态和自动生命周期的对象。通过简单地在本地范围内定义类型的变量来分配自动生命周期对象。它们在执行通过该行时自动创建,并在执行离开范围时自动销毁(包括范围通过异常离开)。静态生命周期对象是在全局/命名空间范围内或使用关键字 static 在本地范围内定义的。它们是在程序启动时(全局/命名空间范围)或它们的定义行首次执行时(本地范围)创建的,它们一直存在到程序结束时,它们会以相反的构造顺序自动销毁。

通常,自动或静态变量比动态分配(即,您使用new分配器或分配器分配的所有内容)更受欢迎,因为编译器关心适当的销毁,这与您必须自己执行的动态分配不同。如果您有动态分配的对象,出于同样的原因,希望它们的生命周期由自动/静态对象(容器、智能指针)管理。

于 2013-06-09T22:47:51.473 回答
1

您似乎有new正确的操作,因为它分配和初始化内存。

一旦new成功完成,你,程序员,负责delete内存。确保发生这种情况的最佳方法是永远不要new直接使用自己,而是更喜欢标准容器和算法以及基于堆栈的对象。但是如果你确实需要分配内存,C++ 习惯用法是使用像unique_ptrC++11 或shared_ptrboost 或 C++11 这样的智能指针。这样可以确保正确回收内存。

如果分配失败,new调用将在清除失败之前构造的对象的任何部分后抛出异常。您可以使用(nothrow)new 的版本返回空指针而不是抛出异常,但这会给客户端代码带来更多的清理负担。

于 2013-06-09T22:33:25.793 回答
0

new 关键字 new 运算符在两种语言中有些相似。主要区别在于每个对象和数组都必须通过 Java 中的 new 分配。(实际上,数组实际上是 Java 中的对象。)因此,虽然以下内容在 C/C++ 中是合法的,并且会从堆栈中分配数组...

 // C/C++ : allocate array from the stack
    void myFunction() {
      int x[2];
      x[0] = 1;
      ...
    }

...在 Java 中,我们必须编写以下内容:

// Java : have to use 'new'; JVM allocates
//        memory where it chooses.
void myFunction() {
  int[] x = new int[2];
  ...
}

参考:https ://www.javamex.com/java_equivalents/new.shtml

于 2020-04-23T03:58:50.437 回答