2

我有以下 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]) {}

};

注意复制构造函数中的赋值。-Wall -Wextra代码工作,编译良好,编译器(gcc)即使有标志也不会抱怨。编译器会自动将编译器重写为此吗?

Stack(const Stack& s) : size(s.size), x(new int[size]) {}

还是有什么其他的魔法?我注意到当我更改定义顺序时,编译器会抱怨非按顺序初始化。所以我认为这是我提到的情况。我在文档中找不到任何内容,ASM 输出也对我没有帮助。

4

4 回答 4

4

编译器会自动将编译器重写为此吗?

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]) {}

初始化两个成员,如果你改变了他们在类中的顺序,你仍然会有正确的行为。

于 2019-03-07T13:37:52.033 回答
2

无论您在成员初始化列表中指定它们的顺序如何,类的成员始终按声明顺序进行初始化。如果您省略它们,它们将被默认初始化。所以这样做:

Stack(const Stack& s) : x(new int[size=s.size]) {}

表示size默认先初始化。这留下了一个不确定的值(因为基本类型应该是默认初始化的)。x然后评估初始化器。评估new int[size=s.size]的一部分涉及赋值表达式size=s.size,它size作为副作用进行修改。所以你的代码在这里是正确的,尽管可能会引起人们的注意。

当您切换成员的顺序时,分配发生 size应该被初始化之前。这会使您的代码代码对未定义的行为开放。

于 2019-03-07T13:36:14.467 回答
0

从 Cppreference.com 的构造函数页面:

出现在表达式列表或大括号初始化列表中的名称在构造函数的范围内进行评估:

所以是的,size这里指this->size的是在构造函数的范围内,并且赋值size=s.size是一个有效的表达式。

不用说,您不应该期望这会通过代码审查 :)

于 2019-03-07T13:35:46.267 回答
0

不,它将它重写为:

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()将是对象的默认构造函数,sizeints 没有,因此它只是一个空语句并且size保持未初始化。或者是吗?

我会说这段代码可能会产生未定义的行为。成员变量按照10.9.2 Initializing bases and members中声明它们的顺序进行初始化。但未定义用于初始化的表达式的评估顺序。因此,size=s.size可以在之前或之后调用size()。对于可能会出现问题的类类型,但我不确定是否size()保证无操作以及编译器是否可能决定将变量初始化为例如 0。

于 2019-03-07T13:47:05.087 回答