【问题标题】:C++ Initialising fields directly vs initialisation list in default constructorC ++直接初始化字段与默认构造函数中的初始化列表
【发布时间】:2016-11-02 21:44:59
【问题描述】:

我想知道这段代码是否有区别:

class Foo{
 private:
    int a = 0;
 public:
    Foo(){}
}

还有:

class Foo{
 private:
    int a;
 public:
    Foo(): a(0) {}
}

如果是这样,应该首选哪个? 我知道使用初始化器列表比在构造函数主体中分配更好,但是初始化器列表与在字段声明中直接初始化(至少对于原始类型,就像这里的情况一样)呢?

还有,下面是什么情况:

class Foo{
 private:
   int a = 0;
 public:
   Foo(){}
   Foo(int i): a(i) {}
}

当非默认构造函数被调用时:“a”是否被初始化了两次,先为0再为“i”,还是直接为“i”?

【问题讨论】:

  • 它们是等价的。但是,IMO 字段初始化更具可读性。从类定义中查看 what 默认值 is 要容易得多。
  • ... 但另一方面,更改默认值会导致重新编译包含标头的所有内容。了解它们并选择更适合您的情况。
  • 根据 Olaf Dietsche 的回答 - 您错过了一个案例;其中 a=0 和 foo: a(0) 都被使用了

标签: c++


【解决方案1】:

来自cppreference - Non-static data members

成员初始化
1) 在构造函数的成员初始化器列表中。
2) 通过一个默认的成员初始化器,它只是一个包含在成员声明中的大括号或等号初始化器,如果成员在成员初始化器列表中被省略,则使用它。

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


总之,这两个初始化器是等价的,并且做它们应该做的事情。

如果我仍然使用默认构造函数,或者如果所有或大多数构造函数将成员初始化为相同的值,我更喜欢默认成员初始化器。

class Foo {
private:
    int a = 0;
};

但是,如果所有构造函数都将成员初始化为不同的值,则使用默认成员初始值设定项意义不大,然后在各自的构造函数中进行显式初始化会更清晰

class Foo {
private:
    int a;
public:
    Foo() : a(3) {}
    Foo(int i) : a(i) {}
};

【讨论】:

    【解决方案2】:

    第一组示例彼此相同。

    对于最后一个例子,C++ 标准规定如下:

    12.6.2 初始化基和成员

    [...]

    如果给定的非静态数据成员同时具有 大括号或相等初始化器和一个内存初始化器,初始化 执行由 mem-initializer 指定的非静态数据 成员的大括号或相等初始化器被忽略。 [示例:给定

    struct A {
    int i = /∗ some integer expression with side effects ∗/ ;
    A(int arg) : i(arg) { }
    // ...
    };
    

    A(int) 构造函数将简单地将 i 初始化为 arg 的值, 并且 i 的大括号或相等初始化器中的副作用不会出现 地方。 —结束示例]

    【讨论】:

      【解决方案3】:

      两者是相同的。

      软件工程的一条规则是 DRY——不要重复自己。 DRY 指出,如果你可以避免重复相同的标记两次,或者有两个相同的列表,你应该这样做。

      这有几个原因。维护两个相同的列表非常容易出错。一个被修改,或者有错字,另一个没有。它使代码变长,从而使其更难阅读。避免复制粘贴编码鼓励使用一些非常强大且富有表现力的技术,这些技术可以使您所做的事情比手动执行 17 次更清晰。

      struct foo {
        int a;
        foo():a(7) {}
      };
      

      这里我们重复了一遍——特别是成员变量列表被列出了两次。一次在foo 的定义中,又一次在foo::foo 的初始化列表中。如果它在某处丢失,您会得到未初始化的数据。

      struct foo {
        int a = 7;
        foo() {}
      };
      

      这里我们不再重复。

      struct foo {
        int a = 7;
        foo() {}
        foo(int i):a(i) {}
      };
      

      这里有一些重复,但重复是不可避免的。但是,它被最小化了。

      这里有一些成本,因为有人可能会将a=7 解释为“它总是从 7 开始”,而不是“默认为 7”。

      struct foo {
        int a = 7;
        foo():a(3) {}
        foo(int i):a(i) {}
      };
      

      以上是一个可怕的反模式。

      【讨论】:

        猜你喜欢
        • 2011-06-22
        • 2020-02-08
        • 1970-01-01
        • 2012-04-11
        • 2020-01-18
        • 1970-01-01
        • 1970-01-01
        • 2015-07-10
        • 1970-01-01
        相关资源
        最近更新 更多