据我所知,new
运营商做了以下事情:(如果我错了,请纠正我。)
运算符new[]
也以类似的方式工作,除了它对数组中的每个元素执行此操作。
任何人都可以告诉我这两个运算符在C ++和Java中是如何不同的:
答案 0 :(得分:9)
在C ++中,T * p = new T;
...
为T
,
在该内存中构造一个T
类型的对象,可能正在初始化它,
返回指向该对象的指针。 (指针与标准new
的已分配内存的地址具有相同的值,但不一定是数组形式new[]
的情况。)
如果内存分配失败,则抛出类型std::bad_alloc
的异常,不构造任何对象,也不分配内存。
如果对象构造函数抛出异常,(显然)没有构造任何对象,则会立即自动释放内存,并传播异常。
否则构建了动态分配的对象,用户必须手动销毁对象并释放内存,通常是说delete p;
。
可以在C ++中控制实际的分配和释放功能。如果没有其他内容,则使用全局预定义函数::operator new()
,但这可能由用户替换;如果存在静态成员函数T::operator new
,则将使用该函数。
在Java中它非常相似,只是new
的返回值可以绑定到T
类型的Java变量(或其基础,例如{{1}你必须总是有一个初始值设定项(所以你要说Object
)。对象的生命周期是不确定的,但保证至少与任何变量仍然引用对象一样长,并且无法(也不需要)手动销毁对象。 Java没有明确的内存概念,你无法控制分配的内部。
此外,C ++允许许多不同形式的T x = new T();
表达式(所谓的 placement 表单)。它们都创建动态存储对象,必须手动销毁,但它们可能相当随意。据我所知,Java没有这样的设施。
最大的区别可能在于使用:在Java中,您始终使用new
来表示所有内容,并且有,因为它是一个并且只能创建(类类型)对象。相比之下,在C ++中,你几乎不应该在用户代码中使用new
个裸。 C ++有无约束的变量,因此变量本身可以是对象,这就是C ++中通常使用对象的方式。
答案 1 :(得分:2)
在你的“语句”中,我不认为“返回对第一块已分配内存的引用是非常正确的。new
返回一个指针(指向分配的对象的类型)。这是巧妙的与参考文献不同,虽然概念上相似。
您的问题的答案:
delete
或delete []
明确删除它(并且你必须使用与你分配的匹配的那个,所以a {{1虽然new int[1];
与new int;
的内存量相同,但无法使用delete
删除(反之亦然,delete []
不能用于new int
)在Java中,一旦“没有对内存的引用”,将来某个时候垃圾收集器会释放内存。std::bad_alloc
,Java就像OutOfMemoryError),但在C ++中你可以使用new(std::nothrow) ...
,在这种情况下new
如果没有足够的话会返回NULL内存可用于满足呼叫。 注意:根据评论,技术上可以“破坏”对象而不释放它的内存。这是一个相当不寻常的案例,除非你真的有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中的细节,但这里是new
和new[]
在C ++中的作用:
分配内存
当你有一个表达式new T
或new T(args)
时,编译器会确定要调用哪个函数来获取内存
T
具有适当的成员operator new
,则称其为否则,如果用户提供了适当的全局operator new
,则会调用其中一个。
如果operator new
无法分配请求的内存,则会调用新的处理函数,您可以使用set_new_handler
进行设置。该函数可以释放一些空间,因此分配可以成功,它可以终止程序,或者它可能抛出类型std::bad_alloc
的异常或从中派生出来。默认的新处理程序只会抛出std::bad_alloc
。
new T[n]
也是如此,除了为内存分配调用operator new[]
。
构造对象resp。新分配的内存中的对象。
对于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
(对于使用普通new
分配的对象)或delete[] ptr
(对于使用数组new T[n]
创建的对象)进行销毁。 。除非添加了第三方库,否则C ++中没有垃圾回收。
请注意,您也可以直接调用operator new
和operator 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函数malloc
和free
。虽然operator new
和operator 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];
...
}