为什么默认的无参数构造函数在使用参数创build时会消失

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

这种行为的原因是什么? 这只是一个“安全措施/猜测”,说:“如果你已经创build了自己的构造函数,你可能不希望这个隐含的构造函数挂在”? 还是它有一个技术的原因,使编译器不可能添加一个你自己创build一个构造函数?

编译器如果添加了自己的编译器,就没有理由添加构造函数 – 编译器可以做任何事情! 但是,你必须看看什么是最有意义的:

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

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

语言必须这样devise,当然没有技术上的原因。

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

  1. 根本没有默认的构造函数
  2. 当前的情况
  3. 总是默认提供一个默认的构造函数,但允许明确地禁止它
  4. 总是提供一个默认的构造函数不允许它被压制

选项1是有吸引力的,因为我编写得越less,我真的想要一个无参数的构造函数。 有一天,我应该算我实际上最终使用默认构造函数的频率…

选项2我很好。

对于其他语言,选项3违背了Java和C#的stream程。 除非明确地指出使事情比在Java中默认的更私有化,否则绝不会有任何明确的“移除”。

选项4是可怕的 – 你绝对希望能够用某些参数强制施工。 什么new FileStream()甚至意味着什么?

所以基本上, 如果您接受提供默认构造函数的前提是有意义的,那么我认为一旦您提供了自己的构造函数就可以将其压制。

编辑。 其实,我在第一个答案中所说的是有效的,这才是真正的原因。

一开始就有C. C不是面向对象的(你可以采取面向对象的方法,但是它不能帮助你或者执行任何事情)。

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

与此相关的自然事情是强制一个类必须始终有一个构造函数来确保它以一个有效的状态开始 – 如果构造函数不需要做任何事情来确保这一点,那么空的构造函数将logging这个事实。

但是C ++的一个目标是与C兼容,所有有效的C程序都是有效的C ++程序(不再是一个活跃的目标,而C的独立于C ++的发展意味着它不再成立)。

其中一个影响是structclass之间function的重复。 前者是C方式(默认情况下是公开的),后者是以一种好的OO方式(默认情况下是私有的,开发者主动公开他们想要公开的东西)做事情。

另一个是为了让一个C struct (因为C没有构造函数而不能有构造函数)在C ++中是有效的,那么对于C ++来说,就必须有这个意义。 所以,虽然没有构造函数会违背OO主动确保一个不变的做法,但C ++把这意味着有一个默认的无参数构造函数,它有一个空的主体。

所有的C structs现在都是有效的C ++ structs (这意味着它们与C ++ classes ,都是成员和inheritance – 公共的),就像它有一个单参数构造函数一样。

但是,如果你把一个构造函数放在一个classstruct ,那么你就是用C ++ / OO方式而不是C方式来执行,并且不需要默认的构造函数。

既然它是一个简写,那么即使在兼容性不可能的情况下,人们也会继续使用它(它使用了C中没有的其他C ++特性)。

因此,当Java出现时(基于C ++)和后来的C#(基于C ++和Java以不同的方式),他们保持这种方式,因为已经可以使用编码器。

Stroustrup在他的“C ++编程语言”(The C ++ Programming Language)中写到了这一点,甚至更多的关注于“ The Design and Evolution of C ++ ”中的语言“whys”。

===原创答案===

假设这没有发生。

比方说,我不想要一个无参数的构造函数,因为我不能让我的类没有一个有意义的状态。 事实上,这是C#中的struct可能发生的事情(但是如果你不能在C#中使用全零和全零struct ,最好使用非公开可见的优化,否则在使用struct有一个devise缺陷)。

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

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

总之,最好不要考虑添加一个构造函数作为删除默认值,最好不要将构造函数看作是添加无参数构造函数的语法糖。

如果你自己没有做任何事来控制对象的创build,那么添加默认的无参数构造函数。 一旦你创build了一个单独的构造函数来控制,编译器会“退后”,让你完全控制。

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

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

对于许多对象来说,使用空的构造函数进行初始化是没有意义的。

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

在我看来,允许一个需要参数运行的类的无参数构造函数是不好的风格。

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

构造函数对于非静态类是强制的。
所以我想如果你还没有定义任何构造函数,那么生成的默认构造函数只是C#编译器的一个方便function,如果没有构造函数,你的类也不会有效。 因此隐式生成一个什么也不做的构造函数没什么问题。 它肯定比在周围有空的构造器更清洁。

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

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

如果你有一个参数化的构造函数,你可能不希望使用默认构造函数创build一个对象。 如果编译器提供了一个默认的构造函数,那么必须编写一个无参数的构造函数,并将其设置为私有的,以防止使用无参数创build对象。

此外,您忘记禁用或“隐藏”默认构造函数的可能性更高,从而导致潜在的function错误难以捕捉。

而现在,如果您希望以默认方式创build对象或传递参数,则必须明确定义无参数构造函数。 这是强烈检查,编译器抱怨,否则,从而确保没有漏洞在这里。

前提

这种行为可以被看作是类的默认公共无参数构造函数的自然延伸。 根据已经提出的问题,我们以这个决定为前提,并假设在这种情况下我们并不怀疑。

删除默认构造函数的方法

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

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

select最佳解决scheme

现在我们问自己: 如果没有无参数的构造函数,它必须replace成什么? 在什么types的情况下,我们想要删除默认的公共无参数构造函数?

事情开始落实。 首先,它必须被具有参数的构造函数replace,或者用非公共的构造函数replace。 其次,你不想要一个无参数构造函数的场景是:

  1. 我们不希望类被实例化,或者我们想控制构造函数的可见性: 声明一个非公有的构造函数
  2. 我们要强制在构造上提供参数: 用参数声明一个构造函数

结论

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

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

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

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