簡體   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