C ++中的new运算符与java中的new运算符之间的区别

时间:2013-06-09 22:21:51

标签: java c++ pointers malloc new-operator

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

  1. 分配内存,然后返回第一个块的引用 分配内存。 (很明显,内存是从堆中分配的。)
  2. 初始化对象(调用构造函数。)
  3. 运算符new[]也以类似的方式工作,除了它对数组中的每个元素执行此操作。

    任何人都可以告诉我这两个运算符在C ++和Java中是如何不同的:

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

5 个答案:

答案 0 :(得分:9)

  • 在C ++中,T * p = new T; ...

    1. T

    2. 类型的对象分配足够的内存
    3. 在该内存中构造一个T类型的对象,可能正在初始化它,

    4. 返回指向该对象的指针。 (指针与标准new的已分配内存的地址具有相同的值,但不一定是数组形式new[]的情况。)

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

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

      否则构建了动态分配的对象,用户必须手动销毁对象并释放内存,通常是说delete p;

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

    6. 在Java中它非常相似,只是new的返回值可以绑定到T类型的Java变量(或其基础,例如{{1}你必须总是有一个初始值设定项(所以你要说Object)。对象的生命周期是不确定的,但保证至少与任何变量仍然引用对象一样长,并且无法(也不需要)手动销毁对象。 Java没有明确的内存概念,你无法控制分配的内部。

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


最大的区别可能在于使用:在Java中,您始终使用new来表示所有内容,并且,因为它是一个并且只能创建(类类型)对象。相比之下,在C ++中,你几乎不应该在用户代码中使用new个裸。 C ++有无约束的变量,因此变量本身可以是对象,这就是C ++中通常使用对象的方式。

答案 1 :(得分:2)

在你的“语句”中,我不认为“返回对第一块已分配内存的引用是非常正确的。new返回一个指针(指向分配的对象的类型)。这是巧妙的与参考文献不同,虽然概念上相似。

您的问题的答案:

  1. 在C ++中,一个对象留在内存中(参见注释),直到用deletedelete []明确删除它(并且你必须使用与你分配的匹配的那个,所以a {{1虽然new int[1];new int;的内存量相同,但无法使用delete删除(反之亦然,delete []不能用于new int)在Java中,一旦“没有对内存的引用”,将来某个时候垃圾收集器会释放内存。
  2. 两者都抛出一个异常(C ++抛出std::bad_alloc,Java就像OutOfMemoryError),但在C ++中你可以使用new(std::nothrow) ...,在这种情况下new如果没有足够的话会返回NULL内存可用于满足呼叫。
  3. 注意:根据评论,技术上可以“破坏”对象而不释放它的内存。这是一个相当不寻常的案例,除非你真的有C ++经验并且你有充分的理由这样做,否则你不应该做些什么。这种情况的典型用例是在delete操作符内部,对应于一个placement new(其中new被调用,其中已经存在的内存地址仅用于执行对象的构造)。同样,placement new几乎是特别使用new,而不是你可以期望看到普通C ++代码中的大部分内容。

答案 2 :(得分:1)

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

new成功完成后,您(程序员)负责delete该内存。确保发生这种情况的最佳方法是永远不要直接使用new,而是选择标准容器和算法,以及基于堆栈的对象。但是如果你确实需要分配内存,那么C ++习惯用法就是使用来自C ++ 11的unique_ptr或来自boost或C ++ 11的shared_ptr等智能指针。这样可以确保正确回收内存。

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

答案 3 :(得分:1)

我不了解Java中的细节,但这里是newnew[]在C ++中的作用:

  1. 分配内存

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

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

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

      new T[n]也是如此,除了为内存分配调用operator new[]

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

    对于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. 取消分配内存
  4. 返回指向新创建对象的指针。请注意,对于new[],指针很可能指向已分配内存的开头,因为可能会有一些关于构造对象之前已分配对象数的信息,这些信息由delete[]计算要销毁的对象数量。

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

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

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

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

    C ++已经提供的另一种使用放置新语法的方法并不新鲜。那个接受一个额外的参数std::nothrow并且与普通new不同,只是因为如果分配失败它会返回一个空指针。

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

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

    通常,自动或静态变量优先于动态分配(即,您使用new或分配器分配的所有内容),因为编译器需要适当的销毁,不像动态分配,您需要自己做那件事。如果你有动态分配的对象,那么由于同样的原因,它们的生命周期由自动/静态对象(容器,智能指针)管理是可取的。

答案 4 :(得分:0)

新关键字 新的运算符在两种语言中有些相似。主要区别在于,必须通过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];
  ...
}

ref:https://www.javamex.com/java_equivalents/new.shtml