C++默认构造函数中直接初始化字段 vs 初始化列表的区别

26

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

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"?


1
它们是等价的。然而,在我看来,字段初始化更易读。从类定义中更容易看到默认值是什么。 - StoryTeller - Unslander Monica
5
另一方面,改变默认值会导致包括头文件在内的所有内容都需要重新编译。了解两者并选择更适合您情况的选项。 - Quentin
根据Olaf Dietsche的回答 - 你错过了一个情况; 即同时使用a = 0和foo:a(0)的情况。 - UKMonkey
3个回答

18

来自cppreference - 非静态数据成员

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

如果成员具有默认成员初始化器,并且也出现在构造函数的成员初始化列表中,则忽略默认成员初始化器。


总之,两种初始化方式都是等效的,而且它们都能完成它们应该做的事情。

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

class Foo {
private:
    int a = 0;
};
如果所有构造函数将成员初始化为不同的值,那么使用默认成员初始化程序就没有意义了,此时在各自的构造函数中进行显式初始化会更加清晰。
class Foo {
private:
    int a;
public:
    Foo() : a(3) {}
    Foo(int i) : a(i) {}
};

9

第一组示例彼此相同。

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

12.6.2 Initializing bases and members

[ ... ]

If a given non-static data member has both a brace-or-equal-initializer and a mem-initializer, the initialization specified by the mem-initializer is performed, and the non-static data member’s brace-or-equal-initializer is ignored. [ Example: Given

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

the A(int) constructor will simply initialize i to the value of arg, and the side effects in i’s brace-or-equal-initializer will not take place. — end example ]


5

两者是完全相同的。

软件工程的一个原则是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) {}
};

以上是一个糟糕的反模式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接