【问题标题】:Why does the default parameterless constructor go away when you create one with parameters为什么在创建带参数的构造函数时默认的无参数构造函数消失
【发布时间】:2012-08-01 07:04:24
【问题描述】:

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

这种行为的原因是什么?这只是一种“安全措施/猜测”,说“如果您创建了自己的构造函数,您可能不希望这个隐含的构造函数存在”? 还是有技术原因导致编译器无法在您自己创建构造函数后添加?

【问题讨论】:

  • 您可以将 C++ 添加到具有此行为的语言列表中。
  • 在 C++ 中,您现在可以说 Foo() = default; 以恢复默认值。
  • 如果你的带参数构造器可以有所有参数的默认参数,那么它会与内置的无参数构造器冲突,因此需要在创建时将其删除。
  • 想象一下出现在第一个编译器的创始人之间的辩论中,以制定默认构造函数要求以及它会激发的激烈讨论。
  • @HenkHolterman C++ 不仅仅是另一个具有这种行为的,而是它的发起者,它允许 C 兼容性,正如 Stroustrup 在 The Design and Evolution of C++ 中所讨论的那样正如我更新的答案中所总结的那样。

标签: c# java c++ default-constructor


【解决方案1】:

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

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

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

【讨论】:

  • 我认为你的其余答案几乎证明你的第一句话是错误的。
  • @KonradRudolph,第一句话说编译器可以在这种情况下添加一个构造函数 - 答案的其余部分解释了为什么它没有(对于问题)
  • 好吧,不。如果我们从零开始设计一种面向对象的语言,那么没有构造函数最明显的含义就是,“你忽略了添加一个确保类不变的构造函数”,并且会引发编译错误。
【解决方案2】:

语言必须以这种方式设计当然没有技术原因。

我可以看到四个有点现实的选项:

  1. 根本没有默认构造函数
  2. 当前情况
  3. 总是默认提供默认构造函数,但允许显式抑制它
  4. 总是提供默认构造函数允许它被抑制

选项 1 有点吸引人,因为我编写的代码越多,我真正想要一个无参数构造函数的频率就越低。总有一天我应该计算一下我实际上多久使用一次默认构造函数...

选项 2 我没问题。

对于语言的其余部分,选项 3 与 Java 和 C# 的流程背道而驰。从来没有任何东西是你明确“删除”的,除非你明确地让事情变得比 Java 中的默认设置更私密。

选项 4 太可怕了 - 你绝对希望能够使用某些参数强制构造。 new FileStream() 到底是什么意思?

所以基本上,如果你接受提供默认构造函数的前提是有意义的,我相信一旦你提供了自己的构造函数就禁止它是很有意义的。p>

【讨论】:

  • 我喜欢选项 3,因为当我写东西时,我需要更频繁地同时拥有两种类型的构造函数,而不是只有带参数的构造函数。所以我宁愿每天一次将一些无参数构造函数设为私有,然后每天编写 10 次无参数构造函数。但这可能只是我,我正在写很多可序列化的类......
  • @PetrMensik:如果你的无参数构造函数真的需要什么都不做,那么它肯定不会比“显式删除”语句花费更多的代码。
  • @PetrMensik:对不起,我的错,是的。这绝对与我的经验相反 - 我认为自动包含某些内容也是更 dangerous 的选项......如果你不小心最终没有排除它,你可以搞砸你的不变量等。
  • #4 是 C# structs 的情况,无论好坏。
  • 我喜欢选项 1,因为它更容易理解。没有您在代码中看不到的“神奇”构造函数。使用选项 1,编译器应该在(或者甚至不允许?)非静态类没有实例构造函数时发出警告。怎么样:“没有找到类 的实例构造函数。您的意思是声明类静态吗?”
【解决方案3】:

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

一开始是 C。C 不是面向对象的(您可以采用 OO 方法,但它对您没有帮助或强制执行任何操作)。

然后是带有类的 C,后来改名为 C++。 C++ 是面向对象的,因此鼓励封装,并确保对象的不变性——在构造以及任何方法的开始和结束时,对象都处于有效状态。

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

但是 C++ 的一个目标是尽可能地与 C 兼容,所有有效的 C 程序也是有效的 C++ 程序(不再是一个积极的目标,而 C 独立于 C++ 的演变意味着它不再成立)。

这样做的一个影响是structclass 之间的功能重复。前者以 C 方式做事(默认一切公开),后者以良好的 OO 方式做事(一切默认私有,开发人员主动公开他们想要公开的内容)。

另一个原因是,为了使 C struct(由于 C 没有构造函数而无法具有构造函数)在 C++ 中有效,那么对于 C++ 的方式来说,它必须具有意义看着它。因此,虽然没有构造函数会违背积极确保不变量的 OO 实践,但 C++ 认为这意味着存在一个默认的无参数构造函数,它的行为就像它有一个空的主体。

所有 C structs 现在都是有效的 C++ structs,(这意味着它们与 C++ classes 相同,所有内容 - 成员和继承 - 公共)从外部处理,就好像它有一个单一的无参数构造函数.

但是,如果您确实在 classstruct 中放置了构造函数,那么您是在使用 C++/OO 方式而不是 C 方式进行操作,并且不需要默认构造函数。

由于它是一种速记,即使在其他情况下无法兼容时,人们仍会继续使用它(它使用了 C 语言中没有的其他 C++ 特性)。

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

