初始化與賦值之間的區別

2021-07-27 11:15:03 字數 3054 閱讀 7222

由於在內建型別中,初始化和賦值不進行區分並不會造成很大的影響,所以讓我很大程度上忽略了他們的差別。

直到c++primer中對於建構函式提出了一句話,建構函式有乙個初始化部分和乙個函式體,在乙個建構函式中,成員的初始化是在函式體執行之前完成的,且按照它們在類中出現的順序進行初始化。讓我第一次開始思考初始化和賦值操作之間的區別。

實際上,我們要注意很重要的一點,我們平時的對於建構函式中使用的「賦值」寫法實際上並沒有對於類內的成員進行初始化,如果類內的成員是已經定義預設建構函式的非內建型別,像string型別,那麼我們即使用的是「賦值」寫法,那麼我們沒有進行初始化操作也沒有問題,因為預設建構函式會幫助我們完成構造過程,但是如果是沒有定義預設建構函式的內建型別,編譯器是是不負責初始化的。而對於一些非內建型別的話,用賦值的寫法理論上也沒有問題,因為實際上也會被初始化。但是為了避免出錯,我們都建議用初始化列表的方式先進行初始化。

首先我們看建構函式的兩種寫法

「賦值」寫法:

[cpp]view plain

copy

//例1:建構函式的一種寫法,雖然合法但比較草率,沒有使用建構函式初始值

sales_data::sales_data(const

string &s,unsigned cnt,

double

price)  

然後是初始化列表初試化類物件

[cpp]view plain

copy

//例2:

sales_data(const

std::string &s,unisigned n,

double

p):bookno(s),units_sold(n),revenue(p*n){}  

對比兩種建構函式的寫法,在第一種情況下,如果類內的成員都是內建型別的話,是不會進行初始化的,而在第二種情況下,用初始化列表的情況下(實際上我們可以看得到就是我們在使用初始化列表的情況下,用括號進行初始化,對於一些非內建型別的其實是呼叫了各個型別的複製建構函式來進行初始化的,這些型別不經過初始化是無法使用的,因而我們說對於這些成員,初始化和賦值之間的區別是巨大的,而對於一般的內建型別,初始化和賦值是沒有明顯區別的)  

當我們在定義變數時習慣立即對其進行初始化,而非先定義再賦值

[cpp]view plain

copy

string foo=

"helloworld"

;//定義並初始化

string bar;//預設初始化成空string物件

bar="helloworld"

;//為bar賦乙個新值

就物件的資料成員而言,初始化和賦值也有類似的區別,如果沒有在建構函式的初始值列表中顯式地初始化成員,則該成員將在建構函式體之前執行預設初始化。

實際上對於一些特定變數(const變數和引用型變數),對於這些變數,是不允許賦值操作的:

[cpp]view plain

copy

class

constref;  

//建構函式部分,這樣寫顯然是要報錯的,因為const和引用物件是無法賦值的,只能進行初始化

"code"

class

="cpp"

>constref::constref(

intii)  

所以在這種情況下,我們就只能列表初始化的方式來書寫建構函式,即

[cpp]view plain

copy

constref::constref(

intii):i(ii),ci(ii),ri(ii){}  

實際上,我們在一些情況下對比上面的兩個例子中的兩個建構函式,就可以發現賦值和初始化兩個操作的區別在建構函式中激化的是比較嚴重的,上面提到的const和引用變數是第一種例子,實際上還有一種情況我們在講合成得預設建構函式的侷限性的時候也有提到。

在c++primer p236頁中曾經提到合成得預設建構函式的侷限性的時候,提到了第三點,當對於類a,如果程式設計師定義了建構函式,也就是編譯器此時並不會生成合成預設建構函式,在沒有顯式指出預設建構函式的時候,如果我們在類b中將類a作為類b的成員的時候,那麼對於類b就無法生成合成的預設建構函式,因為在這種巢狀的方式定義的b類中,b類中要想生成合成預設建構函式,實際上是必須要依賴於預設建構函式的。

實際上,如果我們要在b類中寫建構函式,如果想要採用上面例1的方式,用等號來處理的話,是不可以的!因為就像上文所說的,如果像上面例1的方式來處理,那麼在函式體之前需要對類b(包含類b中的類a)進行初始化的(注意,不是賦值),而這種初始化是依賴於預設建構函式的!所以我們如果在此時使用所謂「賦值」的寫法企圖達到初始化的目的是不可能的。而沒有進行初始化的相關成員是無法使用的,所以這種時候就只能用列表初始化的方式來書寫建構函式。如下:

[cpp]view plain

copy

class

a   

//省略了一些成員

};  

class

b ;   

//此時對於類b就只能用列表初始化的方式來寫建構函式 

b::b(): m(2)  

在上面我們就可以看到列表初始化的好處所在,因為列表初始化是依賴於複製建構函式的,而不管我們有沒有在類中定義這個類的複製建構函式,類中都會自己生成乙個預設複製建構函式,而如果我們想要用」賦值「的方式妄圖實現所謂的初始化的話,其實這種初始化時依賴於預設建構函式的(也就是沒有引數的建構函式,或者是那種在參數列中指明了自己可設可不設引數的建構函式),因此在程式設計師自己設定了任何乙個有引數的預設建構函式且並沒有自己定義預設建構函式的時候就會出現問題。

以上是推薦我們使用列表初始化方式進行初始化的原因之一

其二在於如果我們採用賦值的方式妄圖進行初始化的話,對於一些非內建型別進行的步驟就是先進行初始化後進行賦值,而如果僅僅是進行列表初始化的話,就只會進行初始化這一種個操作,而並不會進行賦值,故首先效率上列表初始化時是會更高。對於內建型別雖然是沒有區別,但是如果在建構函式中先在列表那裡寫初始化,再在函式體中寫內建型別的操作,是有些奇怪的,因此為了統一性,推薦統一使用列表初始化方式進行成員的初始化。

C 初始化與賦值的區別

賦值操作是在兩個已經存在的物件間進行的,而初始化是要建立乙個新的物件,並且其初值 於另乙個已存在的物件。編譯器會區別對待這兩種操作,對於賦值則呼叫賦值操作符,對於初始化則呼叫拷貝建構函式 如果類中沒有拷貝建構函式,則編譯器會提供乙個預設的拷貝建構函式,簡單的賦值類中的成員。對於基本型別來說,差別不大...

賦值與初始化

1 賦值 賦值 是給變數指定乙個值或者是改變乙個變數的值,且 必須是在該變數型別所能表達的範圍之內。int speed 30 標準形式 variable expression 變數型別 變數名 表示式 2 初始化 區域性變數 必須顯示的進行初始化。例項變數 類變數 編譯器可以自動對它們進行初始化。b...

初始化和賦值的區別

區別說明 賦值操作是在兩個已經存在的物件間進行的,而初始化是要建立乙個新的物件,並且其初值 於另乙個已存在的物件。編譯器會區別這兩種情況,賦值 的時候呼叫過載的賦值運算子,初始化的時候呼叫拷貝建構函式。如果類中沒有拷貝建構函式,則編譯器會提供乙個預設的。這個預設的拷貝建構函式只是簡單地復 製類中的每...