新的总是在C ++ / C#/ Java中分配在堆上
无论使用C ++或C#还是Java,我的理解始终是当我们使用new
关键字创建对象时,它会在堆上分配内存。 我认为new
的只需要引用类型(类),并且原始类型(int,bool,float等)从不使用new
并且总是进入堆栈(除非它们是类的成员变量用new
实例化)。 但是,我一直在阅读使我怀疑这个长期存在的假设的信息,至少对于Java和C#而言。
例如,我刚注意到在C#中, new
运算符可用于初始化值类型,请参见此处 。 这是规则的例外,是该语言的辅助function,如果是,那么还会有哪些其他例外?
有人可以澄清一下吗?
我认为只有引用类型(类)需要new,而原始类型(int,bool,float等)从不使用new
在C ++中,如果要执行以下操作,可以在堆上分配基元类型:
int* p = new int(42);
如果你想要一个共享计数器,这很有用,例如在shared_ptr
的实现中。
此外,您不必在C ++中使用new with classes:
void function() { MyClass myObject(1, 2, 3); }
这将在堆栈上分配myObject
。 请注意,在现代C ++中很少使用new
。
此外,您可以在C ++中重载operator new
(全局或特定于类),因此即使您说new MyClass
,该对象也不一定在堆上分配。
我不确切地知道Java(并且很难获得有关它的文档)。
在C#中, new
调用构造函数并返回一个新对象。 如果它是值类型,则将其分配在堆栈(例如,局部变量)或堆上(例如,盒装对象,引用类型对象的成员)。 如果它是引用类型,它总是在堆上并由垃圾收集器管理。 有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/fa0ab757(v=vs.80).aspx 。
在C ++中, “新表达式”返回指向具有动态存储持续时间的对象的指针(即,您必须自行销毁)。 在C ++标准中没有提到堆(具有这个含义),并且获得这样的对象的机制是实现定义的。
无论使用C ++或C#还是Java,我的理解始终是当我们使用
new
关键字创建对象时,它会在堆上分配内存。
您的理解不正确:
-
new
可能在不同的编程语言中以不同的方式工作,即使这些语言在表面上相似也是如此。 不要让C#,C ++和Java的类似语法误导你! -
术语“堆”和“堆栈”(因为它们在内部存储器管理的上下文中被理解)与所有编程语言无关。 可以说,这两个概念更多的是实现细节,而不是它们是编程语言官方规范的一部分。
(IIRC,至少C#和C ++都是如此。我不了解Java。)
事实上,它们是如此广泛的实施细节并不意味着你应该依靠这种区别,也不应该知道它! (但是,我承认我通常认为在内部了解“事情如何运作”是有益的。)
我建议你不要过于担心这些概念。 你需要做的正确的重要事情是理解语言的语义; 例如,对于C#或任何其他.NET语言,引用和值类型语义的差异。
示例:C#规范关于operator new
:
请注意ECMA(第4版)发布的C#规范的以下部分未提及任何“堆栈”或“堆”:
14.5.10新运营商
new运算符用于创建类型的新实例。 […]
新运算符意味着创建类型的实例,但不一定意味着动态分配内存。 特别是,值类型的实例除了它们所在的变量之外不需要额外的内存,并且当new用于创建值类型的实例时,不会发生动态分配。
相反,它谈到“动态分配内存”,但这不是一回事:你可以动态地在堆栈上,堆上或其他任何地方(例如在硬盘驱动器上)分配内存。
然而,它所说的是值类型的实例是就地存储的,这正是值类型语义的全部内容:值类型实例在赋值期间被复制,而引用类型实例被引用/“别名”。 这是重要的事情要理解,而不是“堆”或“堆栈”!
在c#中,一个class
总是存在于堆中。 struct
可以在堆或堆栈上:
- 变量(捕获和迭代器块除外),以及堆栈本身的结构上的字段
- 捕获,迭代器块,堆上的某些字段,以及数组中的值存在于堆上,“盒装”值也是如此
根据http://download.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html,Java 7进行转义分析以确定是否可以在堆栈上分配对象。
但是,您无法指示运行时在堆或堆栈上分配对象。 它是自动完成的。
关于c#,请阅读关于值类型的真相 。 您将看到值类型也可以在堆上。
并且在这个问题上建议引用类型可以进入堆栈。 (但此刻不会发生)
(参考Java)你说的是正确的 – 在栈上分配原语(有例如闭包)。 但是,您可能指的是以下对象:
Integer n = new Integer(2);
这指的是Integer对象,而不是原始int。 也许这是你混乱的根源? 在这种情况下,n将在堆上分配。 也许你的困惑是由于自动装箱规则? 有关自动装箱的更多详细信息,请参阅此问题。 查看对此答案的评论,以了解在堆上分配基元的规则的例外情况。
在Java和C#中,我们不需要在堆上分配基元类型。 它们可以在堆栈上分配(而不是限制为堆栈)。 然而,在C ++中,我们可以在堆栈和堆上分配原始类型和用户定义类型。
在C ++中,还有另一种使用new运算符的方法,那就是’placement new’。 你指向的记忆可以存在于任何地方。
请参阅“放置新”有什么用途?