C ++ 11成员类初始化顺序[重复]

Posted

技术标签:

【中文标题】C ++ 11成员类初始化顺序[重复]【英文标题】:C++11 order of member class initialization [duplicate] 【发布时间】:2018-04-23 07:38:58 【问题描述】:

我有代码:

struct testInit 
    testInit(int i1_) 
        : i1(i1_) 
        std::cout << "testInit::testInit" << std::endl;
    

    int initI2() 
        std::cout << "testInit::initI2::i1: " << i1 << std::endl;
        return i1;
    

    const int i1 = 1;
    const int i2 = initI2();

;

int main() 
    testInit ti(3);

    std::cout << "i1: " << ti.i1 << std::endl;
    std::cout << "i2: " << ti.i2 << std::endl;

    return 0;

输出为:

testInit::initI2::i1: 3
testInit::testInit
i1: 3
i2: 3

所以,我想知道类成员的初始化顺序到底是什么。我认为输出应该是 i1: 3 和 i2: 1 - 这显然是错误的 - 但为什么呢?

【问题讨论】:

变量总是按照它们的声明顺序初始化,所以无论你如何初始化它们,在你的代码中i1 然后i2。你的构造函数相当于testInit(int _i1) : i1(_i1), i2(initI2()) “我认为输出应该是……”为什么? 【参考方案1】:

我想知道类成员的初始化顺序到底是什么

成员在类定义中按照声明顺序总是initialized,所以i1会先被初始化,然后i2

3) 然后,按照类定义中的声明顺序初始化非静态数据成员。

对于testInit::testInit(int),成员初始化器列表和default member initializer都在i1上指定;默认成员初始化程序将被忽略。

如果一个成员有一个默认的成员初始化器,并且还出现在构造函数的成员初始化列表中,则忽略默认的成员初始化器。

那么对于testInit ti(3);i1首先通过成员初始化器列表用3初始化,然后i2通过默认成员初始化器用initI2()初始化,然后它也将是3

【讨论】:

非常了不起,你已经用 const 回答了关于 default 和 mem-initializer 的相同问题。 @codekaizer 他们不一样;我认为这个问题的重点是初始化顺序(成员初始化器列表和默认成员初始化器的混合使其变得复杂),不是吗? 真的。我的意思是有些元素是相同的。但真正的问题是关于初始化顺序。你用这个启发了我。 感谢您的解释!【参考方案2】:

来自dcl.init.list#4

在花括号初始化列表的初始化列表中, 初始化子句,包括任何由包扩展产生的子句, 按照它们出现的顺序进行评估。也就是说,每个值 与给定初始化子句相关的计算和副作用 在每个值计算和相关的副作用之前排序 以逗号分隔的任何初始化子句 初始化列表的列表。

你的情况是:

testInit ti(3); // replaces the default initialization of i1 using the mem-initializer 

然后i2 使用initI2() 默认初始化为3,因为此时i1 已经是3(即testInit 已经定义好)。

【讨论】:

【参考方案3】:

我没有引用标准 - 这已经由另一个答案完成 - 我给你一些经验数据:

#include <iostream>

struct testInit 
    testInit(int i1_) 
        : i1(i1_) 
        std::cout << "testInit::testInit" << std::endl;
    

    int initI2() 
        std::cout << "testInit::initI2::i1: " << i1 << std::endl;
        return i1;
    

    const int i2 = initI2();
    const int i1 = 1;
;

int main() 
    testInit ti(3);

    std::cout << "i1: " << ti.i1 << std::endl;
    std::cout << "i2: " << ti.i2 << std::endl;

    return 0;

输出:

testInit::initI2::i1: 0
testInit::testInit
i1: 3
i2: 0

像这样进行实验当然绝不“足以”确定 C++ 的某些行为,仅仅是因为您很容易遇到未定义的行为(上面的代码实际上有)或实现定义的行为。

不过:只是尝试一下。它不会造成伤害,而且您可能会更好地记住您“发现”的内容,而不是您在某处读过的内容。

由于访问未初始化的i1而导致的未定义行为

【讨论】:

也许输出这样的原因会很有用。 @codekaizer 这不是我回答的重点。毕竟,它在(现在可用的)其他答案中得到了明确的回答。我的回答暗示,尝试一下会比在这里提问更快地给出答案......并且可能会比阅读标准中的段落后更好地记住。 如果初始化的顺序是实现定义呢?这将为您提供仅在一个平台/编译器上的行为。考虑关于order of destruction of function parameters 的相同问题。 我不建议尝试使用 C++ 就足够了。这是非常不幸的,但是有足够多的 UB/未指定/实现定义的东西让你崩溃。 @passerby 当然我确实意味着一个人应该在实验后停止,也不是“足够”。在做的时候学习东西比仅仅阅读它更好。编辑了我的答案以清楚地说明这一点。

以上是关于C ++ 11成员类初始化顺序[重复]的主要内容,如果未能解决你的问题,请参考以下文章

Java类成员初始化顺序

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

java中类成员初始化顺序

类加载顺序

派生类(构造函数)中基类的成员变量初始化顺序

静态构造函数, 静态成员初始化/调用顺序