【问题标题】:Why constructors will always have same name as of class and how they are invoked implicitly?为什么构造函数总是与类同名,以及它们是如何被隐式调用的?
【发布时间】:2019-05-23 02:19:21
【问题描述】:

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

【问题讨论】:

  • 对于你的标记语言(C++、Java、C#)来说确实如此,但还有其他可能性:在 Pascal/Delphi 中,构造函数是用 constructor 关键字定义的(并且可以有任何方法名称,但通常是 Create)并且在python 任何类的构造函数都称为__init__
  • 在 D 中,构造函数称为 this

标签: c# java c++


【解决方案1】:

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

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

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

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

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

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

这可能过于聪明了。 【C++的设计与演进,3.11.2构造函数表示法】


谁能解释一下这种情况下的执行流程?

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

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

    在 Java 中,第 1 步总是从堆中分配。在 C# 中,类也是从堆中分配的,而结构的内存已经可用(在非捕获本地结构的情况下在堆栈上或在它们的父对象/闭包中)。 Note that knowing these details is generally not necessary or very helpful。在C++中,内存分配极其复杂,这里不再赘述。

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

【讨论】:

  • D&E 当然是许多“为什么 X 像 X 一样工作?”的正确来源。问题。
  • @MSalters:起初我以为这是一个纯 C++ 问题。但由于 Java 和 C# 在构造函数名称方面明显受到 C++ 的影响,我认为这句话仍然有意义。如果 C++ 以任何其他方式完成它,Java 和 C# 可能已经完成了方式,太。
【解决方案2】:

为什么?因为你提到的不同语言的设计者决定那样做。完全有可能有人设计一种 OOP 语言,其中构造函数不必与类同名(如评论所述,python 中就是这种情况)。

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

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

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

【讨论】:

  • 不知道为什么它被否决了。唯一“缺失”的是一个设计示例,其中 c'tor 被不同的名称(即 python)调用。 +1。
  • 小修正:可以通过没有返回类型来伪装构造函数。实际上可以使用带有类名的普通方法(至少在 Java 中是这样),但出于某种原因“非常不鼓励”
【解决方案3】:

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

然后,例如,可以将类中的代码复制为另一个类的主体,而无需更改构造函数的名称。我不知道为什么要这样做(可能是在某些代码重构过程中),但关键是人们总是力求事物之间的独立性,而我认为这里的语言语法与此背道而驰。

析构函数也一样。

【讨论】:

  • 一个用例是匿名类。如果该类没有显式名称,(遗憾的是)也无法声明自定义 ctor 和 dtor。
【解决方案4】:

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

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

现在,构造函数定义为,

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

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

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

【讨论】:

  • +1 通过给出(imo)构造函数应该具有类名的充分理由来实际回答问题。
  • 这当然有点递归逻辑:你也可以想象一种语言说 MyClass obj = new() // You already know the type 并因此用 class MyClass { public: new() { ... } }; 定义它的构造函数
  • @MSalters,MyClass obj = new DerivedClass() 也是可能的:)。这就是为什么我们没有简单的MyClass obj = new()
  • new()(没有类名)只有在引用类型与运行时类型相同时才能用作快捷方式 - 但像“MyClass obj = new MyExtendedClass()”这样的语句将不可能。编辑:忍者
  • @iammilind:不讨论这个。你也有像foo(new myClass)这样的案例。我只是指出,通过应用另一个通用规则(不要重复自己),我可以按照相同的逻辑得出不同的结论。由于 new 是所有这些语言中的关键字,因此它也是一个很好的 ctor 名称。
【解决方案5】:

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

在 C++ 中声明和定义构造函数的语法使用类的名称。必须有某种方法可以做到这一点,并且使用类的名称是简洁易懂的。 C# 和 Java 都复制了 C++ 的语法,大概是因为它至少对他们的一些目标受众来说是熟悉的。

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

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

【讨论】:

    【解决方案6】:

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

    这样就可以明确地将其标识为构造函数。

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

    它被编译器调用,因为它已经因其命名模式而被明确识别。

    谁能解释一下这种情况下的执行流程?

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

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

      以其类命名构造函数是一个长期存在的约定,至少可以追溯到 1980 年代早期的 C++,可能是其 Simula 的前身。

    【讨论】:

      【解决方案7】:

      构造函数与类同名的约定是为了编程方便、构造函数链接和语言的一致性。

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

      那么你将如何知道你需要写:

      扫描仪 scObj = new xyz(System.in) ;

      这可能真的很奇怪,对吧!或者,您可能不得不参考大量手册来检查每个类的构造函数名称,以便创建对象,如果您可以通过仅命名与类相同的构造函数来解决问题,这又是毫无意义的。

      其次,如果您没有明确提供构造函数,编译器本身就会创建它,那么编译器会自动选择构造函数的最佳名称,这样程序员就很清楚了!显然,最好的选择是保持与班级相同。

      第三,您可能听说过构造函数链接,那么在将构造函数之间的调用链接在一起时,编译器将如何知道您为链接类的构造函数指定的名称!显然,问题的解决方案还是一样的,保持构造函数的名称与类的名称相同。


      创建对象时,通过使用 new 关键字在代码中调用它来调用构造函数(并在需要时传递参数),然后通过链接最终给出对象的调用来调用所有超类构造函数。

      感谢您的提问。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-02
        • 2017-05-20
        • 2018-08-03
        • 2021-09-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多