就地初始化&初始化列表
就地初始化: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); }