簡體   English   中英

成員初始化程序列表和非靜態數據成員上的默認成員初始值設定項之間的區別是什么?

[英]What's the differences between member initializer list and default member initializer on non-static data member?

我想了解使用一種形式而不是另一種形式(如果有的話)的區別。

代碼1 (直接在變量上初始化):

#include <iostream>

using namespace std;

class Test 
{
public:
    Test() {
        cout<< count;
    }

    ~Test();

private:
    int count=10;
};

int main()
{
    Test* test = new Test();
}

代碼2 (init與構造函數上的初始化列表):

#include <iostream>

using namespace std;

class Test 
{
public:
    Test() : count(10) {
        cout<< count;
    }

    ~Test();

private:
    int count;
};

int main()
{
    Test* test = new Test();
}

語義有什么不同,或者它只是語法嗎?

在C ++核心指南中(參見下面的注釋1), 指南C.48推薦了第一種方法(類內初始化器)。提供的推理是:

明確表示在所有構造函數中使用相同的值。 避免重復。 避免維護問題。 它導致最短和最有效的代碼。

事實上,如果您的構造函數除了初始化成員變量之外什么也沒做,就像在您的問題中那樣,那么Guideline C.45仍然更加堅定,並且肯定會使用類內初始值設定項。 它解釋了這一點

使用類內成員初始值設定項可讓編譯器為您生成函數。 編譯器生成的函數可以更高效。

即使我沒有編寫編譯器,我也不會與Stroustrup,Sutter和他們的幾百個朋友和同事爭論,因此我無法證明它更有效率。 盡可能使用類內初始值設定項。

  1. 如果您不熟悉指南,請按照鏈接查看示例代碼和更多說明。

成員初始化

在這兩種情況下,我們都在談論成員初始化 請記住,成員按照在類中聲明順序進行初始化。

代碼2:成員初始化列表

在第二個版本中:

Test() : count(10) {

: count(10)是構造函數初始值設定項( ctor-initializer ), count(10)成員初始值設定項,作為成員初始值設定項列表的一部分。 我喜歡將此視為初始化發生的“真實”或主要方式,但它並不確定初始化的順序。

代碼1:默認成員初始化程序

在第一個版本中:

private:
    int count=10;

count有一個默認成員intitializer 這是后備選項。 如果構造函數中不存在,它將用作成員初始值設定項 ,但在類中,將確定用於初始化的成員序列。

從第12.6.2初始化基地和成員,標准的第10項

如果給定的非靜態數據成員同時具有大括號或等號初始化器和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-equalinitializer中的副作用。 - 末端的例子]

要記住的其他事情是,如果你引入一個非靜態數據成員初始化程序,那么一個結構將不再被認為是C ++ 11中的聚合,但是已經針對C ++ 14進行了更新


差異

使用一種形式而不是另一種形式(如果有的話)的區別是什么。

  • 區別在於兩個選項的優先級。 直接指定的構造函數初始值設定項具有優先權。 在這兩種情況下,我們都會通過不同的路徑獲得成員初始化程序。
  • 最好使用默認成員初始值設定項,因為
    • 然后編譯器可以使用該信息為您生成構造函數的初始化列表,並且它可能能夠進行優化。
    • 您可以在一個地方按順序查看所有默認值。
    • 它減少了重復。 然后,您只能將異常放在手動指定的成員初始化列表中。

我能想到的差異是成員初始化列表默認成員初始化程序之前。

通過默認成員初始值設定項,它只是成員聲明中包含的大括號或等於初始值設定項,如果在成員初始值設定項列表中省略該成員,則使用該初始值設定項。

如果成員具有默認成員初始值設定項並且也出現在構造函數的成員初始化列表中,則忽略默認成員初始值設定項。

例如:

class Test 
{
public:
    Test() {}  // count will be 10 since it's omitted in the member initializer list
    Test(int c) : count(c) {} // count's value will be c, the default member initializer is ignored. 
private:
    int count = 10;
};

代碼沒有區別。 如果您將有多個構造函數重載並且多個計數將是10,那么差異就會出現。對於第一個版本,您將需要更少的寫入。

class Test 
{
public:
    Test() = default;
    Test(int b) : b(b) {} // a = 1, c = 3

    ~Test();

private:
    int a = 1;
    int b = 2;
    int c = 3;
};

與第二個版本相反,上面的代碼如下所示:

class Test 
{
public:
    Test() : a(1), b(2), c(3) {}
    Test(int b) : a(1), b(b), c(3) {}

    ~Test();

private:
    int a;
    int b;
    int c;
};

隨着更多成員變量,差異越大。

當你在成員的聲明旁邊初始化時,這僅在C ++ 11以后才有效,所以如果你在C ++ 98/03中,你就完全不能這樣做了。

如果值永遠不會改變,你可以選擇將它作為constexpr static代替,然后編譯器將被要求不為該值使用任何額外的存儲空間(只要你沒有定義它)並且即時使用常量傳播而是使用值。

使用by-declaration語法的一個缺點是它必須位於標頭中,這將導致每次要更改其值時重新編譯包含標頭的所有轉換單元。 如果這需要很長時間,那可能是不可接受的。

另一個區別是使用成員初始化列表允許您更改每個構造函數的值,而使用by-declaration版本只允許您為所有構造函數指定一個值(盡管您可以覆蓋此值...但我個人避免這種情況,因為它可能會讓人感到困惑!)


順便說一句,沒有必要在這里使用new來創建Test實例。 當人們從其他語言中學習語言並且我想讓你知道時,這是一個常見的錯誤。 在您的示例之外,當然有很多用途。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM