当你创建一个带参数的构造函数时,为什么默认的无参数构造函数会消失

在 C # 、 C + + 和 Java 中,当你创建一个带参数的构造函数时,默认的无参数构造函数就消失了。我一直只是接受这个事实,但现在我开始想知道为什么。

这种行为的原因是什么?这是否只是一种“安全措施/猜测”,表示“如果您已经创建了自己的构造函数,那么 可能吧不希望这个隐式的构造函数存在”? 或者它有一个技术原因,使编译器不可能添加一个一旦你已经创建了一个构造函数自己?

17435 次浏览

没有理由编译器不能添加构造函数,如果你已经添加了自己的-编译器可以做几乎任何它想要的!然而,你必须看看什么是最有意义的:

  • 如果我还没有为非静态类定义 任何构造函数,那么我很可能希望能够实例化该类。为了允许这种情况,编译器 必须的添加了一个无参数构造函数,这样除了允许实例化之外没有任何效果。这意味着我不必在代码中包含一个空的构造函数来使它工作。
  • 如果我自己定义了一个构造函数,特别是一个带参数的构造函数,那么我很可能有自己的逻辑,必须在创建类时执行。在这种情况下,如果编译器要创建一个空的、无参数的构造函数,它将允许某人使用我编写的逻辑 Skip,这可能会导致我的代码以各种方式被破坏。在这种情况下,如果我想要一个默认的空构造函数,我需要明确地说出来。

因此,在每种情况下,您都可以看到当前编译器的行为在保留代码的 可能的意图方面最有意义。

如果您自己不做任何事情来控制对象创建,则添加默认的无参数构造函数。一旦你创建了一个单独的构造函数来进行控制,编译器就会“退出”,让你拥有完全的控制权。

如果不是这样的话,如果你只想通过一个带参数的构造函数来构造对象,那么你就需要一些明确的方法来禁用缺省构造函数。

为什么 已经语言要这样设计,当然没有技术上的原因。

在我看来,有四个比较现实的选择:

  1. 完全没有默认构造函数
  2. 目前的情况
  3. 默认情况下始终 提供一个缺省构造函数,但允许显式地禁止它
  4. 总是提供一个缺省构造函数 ABc0允许它被抑制

选项1有些吸引人,因为我编写的代码越多,真的希望使用无参数构造函数的频率就越低。总有一天,我会数数自己使用缺省构造函数的频率... ..。

选项二,我没意见。

选项3违背了 Java 和 C # 的流程,对于语言的其余部分。你从来没有明确地“删除”过任何东西,除非你把明确地把东西变得比 Java 中默认的更私有。

选项4是可怕的-您 当然希望能够强制构造与某些参数。new FileStream()是什么意思?

所以基本上,你接受这个前提,即提供一个缺省构造函数是有意义的,我相信一旦你提供了你自己的构造函数,抑制它是非常有意义的。

我认为问题应该反过来: 如果没有定义任何其他构造函数,为什么不需要声明一个缺省构造函数呢?

非静态类必须使用构造函数。
所以我认为如果你没有定义任何构造函数,生成的缺省构造函数只是 C # 编译器的一个方便的特性,而且你的类如果没有构造函数也不会有效。所以隐式生成一个什么都不做的构造函数没有错。它看起来肯定比周围都是空构造函数要干净。

如果你已经定义了一个构造函数,那么你的类就是有效的,那么为什么编译器会假设你需要一个缺省构造函数呢?如果你不想要呢?实现一个属性来告诉编译器不要生成那个缺省构造函数?我觉得这不是个好主意。

这是编译器的一个便利函数。 如果您使用参数定义了构造函数,但是没有定义无参数构造函数,那么您不希望允许无参数构造函数的可能性就会大得多。

对于许多对象来说,使用空构造函数进行初始化毫无意义。

否则,您必须为要限制的每个类声明一个私有的无参数构造函数。

在我看来,对于一个需要参数才能运行的类来说,允许无参数构造函数并不是一种好的风格。

编辑。实际上,虽然我在第一个回答中说的是有道理的,但这才是真正的原因:

刚开始的时候 C 语言不是面向对象的(你可以采用面向对象的方法,但是它不能帮助你或者强制执行任何东西)。

然后是 C With Class,后来改名为 C + + 。C + + 是面向对象的,因此鼓励封装,并确保对象的不变构造,在任何方法的开始和结束,对象是在一个有效的状态。

这样做的自然结果是,强制类必须始终有一个构造函数来确保它以有效状态启动——如果构造函数不必做任何事情来确保这一点,那么空的构造函数将记录这一事实。

但是 C + + 的目标是尽可能地与 C 兼容,所有有效的 C 程序也是有效的 C + + 程序(不再是一个活跃的目标,而且 C 独立于 C + + 的进化意味着它不再适用)。

其中一个影响是 structclass之间的功能重复。前者采用 C 方式(默认情况下所有的公共内容) ,后者采用良好的 OO 方式(默认情况下所有的私有内容,开发人员积极地将他们想要的公共内容公开)。

