說到構造函數,就必需要說到構造函數初始化列表。爲何要說它呢,下面來給各位客官娓娓道來。(目前有許多博客對此處已經說的很好了,但是沙米在前篇給你們寫了構造函數的理解和應用場景博客後,還想讓你們再深刻的理解一下,故作此文章)ios
構造函數進化流程:函數
如今來詳細聊聊:性能
一、使用格式this
構造函數的初始化列表以冒號開頭,後面跟着一系列以逗號分隔的初始化字段。spa
class Teacher { public: Teacher(int x):i(x),j(x){}; //初始化列表 private: int i; int j; };
二、 構造函數執行的兩個階段.net
初始化階段:全部類類型(class type)的成員都會在初始化階段初始化,即便該成員沒有出如今構造函數的初始化列表中。code
計算階段:通常用於執行構造函數體內的賦值操做對象
使用常規構造函數賦值類對象:blog
#include <iostream> using namespace std; class Test_A { public: Test_A() { cout<<"構造函數Test_A()"<<endl; } Test_A(const Test_A& t1) { cout<<"拷貝構造函數Test_A()"<<endl; m_age = t1.m_age; } Test_A& operator = (const Test_A& t1) { cout<<"重載賦值運算符operator="<<endl; m_age = t1.m_age; return *this; } ~Test_A() { cout<<"析構函數~Test_A()"<<endl; } public: int m_age; }; class Test_B { public: Test_B(Test_A& t1) { m_b = t1; } public: Test_A m_b; }; /*此函數至關於一個舞臺,展現此函數內對象的完整生命週期*/ void display() { Test_A t1; Test_B t2(t1); } int main() { display(); system("pause"); return 0; }
輸出結果:生命週期
構造函數Test_A() 構造函數Test_A() 重載賦值運算符operator= 析構函數~Test_A() 析構函數~Test_A()
從輸出結果中能夠看出,在執行Test_B t2(t1)的過程:
先調用Test_A類的構造函數初始化成員對象 m_b (初始化階段)
而後再調用Test_A類的重載賦值運算符函數,將t1賦值給m_b。 (計算階段)
使用初始化列表(只需修改類Test_B中的構造函數):
class Test_B { public: Test_B(Test_A& t1):m_b(t1){}; //使用了構造函數的初始化列表 public: Test_A m_b; };
輸出結果:
構造函數Test_A() 拷貝構造函數Test_A() 析構函數~Test_A() 析構函數~Test_A()
從輸出結果中能夠看出,執行Test_B t2(t1)的過程:
在初始化成員對象 m_b時,直接調用Test_A類的拷貝構造函數進行初始化。(初始化階段)
無 (計算階段)
三、爲何須要——初始化列表
(1)性能的提升,對於內置類型,使用初始化列表和構造函數內賦值性能差異不是很大,可是對於類類型來講,使用初始化列表,減小了一次計算階段,若是是密集型類,效率將會更高。
(2) 成員的類型是沒有默認構造函數的類。若沒有提供顯示初始化式,則編譯器隱式使用成員類型的默認構造函數,若類沒有默認構造函數,則編譯器嘗試使用默認構造函數將會失敗,必須使用初始化列表
(3)const成員或引用類型的成員。由於const對象或引用類型只能初始化,不能對他們賦值,必須使用初始化列表
注意:類中成員是按照他們在類中出現的順序進行初始化的,而不是按照他們在初始化列表出現的順序初始化的
四、本質(很重要)
在執行A類構造函數的初始化階段,就將傳入A類構造函數的參數值在A類的成員初始化時,進行了值得傳遞。
沙米才疏學淺,但願你們多多給沙米提意見。,一塊兒進步,提升。