1. 程式人生 > >C++的變數初始化

C++的變數初始化

C++中變數的初始化有很多種方式,如:預設初始化,值初始化,直接初始化,拷貝初始化,列表初始化。

 

1、預設初始化:預設初始化是指定義變數時沒有指定初值時進行的初始化操作。

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

特別的,採用動態分配記憶體的方式(即採用new關鍵字)建立的變數,不加括號時(如int *p=new int;)也是預設初始化,加了括號(如int *p=new int())為值初始化。

預設初始化變數的值與變數的型別與變數定義的位置有關係:

(1)對於內建型別變數(如int,double,bool等),如果定義在語句塊外(即{}外),則變數被預設初始化為0;如果定義在語句塊內(即{}內),變數將擁有未定義的值。

(2)對於類型別的變數(如string或其他自定義型別),不管定義於何處,都會執行預設建構函式。如果該類沒有預設建構函式,則會引發錯誤。因此,建議為每個類都定義一個預設建構函式(=default)。

 

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

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

注意,若不採用動態分配記憶體的方式(即不採用new運算子),寫成int a();是錯誤的值初始化方式,因為這種方式是聲明瞭一個函式而不是進行值初始化。

如果一定要進行值初始化,必須結合拷貝初始化使用,即寫成int a=int();值初始化和預設初始化一樣,對於內建型別初始化為0,對於類型別則呼叫其預設建構函式,如果沒有預設建構函式,則不能進行初始化。

 

3、直接初始化:直接初始化是指採用小括號的方式進行變數初始化(小括號裡一定要有初始值,如果沒提供初始值,那就是值初始化了!)。

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

直接初始化直接呼叫與實參匹配的建構函式。

 

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

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

拷貝初始化首先使用指定建構函式建立一個臨時物件,然後用拷貝建構函式將那個臨時物件拷貝到正在建立的物件。

 

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

(1)對於內建型別變數(如int,double,bool等),直接初始化與拷貝初始化差別可以忽略不計。

(2)對於類型別的變數(如string或其他自定義型別),直接初始化呼叫類的建構函式(呼叫引數型別最佳匹配的那個),拷貝初始化呼叫類的拷貝建構函式。

特別的,當對類型別變數進行初始化時,如果類的建構函式採用了explicit修飾而且需要隱式型別轉換時,則只能通過直接初始化而不能通過拷貝初始化進行操作。

 

5、列表初始化:列表初始化是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“的元素

 

6、使用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

對於定義了自己的建構函式的類型別來說,要求值初始化是沒有意義的;不管採用什麼方式,物件都會通過預設建構函式來初始化。但對於內建型別,兩種形式的差別就大了;值初始化的內建型別物件有著良好定義的值,而預設初始化的物件的值則是未定義的。類似的,對於類中那些依賴於編譯器合成的預設建構函式的內建型別成員,如果它們未在類內初始化,那麼它們的值