【问题标题】:Why must initializer list order match member declaration order?为什么初始化列表顺序必须匹配成员声明顺序?
【发布时间】:2014-08-08 17:21:19
【问题描述】:

如果初始化器列表顺序与类中的变量顺序不匹配,为什么 gcc 会抛出乱码?

class myClass
{
public:
   int A;
   int B;
   myClass();
};

myClass::myClass() :
B(1),
A(2)
{}

将导致:

file.h:274: warning: 'myClass::A' will be initialized after
file.h:273: warning:   'int myClass::B
file.cpp:581: warning:   when initialized here

发出这种警告有什么具体原因吗?以不同于类中定义的顺序初始化类的变量是否存在任何风险?

(请注意,有 a question 涉及主题,但答案几乎是“因为它应该是这样”,而没有给出任何理由说明 为什么 它应该被订购,或者是什么这是错误的 - 我想知道为什么存在这样的限制 - 有人可以举一个可能适得其反的例子吗?)

【问题讨论】:

  • @ShafikYaghmour:该标准不构成限制,因为这是其作者的反复无常。每个限制都是一些考虑的结果。所以,不,到目前为止,“因为标准是这样说的”还不是一个充分的答案。当然,该标准的某些决定可能存在问题或完全错误 - 但即使是这些问题和错误也源于某些根源。
  • 所以也许你的问题应该是“为什么初始化的顺序是声明的顺序?”而不是询问警告。
  • “发出这种警告有什么具体原因吗?” - 一个原因是为了帮助你。
  • @dyp 你只需要添加一个事实,即有一个类定义和可能有许多 mem-init-lists。
  • @dyp:如果遵循无法实现的mem-init-list,则破坏顺序与构造顺序相反(一个成员可以依赖另一个成员)。 (我会写一个答案,但是在电话上打字很痛苦:))

标签: c++ constructor initializer-list


【解决方案1】:

警告表明,无论您在构造函数初始化列表中使用的顺序如何,标准都要求非静态数据成员按照声明的顺序进行初始化。我们可以通过转到 draft C++ standard 部分 12.6.2 Initializing bases and members 段落 10 看到这一点:

在非委托构造函数中,初始化在 以下顺序:

包括:

然后,非静态数据成员按照它们的顺序初始化 在类定义中声明(同样不管 内存初始化器)。

为什么标准要求这样做?我们可以在 Bjarne Stroustrup 的论文 The Evolution of C++: 1985 to 1989 中的 6 部分找到一个理由:

初始化按照声明的顺序进行 基类在成员之前初始化的类,

[...]

忽略初始化器顺序的原因是为了保留 构造函数和析构函数调用的通常 FIFO 排序。允许两个 构造函数使用不同的基初始化顺序和 成员将限制实现使用更多动态和更多 昂贵的策略

【讨论】:

    【解决方案2】:

    该警告试图防止您可能依赖数据成员的错误顺序的情况。假设您认为 B 在 A 之前初始化,然后您执行以下操作:

    myClass::myClass() :
    B(42), A(B) {}
    

    在这里,您有未定义的行为,因为您正在从未初始化的B 读取。

    【讨论】:

    • 这确实 回答问题,因为 IF 允许以任何顺序指定初始化 [list],然后您所写的在这里,即 B 在 A 之前,A 取决于 B,就可以了。本质上,您只是告诉我们,标准规定非静态数据成员初始化按照成员声明的顺序进行,而没有给出该决定背后的任何逻辑。
    • @DanielGoldfarb 问题不是问为什么标准指定了非静态数据成员的特定初始化顺序。它是在询问为什么实现会在某种情况下发出警告,而我已经回答了这个问题。
    • 也许我读错了。我知道这个问题开始询问编译器警告,但除此之外还有更多。 “是否有任何风险......顺序与定义的不同......”并参见问题末尾的“注释”,表明标准说明如此并没有给出标准的理由。您的回答确实给出了警告的理由,并提出了一个很好的观点;但在我看来,它首先没有回答(引用问题末尾的注释)“为什么应该订购它”。恕我直言,Shafik Yaghmour 的回答更完整。
    【解决方案3】:

    初始化列表的顺序确实重要。 类头中的成员声明定义了初始化顺序。

    这是设计使然并且是必需的,因为您可以有多个具有完全不同的初始化列表顺序的 ctor。

    因此,您的成员将始终按照声明的顺序进行初始化。

    【讨论】:

    • 这是设计和必需的 => 为什么这是一个问题?为什么每个构造函数都不能按照他们喜欢的顺序初始化成员?
    • stackoverflow.com/questions/4037219/…。由于没有多个 dtor,因此反初始化必须按照与初始化相反的顺序进行。
    • 我知道,我只是在试探您编辑您的答案以在您的答案中包含“必需”的理由。顺便说一句,这完全是任意的; C++ 做出了一个明确的选择,即销毁顺序将总是与构造顺序相反,并且记住构造顺序被认为没有用(不要为不需要的东西付费 + 单独编译模型),因此构建顺序和销毁顺序都是固定的。可以使用其他替代方案。
    • @MatthieuM。我很好奇这只是您的个人知识还是该设计记录在某个地方?
    • @ShafikYaghmour:哦!感谢您挖掘参考。我很敬畏这样一个“小”的功能居然有这样的想法!
    【解决方案4】:

    我认为这只是清洁问题。类成员的声明顺序在类的头文件中定义,构造函数在.cpp文件中定义。

    假设您要为该类添加另一个构造函数,并且仅通过查看具有B(1), A(2).cpp 文件,您可能会错误地认为B 将在A 之前初始化,因此写错了(例如,B(42), A(B))。警告是从一开始就防止这种情况发生。

    【讨论】:

      猜你喜欢
      • 2021-07-19
      • 1970-01-01
      • 2021-09-12
      • 1970-01-01
      • 2019-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多