新的总是在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’。 你指向的记忆可以存在于任何地方。

请参阅“放置新”有什么用途?