【问题标题】:Why is the Compiler-Emitted C++ Default Constructor "bad"?为什么编译器发出的 C++ 默认构造函数“不好”?
【发布时间】:2011-05-25 00:49:46
【问题描述】:

谁能解释一下the following是什么意思?

如果您的类定义了成员变量并且没有其他构造函数,则您必须定义一个默认构造函数。否则编译器会为你做这件事,很糟糕。

他们所说的“糟糕”是什么?

【问题讨论】:

  • 我不知道,但它是错误的。你是从哪里弄来的?哦,臭名昭著的 Google C++ 风格指南。几乎所有内容都是错误的,但这是经典之作。如果您有兴趣,在 comp.lang.c++.moderated 上的样式指南上有一个巨大的线程。
  • 谷歌风格指南(见链接)。哦等等,什么?我实际上同意他们中的很多人,但其中一些让我惊掉下巴......
  • 我不知道“糟糕”,但考虑到必须维护代码的可怜的 schlub 的困境。如果成员变量是默认值,那么原始编码员是否打算使用默认值,或者他(不定代词意义)根本不知道更好?
  • 我一秒钟都不会购买那个风格指南条目。 很糟糕,IMO。聚合初始化优于手动构造函数。干燥;在这种情况下,构造函数不会添加任何内容。据他们说,std::array<> 需要重新设计吗?

标签: c++ default-constructor


【解决方案1】:

来自该链接的扩展:

“这样做的原因是,如果你 没有其他构造函数并且没有 定义一个默认构造函数, 编译器会为你生成一个。 这个编译器生成的构造函数 可能无法初始化您的对象 明智的。”

【讨论】:

  • @mkb:大声笑,我确实扩展了链接,但我只注意到definition,而不是优点/缺点。 谢谢
  • +1:确实。所以规则应该是“如果你关心有合理的默认初始值,你应该编写自己的默认构造函数”。但这是常识,真的。
  • @Oli 不,我不这么认为。我很少为我的类提供默认构造函数。
  • @Mehrad:哈哈哈.. 甚至我只看到了定义。谷歌的用户体验非常糟糕:P...他们应该使用一些颜色编码来更清楚地绘制扩展器的边框。
  • @Neil:确实。但我敢打赌,您确实提供的情况是您确实关心有合理的默认值吗?
【解决方案2】:

可以参考new Tnew T()differ when there is no ctor provided如何。

【讨论】:

  • +1 用于提供指向问题的强制性链接,并提供非常有用的答案!
  • 这个答案的重点肯定是 POD 与非 POD,而不是默认构造函数。还是我错过了什么。
【解决方案3】:

最好确保对象是在已知状态下创建的。默认情况下,原始变量不会设置为零,因此您最终可能会遇到并不总是出现的细微错误。通过将成员变量初始化为合理变量,一切都变得更加可预测。

【讨论】:

    【解决方案4】:

    默认构造函数的唯一问题是它只初始化编译器认为必须初始化的东西,而不是你可能认为需要初始化的东西。基本上,这意味着它将为具有默认初始化程序的对象调用初始化程序。它不会将指针或像int 这样的简单类型设置为健全的值等。如果这足够了,那么默认构造函数就不是“坏的”。当它不够时,这是一个错误(在您的代码中),您没有使用正确的初始化定义必要的默认构造函数。

    【讨论】:

      【解决方案5】:

      在谷歌风格指南中加一粒盐——或者一卡车的盐。

      确实,编译器生成的默认构造函数不一定会以有意义的方式初始化内置类型的成员。如果你想这样做,那么是的,它不这样做是不好的。 OTOH,如果您不想这样做,那么这样做也可能有点糟糕(浪费)。

      底线:有时会编写自己的默认 ctor,但它们倾向于例外,而不是规则。尽管有一些简单的经验法则可以涵盖 C++ 中的很多情况并且可以避免很多问题,但这确实不是其中之一——在这里您几乎确实需要知道编译器生成的 ctor 会做,如果你要自己写,你想要什么不同。

      【讨论】:

        【解决方案6】:

        在 Debug 构建中,大多数编译器用一些魔法值填充未初始化的空间,以便调试可靠。并且提供自定义构造函数会阻止某些 POD 优化。

        【讨论】:

          【解决方案7】:

          事实上,它只是为了确保人们明确声明任何对象的无效或默认状态是一个准则。

          这样,与实际执行相比,阅读代码时不会感到意外。

          但是,认为这是一个公司准则,用于确保每个人都遵循相同的规则,这不是你必须遵循它,因为谷歌这样做。

          事实上,如果你设法让你的所有成员对象在默认构造时都处于有效状态,或者强制你设置一个构造函数,那么这样的指导方针就没有充分的理由。

          【讨论】:

            【解决方案8】:

            如果您有任何原始类型作为成员变量(例如 int、float),则默认 ctor 不会初始化它们。类类型的成员变量将调用其默认 ctor。

            首选成员初始化器列表,因此您的用户提供的 ctor 可能为空:

            class Foo {
             int bar;
             float baz;
            
             Foo(): bar(0), baz(0.0f) { /* empty ctor body */ }
            
            };
            

            【讨论】:

              【解决方案9】:

              它不会将整数设置为 0 或将指针设置为 null。它将在具有构造函数的类型的对象上运行默认构造函数。

              有些人会称之为“不明智”。

              【讨论】:

                【解决方案10】:

                这似乎是规则3的一个过于简化的版本,你应该自己定义或离开编译器版本

                • 复制构造函数
                • 赋值运算符
                • 析构函数

                (请注意,通过定义自己的复制构造函数,编译器不会定义默认构造函数)。

                【讨论】:

                  【解决方案11】:

                  编译器构建的默认构造函数“什么都不做”,它甚至不会将对象占用的内存归零

                  【讨论】:

                  • 嗯,你有多大的错。它不会进行零初始化(为什么要这样做?零初始化的狂热是什么?),但它也不会“什么都不做”。
                  • 天哪,我不知道,显然不如你聪明。尽管我已经看到我相当一部分昂贵的系统被一些没有完全初始化的人拖垮了。是其他一些作者将编译器构建的默认 ctor 称为坏的,最初的问题是“为什么会这样”,而我只是提供了许多可能的观点中的一种,作为再次挑选的“素材” ----他们为贬低他人而激动,为遭受的苦难而欢欣鼓舞,同时贡献很少。
                  • 很抱歉,您认为我的贡献很少(如果这是您的意思)。客观地看,我过去一个月的贡献似乎是你过去一年的 6 倍左右。
                  • 啊哈,所以我们得到了它的真相,对一个人的追求。酷,在那种情况下,我认输,你赢,你的贡献将永远取代我的贡献,因为我正在撤回我的贡献。恭喜,又倒了一个。
                  • 你指责我没有贡献,然后当我为自己辩护时,指责我“一事无成”?酷。
                  猜你喜欢
                  • 1970-01-01
                  • 2022-12-06
                  • 2019-05-17
                  • 2016-10-16
                  • 2020-08-29
                  • 1970-01-01
                  • 2022-01-15
                  • 1970-01-01
                  • 2015-09-05
                  相关资源
                  最近更新 更多