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

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

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

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

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

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

相反,我选择了一个声明语法来反映构造函数的使用

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

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


任何人都可以解释这种情况下的执行流程吗?

对象的生命周期可以总结如下:

  1. 分配内存
  2. 调用构造函数
  3. 使用对象
  4. 调用析构函数/终结符
  5. 释放记忆

在Java中,步骤1始终从堆中分配。 在C#中,类也是从堆中分配的,而结构的内存已经可用(在非捕获的本地结构或其父对象/闭包的情况下,在堆栈上)。 请注意,了解这些细节通常不是必需的或非常有用 。 在C ++中,内存分配非常复杂,所以我不会在这里详细介绍。

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

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

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

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

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

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

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

析构函数也是如此。

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

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

现在,构造函数定义为,

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

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

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

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

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

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

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

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

这样它就可以毫不含糊地被识别为构造函数。

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

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

任何人都可以解释这种情况下的执行流程吗?

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

问题是设计师为何如此决定?

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