类静态变量初始化顺序

Posted

技术标签:

【中文标题】类静态变量初始化顺序【英文标题】:Class static variable initialization order 【发布时间】:2018-12-19 03:04:45 【问题描述】:

我有一个 A 类,它有两个静态变量。我想用另一个不相关的静态变量初始化一个,就像这样:

#include <iostream>
class A

public:
    static int a;
    static int b;
;

int A::a = 200;
int a = 100;
int A::b = a;
int main(int argc, char* argv[])

    std::cout << A::b << std::endl;

    return 0;

输出是 200。那么,谁能告诉我为什么?

【问题讨论】:

我不是语言专家,但我最好的猜测是,只要你在作业语句中写下A::b,你就将范围限定为A 类和a 那里.要访问全局a,它应该类似于int A::b = ::a; @AmirRasulov:嗯,到目前为止,有 11 人(包括我)认为这个问题表明了研究努力、有用并且很清楚。 @AmirRasulov - 这是一个问答网站。 Q 属于这里的门槛是它的客观质量(就像 Bathsheba 指出的那样),而不是答案对你或我来说听起来多么简单。 @AmirRasulov 您可以对本网站上的每个问题使用该论点... @AmirRasulov (0) 这是话题。 (1) 很简单 ⇒ 很多人都明白。 (因此,不幸的是,如果它在一个晦涩的话题中,那么无论它研究得多么好,它都会得到更少的支持)(2)它在 HNQ 上。 【参考方案1】:

因为名称查找将a 解析为A::a。如果你想这样做,你需要手动解析范围:

int A::b = ::a;
        // ^ Global scope resolution

Live Example

【讨论】:

【参考方案2】:

c++draft/class.static

如果在成员的 declarator-id 之后的静态成员的定义中使用了 unqualified-id,和名称查找 ([basic.lookup.unqual]) 会发现不合格的-id 引用成员类(或成员类的基类)的静态成员、枚举数或嵌套类型,unqualified-id 转化为qualified-id 表达式 strong> 其中nested-name-specifier 命名了引用该成员的类范围。 [ 注:非静态数据成员和非静态成员函数的使用限制见[expr.prim.id]。 — 尾注 ]

它表示在您的情况下,非限定 ID 已转换为限定 ID 表达式。

int A::b = a;

您可以设置qualified-id,但没有像这样的nested-name-specifier。

int A::b = ::a;

【讨论】:

【参考方案3】:

那么,谁能告诉我为什么?

这在basic.scope.class/4中有明确说明,强调我的:

延伸到或超过结尾的声明的潜在范围 类定义也延伸到其定义的区域 成员定义,即使成员是在词法外部定义的 类(这包括静态数据成员定义,嵌套类 定义和成员函数定义,包括成员 函数体和声明部分的任何部分 declarator-id 之后的定义,包括 参数声明子句和任何默认参数)。

因此,当你有

int A::a = 200;
int a = 100;
int A::b = a; // note the '::' scope resolution operator
              // OUTPUT: 200

a 实际上是指A::a,因为类范围A::b扩展

如果你有:

int A::a = 200;
int a = 100;
int b = a; // note b is not A::b
           // i.e. without the '::', scope resolution operator
           // OUTPUT: 100

a 将引用(全局)::a,因为这里的b 不是class A 的成员,即没有类范围扩展。

【讨论】:

非常感谢您的回答。 @QuantumPlus Just upvote instead。 “谢谢”cmets 不鼓励。另外,如果你看到这样的cmets,你可以flag them。【参考方案4】:

根据查找规则,这是正确的。 [basic.lookup.unqual]/13 说:

用于定义类 X 的静态数据成员的名称 (在静态成员的限定 ID 之后)被查找,就好像 在 X 的成员函数中使用了名称。[ 注意:[class.static.data] 进一步描述了对名称使用的限制 静态数据成员的定义。 — 尾注 ]

由于查找不合格的a就像你在一个成员函数中,它必须首先找到成员A::aA::aA::b 的初始化顺序不会影响查找,但会影响结果的定义程度。

【讨论】:

任何人都可以说明为什么会这样决定行为的任何理由吗?我发现问题的代码非常混乱。 @Chiel:开玩笑地说,我认为这是解决这种歧义的最简单一致的方法。 @Bathsheba。但是据我所知,该规则会产生歧义而不是解决它。 @Chiel - 名称查找必须从 某个范围 开始,这只是一个合理的默认值。想象一下,我们的类中有一个enum class,我们想使用它的枚举器之一来初始化静态成员。你会更自然地写A::a = EnumClass::THING; 而不是A::a = A::EnumClass::THING; @Chiel - 我还想补充一点。这不是使这种模棱两可的规则。规则很好地消除了歧义。正是程序员选择了不好的名字,才使这变得模棱两可。

以上是关于类静态变量初始化顺序的主要内容,如果未能解决你的问题,请参考以下文章

转!!关于java类初始化顺序

C#类的初始化顺序

一文详解:Java中父子类静态块构造块构造方法成员变量之间的初始化先后顺序与执行先后顺序

Java初始化顺序(静态变量静态初始化块实例变量实例初始化块构造方法)

类变量块构造器继承初始化顺序,终极解答。

类加载顺序--总结版