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> 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

對於定義了自己的構造函數的類類型來說,要求值初始化是沒有意義的;不管采用什么方式,對象都會通過默認構造函數來初始化。但對於內置類型,兩種形式的差別就大了;值初始化的內置類型對象有着良好定義的值,而默認初始化的對象的值則是未定義的。類似的,對於類中那些依賴於編譯器合成的默認構造函數的內置類型成員,如果它們未在類內初始化,那么它們的值也是未定義的。

 


免責聲明!

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



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