Stroustrup 在他的C++ 编程语言 中写到了这一点,甚至更多,在C++ 的设计和演变 中更关注语言的“为什么” .

=== 原始答案 ===

假设这没有发生。

假设我不想要一个无参数的构造函数,因为没有它我无法将我的类置于有意义的状态。事实上,这在 C# 中使用 struct 可能会发生这种情况(但如果你不能在 C# 中有意义地使用全零和空值 struct,那么你充其量只能使用非公开可见的优化,否则在使用 struct 时存在设计缺陷。

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

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

总而言之,最好不要将添加构造函数视为删除默认值,最好将根本没有构造函数视为添加无参数构造函数的语法糖。

【讨论】:

  • 再一次,我看到有人觉得这是一个足够糟糕的答案,可以否决它,但不能打扰我或其他任何人。你知道,这可能很有用。
  • 我的回答也一样。好吧,我在这里没有看到任何问题,所以我 +1。
  • @Botz3000 我不在乎分数,但如果他们有批评,我宁愿阅读。尽管如此,它确实让我想到了要添加到上面的东西。
  • 再一次在没有任何解释的情况下投了反对票。拜托,如果我遗漏了一些如此明显以至于不需要解释的东西,那就假设我很愚蠢,无论如何都要帮我解释一下。
  • @jogojapan 我也不是专家,但是作为做出决定的人的那个人写了这件事,所以我不必这样做。顺便说一句,这是一本非常有趣的书。很少涉及低级技术,很多关于设计决策,有趣的是有人在演讲之前说你应该在提出新功能之前必须捐献一个肾脏(你会很努力地思考,只做两次)引入模板和异常。
【解决方案4】:

如果您自己不执行任何操作来控制对象创建,则会添加默认的无参数构造函数。一旦您创建了一个构造函数来获得控制权,编译器就会“退出”并让您拥有完全的控制权。

如果不是这样,如果您只想通过带参数的构造函数构造对象,则需要一些显式方法来禁用默认构造函数。

【讨论】:

  • 你实际上有这个选项。将无参数构造函数设为私有。
  • 那不一样。类的任何方法,包括静态方法,都可以调用它。我更喜欢让它完全不存在。
【解决方案5】:

这是编译器的一个便利功能。 如果你定义了一个带参数的构造器,但没有定义一个无参数的构造器,那么你不想允许一个无参数的构造器的可能性就会高很多。

对于许多使用空构造函数初始化没有意义的对象来说就是这种情况。

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

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

【讨论】:

    【解决方案6】:

    我认为问题应该是相反的:如果你还没有定义任何其他构造函数,为什么你不需要声明一个默认构造函数?

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

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

    【讨论】:

      【解决方案7】:

      只有当类没有构造函数时,才能构造默认构造函数。编译器的编写方式仅将其作为备份机制。

      如果您有参数化构造函数,您可能不希望使用默认构造函数创建对象。如果编译器提供了默认构造函数,您将不得不编写一个无参数构造函数并将其设为私有,以防止使用无参数创建对象。

      此外,您更有可能忘记禁用或“私有化”默认构造函数,从而导致难以捕获的潜在功能错误。

      现在,如果您希望以默认方式或通过传递参数创建对象,则必须显式定义无参数构造函数。这是经过严格检查的,否则编译器会报错,从而确保这里没有漏洞。

      【讨论】:

        【解决方案8】:

        前提

        这种行为可以看作是类具有默认公共无参数构造函数的决定的自然扩展。根据所提出的问题,我们将此决定作为前提,并假设在这种情况下我们不会质疑它。

        移除默认构造函数的方法

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

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

        选择最佳解决方案

        现在我们扪心自问:如果没有无参构造函数,必须用什么来代替?在什么类型的场景下,我们希望去掉默认的公共无参构造函数?

        事情开始落实到位。首先,它必须要么用带参数的构造函数替换,要么用非公共构造函数替换。其次,不想要无参数构造函数的场景有:

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

        结论

        我们有了它——正是 C#、C++ 和 Java 允许删除默认公共无参数构造函数的两种方式。

        【讨论】:

        • 结构清晰,易于理解,+1。但是关于上面的(3.):我不认为构造函数移除的特殊关键字是一个如此尴尬的想法,实际上C++11为此引入了= delete
        【解决方案9】:

        我认为这是由编译器处理的。如果您在ILDASM 中打开.net 程序集,您将看到默认构造函数,即使它不在代码中。如果您定义参数化构造函数,则不会看到默认构造函数。

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

        【讨论】:

          【解决方案10】:

          这是因为当您不定义构造函数时,编译器会自动为您生成一个不带任何参数的构造函数。当你想从构造函数中得到更多东西时,你会覆盖它。这不是函数重载。因此,编译器现在看到的唯一构造函数是带有参数的构造函数。为了解决这个问题,如果构造函数没有传递值,你可以传递一个默认值。

          【讨论】:

            【解决方案11】:

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

            • 如果不创建,系统会自动给你无参构造函数。
            • 如果您不想要无参数构造函数,则需要创建自己的构造函数。
            • 如果您同时需要无参数构造函数和基于参数的构造函数,您可以手动添加它们。

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-06-14
              • 2014-07-09
              相关资源
              最近更新 更多