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成员类初始化顺序[重复]的主要内容,如果未能解决你的问题,请参考以下文章