18

为什么new 运算符存在于 C# 和 Java 等现代语言中?它是纯粹的自我记录代码功能,还是有任何实际用途?

例如下面的例子:

Class1 obj = new Class1();

Class1 foo()
{
    return new Class1();
}

就像 Pythonesque 的写法一样容易阅读:

Class1 obj = Class1();

Class1 foo()
{
    return Class1();
}

编辑:考恩在澄清问题时一针见血:他们为什么选择这种语法?

4

9 回答 9

24
  1. 这是一个自我记录的功能。
  2. 这是一种可以在其他类中命名方法“Class1”的方法
于 2009-01-11T20:23:20.960 回答
9
Class1 obj = Class1();

在 C# 和 Java 中,您需要“new”关键字,因为没有它,它将“Class1()”视为对名为“Class1”的方法的调用。

于 2009-01-11T20:24:33.967 回答
9

有用的是文档 - 与 Python 相比,区分对象创建和方法调用更容易。

原因是历史性的,直接来自 C++ 语法。在 C++ 中,“Class1()”是在堆栈上创建 Class1 实例的表达式。例如:vector a = vector(); 在这种情况下,会创建一个向量并将其复制到向量 a(优化器在某些情况下可以删除冗余副本)。

相反,“new Class1()”在堆上创建一个 Class1 实例,就像在 Java 和 C# 中一样,并返回一个指向它的指针,使用不同的访问语法,这与 Java 和 C++ 不同。实际上,new 的含义可以重新定义为使用任何特殊用途的分配器,它仍然必须引用某种堆,以便可以通过引用返回获得的对象。

此外,在 Java/C#/C++ 中,Class1() 本身可以引用任何方法/函数,这会造成混淆。Java 编码约定实际上会避免这种情况,因为它们要求类名以大写字母开头,方法名以小写字母开头,这可能是 Python 在这种情况下避免混淆的方式。读者希望“Class1()”创建一个对象,“class1()”是一个函数调用,“x.class1()”是一个方法调用(其中“x”可以是“self”)。

最后,由于在 Python 中他们选择将类作为对象,尤其是可调用对象,因此允许使用没有“new”的语法,并且允许同时使用另一种语法是不一致的。

于 2009-01-11T21:04:44.853 回答
5

C# 中的 new 运算符直接映射到调用的 IL 指令,该指令newobj实际上为新对象的变量分配空间,然后执行构造函数(在 IL 中称为 .ctor)。在执行构造函数时——很像 C++——对初始化对象的引用作为不可见的第一个参数传入(如thiscall)。

类似于 thiscall 的约定允许运行时仅一次为特定类加载和 JIT 内存中的所有代码,并为该类的每个实例重用它。

Java 在它的中间语言中可能有类似的操作码,虽然我不太熟悉。

于 2009-01-11T20:41:39.253 回答
2

C++ 为程序员提供了在堆上或栈上分配对象的选择。

基于堆栈的分配更有效:分配更便宜,释放成本真正为零,并且该语言在划分对象生命周期方面提供了帮助,降低了忘记释放对象的风险。
另一方面,在 C++ 中,在发布或共享对基于堆栈的对象的引用时需要非常小心,因为基于堆栈的对象会在堆栈帧展开时自动释放,从而导致指针悬空。

使用newoperator,所有对象都在 Java 或 C# 中的堆上分配。

Class1 obj = Class1();

实际上,编译器会尝试找到一个名为Class1().

例如,以下是一个常见的 Java 错误:

public class MyClass
{
  //Oops, this has a return type, so its a method not a constructor!
  //Because no constructor is defined, Java will add a default one.
  //init() will not get called if you do new MyClass();
  public void MyClass()
  {
     init();
  }
  public void init()
  {
     ...
  }
} 

注意:“所有对象都在堆上分配”并不意味着偶尔不会在后台使用堆栈分配。

例如,在 Java 中,像逃逸分析这样的热点优化使用堆栈分配。

运行时编译器执行的这种分析可以得出结论,例如,堆上的对象仅在方法中被本地引用,并且没有任何引用可以从这个范围中逃脱。如果是这样,Hotspot 可以应用运行时优化。它可以在堆栈上或寄存器中分配对象,而不是在堆上。

虽然这种优化并不总是被认为是 决定性的......

于 2009-01-11T21:21:12.043 回答
2

Java 选择它的原因是因为 C++ 开发人员熟悉该语法。C# 之所以选择它,是因为它为 Java 开发人员所熟悉。

在 C++ 中使用运算符的原因new可能是因为在手动内存管理中,明确何时分配内存非常重要。虽然 pythonesque 语法可以工作,但它使得内存分配不太明显。

于 2009-01-13T22:22:05.417 回答
1

new 运算符为对象分配内存,这是它的目的;正如您所说,它还自我记录您正在使用实例(即新实例)

于 2009-01-11T20:22:53.473 回答
0

正如其他人所指出的,Java 和 C# 提供了new语法,因为 C++ 提供了。C++ 需要某种方法来区分在堆栈上创建对象、在堆上创建对象或调用返回指向对象的指针的函数或方法。

C++ 使用这种特殊语法是因为早期的面向对象语言 Simula 使用了它。Bjarne Stroustrup 受到 Simula 的启发,并试图在 C 中添加类似 Simula 的功能。C 具有分配内存的函数,但不保证也调用构造函数。

摘自“C++ 的设计和演变”,1994 年,Bjarne Stroustrup 着,第 57 页:

因此,我引入了一个运算符来确保完成分配和初始化:

monitor* p = new monitor;

调用该运算符是new因为那是相应 Simula 运算符的名称。运算符调用一些分配函数来获取内存,然后new调用构造函数来初始化该内存。组合操作通常称为实例化或简称为对象创建:它从原始内存中创建一个对象。

操作员提供的符号便利性new非常重要。……”

于 2019-07-15T17:14:49.293 回答
-1

除了以上 AFAIK 的评论外,他们还计划在早期草案中删除 Java 7 的新关键字。但后来他们取消了。

于 2009-01-11T22:16:50.573 回答