为什么构造函数将始终具有与类相同的名称以及如何隐式调用它们?

时间:2011-08-08 08:46:45

标签: c# java c++

我想知道为什么构造函数的名称总是与类名的名称相同,以及在创建该类的对象时如何隐式调用它。有谁能解释一下这种情况下的执行流程?

7 个答案:

答案 0 :(得分:13)

  

我想知道为什么构造函数的名称总是与类名

的名称相同

因为此语法不需要任何新关键字。除此之外,没有充分的理由。

  

为了尽量减少新关键字的数量,我没有使用这样的显式语法:

class X {
    constructor();
    destructor();
}
     

相反,我选择了一个声明语法,它反映了构造函数的 use

class X {
    X();
    ~X();
     

这可能过于聪明了。 [C ++的设计和演变,3.11.2构造函数表示法]


  

有谁可以解释这种情况下的执行流程?

对象的生命周期可以这样总结:

  1. 分配内存
  2. call constructor
  3. 使用对象
  4. call destructor / finalizer
  5. 释放记忆
  6. 在Java中,步骤1始终从堆中分配。在C#中,类也是从堆中分配的,而结构的内存已经可用(在非捕获的本地结构的情况下在堆栈中或在它们的父对象/闭包内)。 Note that knowing these details is generally not necessary or very helpful。在C ++中,内存分配非常复杂,所以我不会在这里详细介绍。

    步骤5取决于内存的分配方式。方法结束后,堆栈存储器将自动释放。在Java和C#中,垃圾收集器在不再需要它的某个未知时间内隐式释放堆内存。在C ++中,通过调用delete在技术上释放堆内存。在现代C ++中,delete很少被手动调用。相反,您应该使用自己处理的std::stringstd::vector<T>std::shared_ptr<T>等RAII对象。

答案 1 :(得分:6)

为什么呢?因为你提到的不同语言的设计师决定以这种方式制作它们。某人完全有可能设计一种OOP语言,其中构造函数不必与类具有相同的名称(如注释,python中就是这种情况)。

这是一种区分构造函数与其他函数的简单方法,并使代码中的类构造非常易读,因此作为语言设计选择是有意义的。

这种机制在不同语言中略有不同,但实质上这只是一种语言功能辅助的方法调用(例如java和c#中的new关键字)。

每当创建新对象时,运行时都会调用构造函数。

答案 2 :(得分:4)

对我来说,使用sepearte关键字来声明构造函数会“更好”,因为它会删除对类本身名称的其他不必要的依赖。

然后,例如,类中的代码可以作为另一个的主体复制,而不必对构造函数的名称进行更改。为什么人们想要这样做我不知道(可能在一些代码重构过程中),但重点是人们总是在努力实现事物之间的独立性,我认为语言语法与此相反。

相同的析构函数。

答案 3 :(得分:2)

构造函数具有相同名称的一个很好的理由是它们的表现力。例如,在Java中,您可以创建一个对象,如

MyClass obj = new MyClass();  // almost same in other languages too

现在,构造函数定义为

class MyClass {
  public MyClass () {... }
}

所以上面的语句很好地表达了,你正在创建一个对象,而在这个过程中,构造函数MyClass()被调用。

现在,无论何时创建对象,它总是调用其构造函数。如果该类是extend其他一些Base类,那么将首先调用它们的构造函数,依此类推。所有这些操作都是隐含的。首先分配对象的内存(在堆上)然后调用构造函数来初始化对象。如果您不提供构造函数,编译器将为您的类生成一个。

答案 4 :(得分:2)

在C ++中,严格来说构造函数根本没有名称。 12.1 / 1在标准状态下,“构造函数没有名称”,它没有比那更清楚。

在C ++中声明和定义构造函数的语法使用类的名称。必须有一些方法,并使用类的名称简洁,易于理解。 C#和Java都复制了C ++的语法,大概是因为它至少对他们所针对的一些观众来说是熟悉的。

精确的执行流程取决于你所说的语言,但你列出的三个共同之处在于,首先从某个地方分配一些内存(可能是动态分配的,也许是堆栈内存的某些特定区域或者其他)。然后,运行时负责确保以正确的顺序调用正确的构造函数或构造函数,用于最派生的类以及基类。由实现决定如何确保这种情况发生,但所需的效果由每种语言定义。

对于C ++中最简单的情况,对于没有基类的类,编译器只是调用由创建对象的代码指定的构造函数,即与提供的任何参数匹配的构造函数。一旦你有几个虚拟基地,它会变得更加复杂。

答案 5 :(得分:1)

  

我想知道为什么构造函数的名称总是与之相同   班级名称

因此可以毫不含糊地将其识别为构造函数。

  

以及在创建该类的对象时如何隐式调用它。

它由编译器调用,因为它已经被明确地识别,因为它的命名符号。

  

有谁可以解释这种情况下的执行流程?

  1. 调用新的X()运算符。
  2. 分配内存,或抛出异常。
  3. 调用构造函数。
  4. new()运算符返回给调用者。
  5.   

    问题是为什么设计师决定这样做?

    在类之后命名构造函数是一个历史悠久的约定,至少可以追溯到20世纪80年代早期的C ++早期,可能是它的Simula前身。

答案 6 :(得分:0)

构造函数与类相同的名称的约定是为了简化编程,构造函数链接和语言的一致性。

例如,考虑一个要使用Scanner类的场景,现在,如果JAVA开发人员将构造函数命名为xyz会怎样!

然后您将如何知道需要写:

Scanner scObj = new xyz(System.in);

这本来是很奇怪的,对!或者,更确切地说,您可能必须参考大量手册来检查每个类的构造函数名称以创建对象,如果仅通过命名与该类相同的构造函数就可以解决问题,那么这又毫无意义。 / p>

第二,如果您未明确提供构造函数,则构造函数本身是由编译器创建的,那么编译器会自动选择最适合构造函数的名称,因此程序员很清楚!显然,最好的选择是与班级保持一致。

第三,您可能听说过构造函数链接,然后在构造函数之间链接调用时,编译器将如何知道您给链接类的构造函数指定了什么名称!显然,解决该问题的方法还是相同的,将构造函数的名称保留为该类的名称。


创建对象时,您可以通过使用new关键字(并在需要时传递参数)在代码中对其进行调用来调用构造函数,然后通过链接最终提供该对象的调用来调用所有超类构造函数。

感谢提问。