C ++中的new运算符与java中的new运算符之间的区别
据我所知, new
运营商做了以下事情:(如果我错了,请纠正我。)
- 分配内存,然后返回分配的内存的第一个块的引用。 (很明显,内存是从堆中分配的。)
- 初始化对象(调用构造函数。)
运算符new[]
以类似的方式工作,除了它对数组中的每个元素执行此操作。
任何人都可以告诉我这两个运算符在C ++和Java中有何不同:
- 就他们的生命周期而言。
- 如果他们无法分配内存怎么办?
-
在C ++中,
T * p = new T;
…-
为
T
类型的对象分配足够的内存, -
在该内存中构造一个
T
类型的对象,可能正在初始化它,以及 -
返回指向该对象的指针。 (指针与标准
new
的已分配内存的地址具有相同的值,但不一定是new[]
数组的情况。)
如果内存分配失败,则抛出类型为
std::bad_alloc
的exception,不构造任何对象且不分配内存。如果对象构造函数抛出exception,(显然)没有构造对象,则会立即自动释放内存,并传播exception。
否则,构造了一个动态分配的对象,用户必须手动销毁该对象并释放内存,通常是说
delete p;
。实际的分配和释放function可以用C ++控制。 如果没有别的,则使用全局的预定义函数
::operator new()
,但这可以由用户替换 ; 如果存在静态成员函数T::operator new
,则将使用该函数。 -
-
在Java中它非常相似,只是
new
的返回值可以绑定到类型为T
的Java变量(或其基类,如Object
),并且必须始终有一个初始化器(所以你要说T x = new T();
)。 对象的生命周期是不确定的,但保证至少与任何变量仍引用对象一样长,并且无法(也不需要)手动销毁对象。 Java没有明确的内存概念,您无法控制分配的内部。
此外,C ++允许许多不同forms的new
表达式(所谓的放置forms)。 它们都创建动态存储对象,必须手动销毁,但它们可能相当随意。 据我所知,Java没有这样的设施。
最大的区别可能就是使用 :在Java中,你总是使用new
所有事情,而且你必须这样做,因为它是创建(类类型)对象的唯一方法。 相比之下,在C ++中,你几乎不应该在用户代码中拥有裸露的new
。 C ++具有无约束变量,因此变量本身可以是对象,这就是C ++中通常使用对象的方式。
在你的“语句”中,我不认为“返回对分配的内存的第一个块的引用是非常正确的new
返回一个指针(对于分配的对象的类型)。这与引用略有不同,虽然在概念上类似。
您的问题的答案:
- 在C ++中,一个对象留在内存中(参见注释),直到用
delete
或delete []
显式删除它(并且你必须使用与你分配的匹配的那个,所以一个new int[1];
尽管它是相同的作为new int;
的内存量new int;
不能用delete
(反之亦然,delete []
不能用于new int
)。在Java中,内存在将来的某个时刻被垃圾回收器释放一次有“没有提到记忆”。 - 两者都抛出exception(C ++抛出
std::bad_alloc
,Java就像OutOfMemoryError),但在C ++中你可以使用new(std::nothrow) ...
,在这种情况下,如果没有足够的可用内存,new
将返回NULL满足电话。
注意:根据评论,技术上可以“破坏”对象而不释放它的内存。 这是一个相当不寻常的案例,除非你真的有C ++经验并且你有充分的理由这样做,否则你应该做的事情。 典型的用例是在delete操作符内部,对应于一个placement new(其中new
用一个已经存在的内存地址调用,只执行对象的构造)。 同样,placement new非常特别地使用new,而不是你可以期望在普通C ++代码中看到的东西。
您似乎具有new
正确操作,因为它分配和初始化内存。
一旦new
成功完成,程序员就会负责delete
该内存。 确保发生这种情况的最佳方法是永远不要直接使用new
,而是选择标准容器和算法,以及基于堆栈的对象。 但是如果你确实需要分配内存,那么C ++习惯用法就是使用一个智能指针,如C ++ 11中的unique_ptr
或者来自boost或C ++ 11的shared_ptr
。 这确保了内存被正确回收。
如果分配失败,则在清除失败之前构造的对象的任何部分之后, new
调用将抛出exception。 您可以使用new的(nothrow)
版本返回空指针而不是抛出exception,但这会给客户端代码带来更多的清理负担。
我不知道Java中的细节,但这里是C ++中new
和new[]
:
-
分配内存
当你有一个表达式
new T
或new T(args)
,编译器会确定要调用哪个函数来获取内存- 如果类型
T
具有适当的成员operator new
,则调用该operator new
-
否则,如果用户提供了适当的全局
operator new
,则调用该operator new
。如果
operator new
无法分配所请求的内存,则它会调用一个新的处理函数,您可以使用set_new_handler
进行设置。 该函数可以释放一些空间,因此分配可以成功,它可以终止程序,或者它可能抛出std::bad_alloc
类型的exception或从中派生。 默认的新处理程序只抛出std::bad_alloc
。对于
new T[n]
operator new[]
也会发生同样的情况,除了调用operator new[]
进行内存分配。
- 如果类型
-
构造对象resp。 新分配的内存中的对象。
对于
new T(args)
,调用对象的相应构造函数。 如果构造函数抛出exception,则通过调用相应的operator delete
(可以在与operator new
相同的位置找到)来释放内存。对于
new T
它取决于T
是否为POD(即内置类型或基本上是C结构/联合)。 如果T是POD,则没有任何反应,否则它将被视为new T()
。对于
new T[n]
,它还取决于T
是否为POD
。 同样,POD未初始化。 对于非POD,依次为每个对象调用默认构造函数。 如果一个对象的默认构造函数抛出,则不会调用其他构造函数,但是已经构造的对象(不包括构造函数刚刚抛出的对象)将以相反的顺序被破坏(即,调用析构函数)。 然后使用适当的operator delete[]
释放内存。 -
返回指向新创建的对象的指针。 请注意,对于
new[]
,指针可能不会指向已分配内存的开头,因为可能会有一些关于构造对象之前已分配对象数的信息,delete[]
来确定有多少对象毁灭
在所有情况下,对象一直存在,直到它们被delete ptr
(对于使用普通new
分配的对象)或delete[] ptr
(对于使用array new T[n]
创建的对象)进行销毁。 除非添加了第三方库,否则C ++中没有垃圾收集。
请注意,您还可以直接调用operator new
和operator delete
来分配原始内存。 对于operator new[]
和operator delete[]
也是如此。 但请注意,即使对于那些低级函数,您也可以不混合调用,例如通过使用operator new[]
分配的operator delete
释放内存。
您还可以使用所谓的placement new来分配已分配内存中的对象(无论您如何获得)。 这是通过将指向原始内存的指针作为new
参数来完成的,如下所示: new(pMem) T(args)
。 要破坏这样一个显式构造的对象,你可以直接调用对象的析构函数, p->~T()
。
通过调用operator new
将operator new
作为附加参数并将其返回来放置new工作。 这种相同的机制也可用于向operator new
提供其他信息,这些operator new
重载采用相应的附加参数。 但是,虽然您可以定义相应的operator delete
,但这些操作仅用于在对象在构造期间抛出exception时进行清理。 没有“放置删除”语法。
C ++已经提供的另一种使用放置新语法的方法并不新鲜。 那个采用额外的参数std::nothrow
并且与普通的new
不同,只是在分配失败时返回空指针。
另请注意, new
不是C ++中唯一的内存管理机制。 一方面,有C函数malloc
和free
。 通常operator new
和operator new[]
只调用malloc
,但这并不能保证。 因此,您可能不会混合使用这些表单(例如,通过在指向使用operator new
分配的内存的指针上调用free
)。 另一方面,STL容器通过分配器处理它们的分配,分配器是管理对象的分配/释放以及容器中对象的构造/销毁的对象。
最后,有些对象的生命周期由语言直接控制,即静态和自动生命周期。 通过简单地在本地范围定义类型的变量来分配自动生命周期对象。 它们在执行通过该行时自动创建,并在执行离开作用域时自动销毁(包括通过exception保留作用域)。 静态生存期对象使用关键字static在全局/命名空间范围或本地范围定义。 它们是在程序启动时创建的(全局/命名空间范围),或者当它们的定义行被最终执行时(本地范围),它们一直存在到程序结束时,它们以相反的构造顺序自动销毁。
通常,自动或静态变量优先于动态分配(即,您使用new
或allocators分配的所有内容),因为编译器需要适当的销毁,不像动态分配,您必须自己完成。 如果您有动态分配的对象,则由于同样的原因,最好使用自动/静态对象(容器,智能指针)来管理它们的生命周期。