繁体   English   中英

C ++在默认构造函数中直接初始化字段与初始化列表

[英]C++ Initialising fields directly vs initialisation list in default constructor

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

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

来自cppreference - 非静态数据成员

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

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


总而言之,两个初始化器都是等效的,并且可以执行它们应该执行的操作。

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

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

如果所有构造函数都将该成员初始化为某个不同的值,那么使用默认成员初始化程序就没有意义,然后在各自的构造函数中进行显式初始化将更加清晰

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

第一组示例彼此相同。

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

12.6.2初始化基础和成员

[...]

如果给定的非静态数据成员同时具有大括号或等号初始化器和mem-initializer,则执行mem-initializer指定的初始化,并且非静态数据成员的大括号或等于初始值为忽略。 [例子:给定

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

A(int)构造函数将简单地将i初始化为arg的值,并且不会发生i的brace-or-equal-initializer中的副作用。 - 结束例子]

两者是相同的。

软件工程的一个原则是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解释a=7 “它始终从7开始”,而不是“默认值为7”。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM