值初始化和默認初始化的區別


直接初始化和拷貝初始化

如果使用等號(=)初始化一個變量,實際上執行的是拷貝初始化,編譯器把等號右側的初始值拷貝到新創建的對象中去。與之相反,如果不使用等號,則執行的是直接初始化。

當初始值只有一個時,使用直接初始化或拷貝初始化都行。如果用多個值進行初始化的情況,非要用拷貝初始化的方式處理也不是不可以,不過需要顯式地創建一個(臨時)對象用於拷貝。

string s8=string(10,'c'); //拷貝初始化,s8的內容是cccccccccc。

  • C++支持兩種初始化形式:直接初始化復制初始化。復制初始化使用“=”符號,而直接初始化將初始化式放在圓括號中。
  • 當用於類類型對象時,初始化的復制形式和直接形式有所不同。
  • 支持初始化的復制形式主要是為了與C的用法兼容。當情況許可時,可以允許編譯器跳過復制構造函數直接創建對象,但編譯器沒有義務這樣做。
    • 直接初始化直接調用與實參匹配的構造函數
    • 復制初始化首先使用指定構造函數創建一個臨時對象,然后用復制構造函數將那個臨時對象復制到正在創建的對象。
  • 通常直接初始化和復制初始化僅在低級別優化上存在差異。
  • 對於不支持復制的類型,或者使用explicit構造函數,不能進行復制初始化。

值初始化

通常情況下,可以只提供vector對象容納的元素數量而不用略去初始值。此時庫會創建一個值初始化的元素初值,並把它賦給容器中的所有元素,這個初值由vector對象中元素的類型決定。

如果vector對象的元素是內置類型,比如int,則元素初始值自動設為0.如果元素時某種類類型,比如string,則元素由類默認初始化。

vector<int> ivec(10);            //10個元素,每個都初始化為0

vector<string> svec(10);      //10個元素,每個都是空string對象

對這種初始化的方式有兩個特殊限制:其一,有些類要求必須明確地提供初始值,如果vector對象中元素的類型不支持默認初始化,我們就必須提供初始的元素值。對這種類型的對象來說,只提供元素的數量而不設定初始值無法完成初始化工作。其二,如果只提供了元素的數量而沒有設定初始值,只能使用直接初始化:

vector<int> vi=10;  //錯誤,必須使用直接初始化的形式指定向量的大小

這里的10是用來說明如何初始化vector對象的,我們用它的本意是想創建含有10個值初始化了的元素的vector對象,而非數字10”拷貝“到vector中。因此,此時不宜使用拷貝初始化。

列表初始值還是元素數量?

在某些情況下,初始化的真實含義依賴於傳遞初始值時用的是花括號還是圓括號,例如,用一個整數來初始化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“的元素

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