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