C++成員變量初始化-就地初始化&初始化列表


就地初始化&初始化列表

就地初始化:member initializer list

初始化列表:member initializer list,或:member initialization list

 

參考:https://www.cnblogs.com/lidabo/p/3628987.html

C++構造函數初始化按下列順序被調用:

  • 首先,任何虛擬基類的構造函數按照它們被繼承的順序構造;
  • 其次,任何非虛擬基類的構造函數按照它們被繼承的順序構造;
  • 最后,任何成員對象的構造函數按照它們聲明的順序調用;

 

由於類成員初始化總在構造函數執行之前,編譯器總是確保所有成員對象在構造函數體執行之前初始化,

參考:https://www.cnblogs.com/simplepaul/p/7635648.html

 

在C++11中,直接對成員變量賦值(就地初始化),和初始化列表,哪個先執行,哪個后執行?

例如:

class MyClass
{
private:
    int a = 10;
public:
    MyClass()
        :a(20)
    {}
    ~MyClass(){}

    int getA()
    {
        return a;
    }
};

  構造一個MyClass后,a的值是多少?

條款4:C++“成員初始化次序”:base class更早於其derived class被初始化,而class的成員變量總是以其聲明次序被初始化,即使成員變量在成員初始化列表中的次序不同。

這里只是說了成員初始化的順序

  但是:在構造MyClass中,已經初始化了a,然后再在初始化列表中調用拷貝構造函數嗎?待查

 寫了如下測試代碼:

class B
{
public:
    explicit B(int a)
    {
        this->b = a;
        cout << "construct:" << a << endl;
    }

    B(const B &val)
    {
      cout << "copy:" << val.b << endl;
      this->b = val.b;
    }

~B()
    {
        cout << "Destruct:" << this->b << endl;
    }
private:
    int b;
};
class MyClass
{
private:
    int a = 10;
    B b = B(3);
public:
    MyClass()
        :a(20),
        b(B(4))
    {}
    ~MyClass(){}

    int getA()
    {
        return a;
    }
};
int main()
{
    MyClass aa;
    cout << "val a:" << aa.getA() << endl;
    int val = aa.getA();
}

輸出的打印,類B只構造了一次。。。即成員初始化列表中的4那次。

為啥嘞???

相關文章:https://cloud.tencent.com/developer/article/1394301

這篇文章說的是,就地初始化在先,然后成員列表初始化: 

  就地初始化與初始化列表的先后順序

  C++11標准支持了就地初始化非靜態數據成員的同時,初始化列表的方式也被保留下來,也就是說既可以使用就地初始化,也可以使用初始化列表來完成數據成員的初始化工作。當二者同時使用時,並不沖突,初始化列表發生在就地初始化之后,即最終的初始化結果以初始化列表為准。

Note:

  上面鏈接的文章說的不准確,參考:https://en.cppreference.com/w/cpp/language/constructor,這個文章中說,如果成員變量就地初始化,成員變量又出現在初始化列表中,則就地初始化被忽略,以初始化列表中的為准

If a non-static data member has a default member initializer and also appears in a member initializer list, then the member initializer is used and the default member initializer is ignored:

struct S { int n = 42; // default member initializer S() : n(7) {} // will set n to 7, not 42 };

   該文章中對構造函數解釋如下:

  構造函數沒有函數名且不能直接被調用。構造函數在初始化發生時被調用,且根據初始化的規則選擇特定的構造函數。沒有explicit說明符的是可以隱式轉換的構造函數。帶有constexpr說明符的構造函數成為一個字面類型(LiteralType)。沒夠任何參數的構造函數為默認構造函數 default constructor。以另一個同類型對象作為的參數的構造函數有拷貝構造函數 copy constructor,和移動構造函數 move constructor。

  在構造函數的函數體執行之前(即{}內的語句),所有的父類、虛類、non-static成員變量都已初始化完成。成員初始化列表(member initializer list)可用於父類、虛類、非靜態成員變量的初始化(非默認的初始化,non-default initialization)。對於不能調用默認構造函數構造的基類、non-static成員變量,必須在初始化列表中進行初始化,例如:引用類型、const類型的成員變量。

 什么情況必須使用初始化列表

  什么時候必須使用初始化列表?《深入理解C++對象模型》中描述如下:

 成員初始化順序

  如果在構造函數中,通過賦值的形式對成員變量進行初始化,如下所示:

  這種情況,構造函數可能的內部擴張結果為:

   也就是說,在構造函數中,編譯器先調用默認構造函數初始化了 _name ,_cnt,然后再通過賦值操作符(operator =)對其進行賦值操作,效率較差。

  而采用初始化列表形式,如:

   它會被編譯器擴張為如下代碼:

 

   即直接調用拷貝構造函數(copy constructor)對成員變量進行構造,效率較高。(注意:不論在初始化列表中寫的初始化順序是什么,成員變量的初始化順序按照在類的聲明中的順序進行,條款4)

  在本例中,_name 先於 _cnt聲明,則 _name先於_cnt初始化,“初始化次序” 和 “initialization list中的項目排列次序”是不同的

 構造函數調用構造函數

  在C++11之后,可以在構造函數中調用構造函數:如果一個類中有多個構造函數,為避免代碼的重復,可以在一個構造函數中的初始化列表中調用另一個構造函數,但是注意構造函數調用不能出現循環情況。例如:

class Foo { public: Foo(char x, int y) {} Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char,int) };

 

下面是cppreference給出的示例代碼,包含大部分情況:

#include <fstream>
#include <string>
#include <mutex>
 
struct Base {
    int n;
};   
 
struct Class : public Base
{
    unsigned char x;
    unsigned char y;
    std::mutex m;
    std::lock_guard<std::mutex> lg;
    std::fstream f;
    std::string s;
 
    Class ( int x )
      : Base { 123 }, // initialize base class
        x ( x ),      // x (member) is initialized with x (parameter)
        y { 0 },      // y initialized to 0
        f{"test.cc", std::ios::app}, // this takes place after m and lg are initialized
        s(__func__),  //__func__ is available because init-list is a part of constructor
        lg ( m ),     // lg uses m, which is already initialized
        m{}           // m is initialized before lg even though it appears last here
    {}                // empty compound statement
 
    Class ( double a )
      : y ( a+1 ),
        x ( y ), // x will be initialized before y, its value here is indeterminate
        lg ( m )
    {} // base class initializer does not appear in the list, it is
       // default-initialized (not the same as if Base() were used, which is value-init)
 
    Class()
    try // function-try block begins before the function body, which includes init list
      : Class( 0.0 ) //delegate constructor
    {
        // ...
    }
    catch (...)
    {
        // exception occurred on initialization
    }
};
 
int main() {
    Class c;
    Class c1(1);
    Class c2(0.1);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM