成员初始化器列表中的赋值操作
Posted
技术标签:
【中文标题】成员初始化器列表中的赋值操作【英文标题】:Assignment operation in member initializer lists 【发布时间】:2019-07-29 09:50:26 【问题描述】:我有以下 Stack 类。
class Stack
public:
int size;
int* x;
Stack() : size(10), x(new int[10])
Stack(const Stack& s) : x(new int[size=s.size])
;
注意复制构造函数中的赋值。该代码工作正常,编译良好,编译器(gcc)即使使用-Wall -Wextra
标志也不会抱怨。编译器会自动将编译器改写成这个吗?
Stack(const Stack& s) : size(s.size), x(new int[size])
或者还有什么其他的魔法?我注意到当我更改定义顺序时,编译器会抱怨非按顺序初始化。所以我认为这是我提到的情况。我在文档中找不到任何内容,ASM 输出也对我没有帮助。
【问题讨论】:
【参考方案1】:来自constructors 上的 Cppreference.com 页面:
出现在表达式列表或大括号初始化列表中的名称在构造函数的范围内进行计算:
所以是的,这里的size
指的是构造函数范围内的this->size
,而赋值size=s.size
是一个有效的表达式。
不用说,你不应该期望这会通过代码审查:)
【讨论】:
【参考方案2】:无论您在成员初始化列表中指定它们的顺序如何,类的成员始终按声明顺序进行初始化。如果您省略它们,它们将被默认初始化。这样做:
Stack(const Stack& s) : x(new int[size=s.size])
表示size
默认首先初始化。这留下了一个不确定的值(因为基本类型应该是默认初始化的)。然后评估x
的初始化程序。评估new int[size=s.size]
的一部分涉及赋值表达式size=s.size
,它修改size
作为副作用。所以你的代码在这里是正确的,尽管可能会引起人们的注意。
当您切换成员的顺序时,分配发生在之前 size
应该被初始化。这会使您的代码代码对未定义的行为开放。
【讨论】:
您确定分配必须在默认初始化之后 发生吗? (是否在之后排序?) @MartinBonner - 我隐约记得读过一次。让我仔细检查一下。 @MartinBonner 是的。 there is a note here,如果您真的需要,我可以找到适用的实际条款。 @MartinBonner - 将timsong-cpp.github.io/cppwp/intro.execution#5.4 与timsong-cpp.github.io/cppwp/intro.execution#9 结合起来,并认为初始化按声明顺序进行。所以,是的,我现在确定。【参考方案3】:编译器会自动将编译器改写成这个吗?
Stack(const Stack& s) : size(s.size), x(new int[size])
没有。
Stack(const Stack& s) : x(new int[size=s.size])
可以认为是存在
Stack(const Stack& s) : size(), x(new int[size=s.size])
但这并不是真的,因为实际上编写 size()
会初始化它,这意味着它是零初始化的,但由于是编译器合成了初始化,默认初始化[1] 发生意味着它没有被初始化。然后在x
的初始化中为其赋值。这是“安全的”,这意味着它在这种情况下有效,但我不推荐它。
Stack(const Stack& s) : size(s.size), x(new int[s.size])
初始化两个成员,如果你改变了他们在类中的顺序,你仍然会有正确的行为。
【讨论】:
一定会喜欢一门初始化需要整篇论文的编程语言,嗯? :) 很抱歉吹毛求疵,但无论如何,OP 所在的地方都很危险。 @StoryTeller 不用担心。我宁愿挑选尼特,因为它离兔子洞还有很长的路要走:)【参考方案4】:不,它重写为:
class Stack
public:
int size;
int* x;
Stack() : size(10), x(new int[10])
Stack(const Stack& s) :size(), x(new int[size=s.size])
;
size()
将是对象size
的默认构造函数,但int
s 没有,所以它只是一个空语句,size
保持未初始化。 是吗?
我会说这段代码可能会产生未定义的行为。成员变量按照10.9.2 Initializing bases and members 的声明顺序进行初始化。但未定义用于初始化的表达式的评估顺序。所以,size=s.size
可以在size()
之前或之后调用。对于会出现问题的类类型,但我不确定 size()
是否保证为无操作以及编译器是否可能决定将变量初始化为例如0.
【讨论】:
查看 Story Teller 回答下的 cmets。这不是未定义的行为(但无论如何都很臭)。 @MartinBonner 哦,写我的答案时没看到。谢谢。以上是关于成员初始化器列表中的赋值操作的主要内容,如果未能解决你的问题,请参考以下文章