另一个原因是,为了让 C struct,不能有构造函数,因为 C 没有构造函数,在 C + + 中有效,那么 C + + 看待它的方式,必须有一个意义。因此,尽管没有构造函数会违背主动确保不变量的面向对象实践,但 C + + 认为这意味着存在一个默认的无参数构造函数,就像它有一个空的主体一样。

所有的 C structs现在都是有效的 C + + structs,(这意味着它们与 C + + classes一样,都是 everything-member 和  一個公共继承) ,就好像它有一個單一的、無參數的构造函數一樣。

然而,如果你在 class或者 struct中放入了构造函数,那么你就是在用 C + +/OO 的方式而不是 C 的方式来做事情,并且不需要缺省构造函数。

因为它是一种速记方式,人们一直在使用它,即使在无法兼容的情况下(它使用了 C 语言以外的其他 C + + 特性)。

因此,当 Java 出现(在许多方面基于 C + +)和后来的 C # (在不同方面基于 C + + 和 Java)时,他们保留了这种方法,因为编码人员可能已经习惯了这种方法。

Stroustrup 在他的 C++程式语言中写到了这一点,甚至更多地关注了 C + + 的设计与演进中语言的“为什么”。

= = 原始答案 = =

就当这事没发生过。

假设我不想要一个无参数的构造函数,因为没有这个构造函数,我就不能让我的类进入有意义的状态。事实上,在 C # 中使用 struct就可以做到这一点(但是如果你不能在 C # 中有意义地使用全零和零的 struct,那么你最好使用一个不公开可见的优化,否则在使用 struct时就会出现设计缺陷)。

为了使我的类能够保护其不变量,我需要一个特殊的 removeDefaultConstructor关键字。至少,我需要创建一个私有的无参数构造函数,以确保没有调用代码调用默认值。

这使得语言更加复杂,最好不要这样做。

总之,最好不要认为添加构造函数就是删除默认值,最好认为没有构造函数就是添加无参数构造函数的语法糖。

只有当类没有构造函数时,才能构造缺省构造函数。编译器的编写方式只是将其作为备份机制提供。

如果您有一个参数化的构造函数,您可能不希望使用缺省构造函数创建对象。如果编译器提供了一个缺省构造函数,那么你就必须编写一个 no-arg 构造函数,并将其设置为私有,以防止使用 no-arg 创建对象。

此外,你忘记关闭或“私有化”缺省构造函数的可能性也更大,从而导致潜在的功能错误难以发现。

现在,如果希望以默认方式或通过传递参数创建对象,则必须显式定义 no-arg 构造函数。这是强制检查,编译器抱怨,否则,从而确保没有漏洞这里。

前提

这种行为可以被看作是 决定类有一个默认的公共无参数构造函数的自然延伸。基于被问到的问题,我们把这个决定作为一个前提,假设我们在这个例子中没有质疑它。

清除缺省构造函数的方法

因此,必须有一种方法来删除默认的公共无参数构造函数。这种清除可以通过以下方式实现:

  1. 声明一个非公共的无参数构造函数
  2. 当声明带参数的构造函数时,自动删除无参数构造函数
  3. 一些关键字/属性,指示编译器删除无参数构造函数(笨拙到很容易排除)

选择最佳解决方案

现在我们问自己: 如果没有无参数构造函数,那么必须用什么替换它?在什么类型的场景下,我们希望删除默认的公共无参数构造函数?

一切开始就绪。首先,必须使用带参数的构造函数或非公共构造函数替换它。其次,不需要无参数构造函数的情况包括:

  1. 我们根本不希望类被实例化,或者我们希望控制构造函数的可见性: 声明一个非公共构造函数
  2. 我们希望强制参数提供的建设: 用参数声明一个构造函数

结论

就是这样——正是 C # 、 C + + 和 Java 允许删除默认的公共无参数构造函数的两种方式。

这是因为当你没有定义一个构造函数时,编译器会自动为你生成一个不带任何参数的构造函数。当您需要构造函数提供更多内容时,可以重写它。这不是函数重载。所以编译器现在看到的唯一的构造函数就是带参数的构造函数。为了解决这个问题,如果构造函数没有传递任何值,您可以传递一个默认值。

我认为这是由编译器处理的。如果你在 ILDASM中打开 .net程序集,你会看到缺省构造函数,即使它不在代码中。如果你定义了一个参数化的构造函数,缺省构造函数就不会显示出来。

实际上,当您定义类(非静态)时,编译器提供了这个特性,认为您只是在创建一个实例。如果您想要执行任何特定的操作,您肯定会有自己的构造函数。

类需要一个构造函数,这是强制要求。

  • 如果您没有创建一个,无参数构造函数将自动提供给您。
  • 如果您不想要一个无参数的构造函数,那么您需要创建您自己的。
  • 如果两者都需要,可以手动添加无参数构造函数和基于参数的构造函数。

我会用另一个问题来回答你,为什么我们总是需要一个默认的无参数构造函数?在某些情况下,这是不需要的,因此开发人员可以根据需要控制添加或删除它。