【问题标题】: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 输出也对我没有帮助。

【问题讨论】:

    标签: c++ constructor order-of-execution member-initialization initialization-order


    【解决方案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 应该被初始化。这会使您的代码代码对未定义的行为开放。

      【讨论】:

      【解决方案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 的默认构造函数,但ints 没有,所以它只是一个空语句,size 保持未初始化。 是吗?

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

      【讨论】:

      • 查看 Story Teller 回答下的 cmets。这不是未定义的行为(但无论如何都很臭)。
      • @MartinBonner 哦,写我的答案时没看到。谢谢。
      猜你喜欢
      • 2019-09-10
      • 2015-09-25
      • 1970-01-01
      • 2015-02-28
      • 1970-01-01
      • 2015-11-28
      • 2011-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多