C++的變量初始化

C++中變量的初始化有不少種方式,如:默認初始化,值初始化,直接初始化,拷貝初始化,列表初始化。

 

一、默認初始化:默認初始化是指定義變量時沒有指定初值時進行的初始化操做。函數

如:int a;這些變量被定義了而沒有顯式的賦予初值。spa

特別的,採用動態分配內存的方式(即採用new關鍵字)建立的變量,不加括號時(如int *p=new int;)也是默認初始化,加了括號(如int *p=new int())爲值初始化。code

默認初始化變量的值與變量的類型與變量定義的位置有關係:對象

(1)對於內置類型變量(如int,double,bool等),若是定義在語句塊外(即{}外),則變量被默認初始化爲0;若是定義在語句塊內(即{}內),變量將擁有未定義的值。內存

(2)對於類類型的變量(如string或其餘自定義類型),無論定義於何處,都會執行默認構造函數。若是該類沒有默認構造函數,則會引起錯誤。所以,建議爲每一個類都定義一個默認構造函數(=default)。ci

 

二、值初始化:值初始化是指使用了初始化器(即便用了圓括號或花括號)但卻沒有提供初始值的狀況。v8

例如:int *p = new int(); vector<string> vec(10);字符串

注意,若不採用動態分配內存的方式(即不採用new運算符),寫成int a();是錯誤的值初始化方式,由於這種方式是聲明瞭一個函數而不是進行值初始化。編譯器

若是必定要進行值初始化,必須結合拷貝初始化使用,即寫成int a=int();值初始化和默認初始化同樣,對於內置類型初始化爲0,對於類類型則調用其默認構造函數,若是沒有默認構造函數,則不能進行初始化。string

 

三、直接初始化:直接初始化是指採用小括號的方式進行變量初始化(小括號裏必定要有初始值,若是沒提供初始值,那就是值初始化了!)。

例如:int a(12); vector<int> ivec(ivec2);string s("123456");等等。

直接初始化直接調用與實參匹配的構造函數。

 

四、拷貝初始化:拷貝初始化是指採用等號(=)進行初始化的方式,編譯器把等號右側的初始值拷貝到新建立的對象中去。

例如int a=12;string s=string("123456");等等。拷貝初始化看起來像是給變量賦值,其實是執行了初始化操做,與先定義再賦值本質不一樣。

拷貝初始化首先使用指定構造函數建立一個臨時對象,而後用拷貝構造函數將那個臨時對象拷貝到正在建立的對象。

 

直接初始化與拷貝初始化:簡而言之複製初始化使用「=」符號,而直接初始化將初始化式放在圓括號中。

(1)對於內置類型變量(如int,double,bool等),直接初始化與拷貝初始化差異能夠忽略不計。

(2)對於類類型的變量(如string或其餘自定義類型),直接初始化調用類的構造函數(調用參數類型最佳匹配的那個),拷貝初始化調用類的拷貝構造函數。

特別的,當對類類型變量進行初始化時,若是類的構造函數採用了explicit修飾並且須要隱式類型轉換時,則只能經過直接初始化而不能經過拷貝初始化進行操做。

 

五、列表初始化:列表初始化是C++ 11 新引進的初始化方式,它採用一對花括號(即{})進行初始化操做。能用直接初始化和拷貝初始化的地方都能用列表初始化,並且列表初始化能對容器進行方便的初始化,所以在新的C++標準中,推薦使用列表初始化的方式進行初始化。列表初始化的應用場景有:int a{12};string s{"123"};vector<int> vec{1,2,3};

在某些狀況下,初始化的真實含義依賴於傳遞初始值時用的是花括號仍是圓括號,例如,用一個整數來初始化vector<int>時,整數的含義多是vector對象的容量也多是元素的值。相似的,用兩個整數來初使化vector<int>時,這兩個整數可能一個是vector對象的容量,另外一個是元素的初值,也可能它們是容量爲2的vector對象中兩個元素的初值。經過使用花括號或圓括號能夠區分上述這些含義:

vector<int>v1(10); //v1有10個元素,每一個的值都是0

vector<int> v2{10}; //v2有1個元素,該元素的值時10

vector<int> v3(10,1); //v3有10個元素,每一個的值都是1

vector<int> v4{10,1}; //v4有兩個元素,值分別是10,1

若是用圓括號,能夠說提供的值是用來構造vector對象的。例如,v1的初始值說明了vector對象的容量;v3的兩個初始值則分別說明了vector對象的容量和元素的初值。

若是用的是花括號,能夠表述成咱們想列表初始化該vector對象。也就是說,初始化過程會盡量地把花括號內的值當成是元素初始值的列表來處理,只有在沒法執行列表初始化時纔會考慮其餘初始化方式。在上例中,給v2和v4提供的初始值都做爲元素的值,因此它們都會執行列表初始化,vector對象v2包含一個元素而vector對象v4包含兩個元素。

另外一方面,若是初始化時使用了花括號的形式可是提供的值又不能用來列表初始化,就要考慮用這樣的值來構造vector對象了。例如,要想列表初始化一個含有string對象的vector對象,應該提供能賦給string對象的初值。此時不難區分究竟是要列表初始化vector對象的元素仍是用給定的容量值來構造vector對象:

vector<string> v5{"hi}; //列表初始化,v5有一個元素

vector<string> v6("hi"); //錯誤,不能使用字符串字面值構造vector對象

vector<string> v6(10,"hi");//正確,10個string類型,每一個初始值爲「hi」。

以上兩條能夠看出,vector<string> v6(1,"hi");就能達到你的目的,爲何還要畫蛇添足,再提供一個專門的構造呢

vector<string> v7(10); //v7有10個默認初始化的元素

vector<string> v8{10,"hi"}; //v8有10個值爲」hi「的元素

 

六、使用new動態分配的默認初始化和值初始化

默認狀況下,動態分配對象是默認初始化的,這意味着內置類型或組合類型的對象的值時未定義的,而類類型對象將默認構造函數進行初始化:

string *ps=new string;  //初始化爲空string

int *pi =new int;   //pi指向一個未初始化的int

咱們可使用直接初始化方式來初始化動態分配的對象。咱們可使用傳統的構造方式,在新標準下,也可使用列表初始化(使用花括號):

int *pi=new int(1024);  //pi指向的對象的值爲1024

string *ps=new string(10,'9');   //*派生爲「9999999999」

//vector有10個元素,值依次從0-9

vector<int> *pv=new vector<int>{0,1,2,3,4,5,6,7,8,9};

也能夠對動態分配的對象進行值初始化,只需在類型名以後跟一對空括號便可:

string *ps1=new string ;//默認初始化爲空string

string *ps=new string(); //值初始化爲空string

int *pi1=new int;   //默認初始化;*pi1的值未定義

int *pi2=new int(); //值初始化爲0;*pi2的值爲0

對於定義了本身的構造函數的類類型來講,要求值初始化是沒有意義的;無論採用什麼方式,對象都會經過默認構造函數來初始化。但對於內置類型,兩種形式的差異就大了;值初始化的內置類型對象有着良好定義的值,而默認初始化的對象的值則是未定義的。相似的,對於類中那些依賴於編譯器合成的默認構造函數的內置類型成員,若是它們未在類內初始化,那麼它們的值也是未定義的。

相關文章
相關標籤/搜索