類的初始化和賦值

2021-04-02 01:03:29 字數 2370 閱讀 3728

我的問題是關於初始化c++類成員的。我見過許多這樣的**(包括在你的欄目中也見到過):

csomeclass::csomeclass()

x=0;

y=1;

而在別的什麼地方則寫成下面的樣子:

csomeclass::csomeclass() : x(0), y(1)

我的一些程式設計師朋友說第二種方法比較好,但他們都不知道為什麼是這樣。你能告訴我這兩種類成員初始化方法的區別嗎?

回答

從技術上說,你的程式設計師朋友是對的,但是在大多數情況下,兩者實際上沒有區別。有兩個原因使得我們選擇第二種語法,它被稱為成員初始化列表:乙個原因是必須的,另乙個只是出於效率考慮。

讓我們先看一下第乙個原因——必要性。設想你有乙個類成員,它本身是乙個類或者結構,而且只有乙個帶乙個引數的建構函式。

class cmember

因為cmember有乙個顯式宣告的建構函式,編譯器不產生乙個預設建構函式(不帶引數),所以沒有乙個整數就無法建立cmember的乙個例項。

cmember* pm = new cmember;        // error!!

cmember* pm = new cmember(2);     // ok

如果cmember是另乙個類的成員,你怎樣初始化它呢?你必須使用成員初始化列表。

class cmyclass {

cmember m_member;

public:

cmyclass();

//必須使用成員初始化列表

cmyclass::cmyclass() : m_member(2)

沒有其它辦法將引數傳遞給m_member,如果成員是乙個常量物件或者引用也是一樣。根據c++的規則,常量物件和引用不能被賦值,它們只能被初始化。

第二個原因是出於效率考慮,當成員類具有乙個預設的建構函式和乙個賦值操作符時。mfc的cstring提供了乙個完美的例子。假定你有乙個類cmyclass具有乙個cstring型別的成員m_str,你想把它初始化為"yada yada."。你有兩種選擇:

cmyclass::cmyclass() {

// 使用賦值操作符

// cstring::operator=(lpctstr);

m_str = _t("yada yada");

//使用類成員列表

// and constructor cstring::cstring(lpctstr)

cmyclass::cmyclass() : m_str(_t("yada yada"))

在它們之間有什麼不同嗎?是的。編譯器總是確保所有成員物件在建構函式體執行之前初始化,因此在第乙個例子中編譯的**將呼叫cstring::cstring來初始化m_str,這在控制到達賦值語句前完成。在第二個例子中編譯器產生乙個對cstring:: cstring(lpctstr)的呼叫並將"yada yada"傳遞給這個函式。結果是在第乙個例子中呼叫了兩個cstring函式(建構函式和賦值操作符),而在第二個例子中只呼叫了乙個函式。在cstring的例子裡這是無所謂的,因為預設建構函式是內聯的,cstring只是在需要時為字串分配記憶體(即,當你實際賦值時)。但是,一般而言,重複的函式呼叫是浪費資源的,尤其是當建構函式和賦值操作符分配記憶體的時候。在一些大的類裡面,你可能擁有乙個建構函式和乙個賦值操作符都要呼叫同乙個負責分配大量記憶體空間的init函式。在這種情況下,你必須使用初始化列表,以避免不要的分配兩次記憶體。在內部型別如ints或者longs或者其它沒有建構函式的型別下,在初始化列表和在建構函式體內賦值這兩種方法沒有效能上的差別。不管用那一種方法,都只會有一次賦值發生。有些程式設計師說你應該總是用初始化列表以保持良好習慣,但我從沒有發現根據需要在這兩種方法之間轉換有什麼困難。在程式設計風格上,我傾向於在主體中使用賦值,因為有更多的空間用來格式化和新增注釋,你可以寫出這樣的語句:x=y=z=0;

或者memset(this,0,sizeof(this));

注意第二個片斷絕對是非物件導向的。

當我考慮初始化列表的問題時,有乙個奇怪的特性我應該警告你,它是關於c++初始化類成員的,它們是按照宣告的順序初始化的,而不是按照出現在初始化列表中的順序。

class cmyclass {

cmyclass(int x, int y);

int m_x;

int m_y;

cmyclass::cmyclass(int i) : m_y(i), m_x(m_y)

你可能以為上面的**將會首先做m_y=i,然後做m_x=m_y,最後它們有相同的值。但是編譯器先初始化m_x,然後是m_y,,因為它們是按這樣的順序宣告的。結果是m_x將有乙個不可**的值。我的例子設計來說明這一點,然而這種bug會更加自然的出現。有兩種方法避免它,乙個是總是按照你希望它們被初始化的順序宣告成員,第二個是,如果你決定使用初始化列表,總是按照它們宣告的順序羅列這些成員。這將有助於消除混淆。

類的初始化 賦值和析構

一般情況下,對於類的初始化操作,使用建構函式來完成類的初始化操作。要想理解和學習類的初始化,主要要弄明白如下幾個問題 1 c 提供了哪些初始化類的方法?2 為什麼要建構函式來初始化類?3 使用建構函式初始化類具體做什麼?c 提供兩類對於類的初始化方法 第一類 從c語音繼承來的顯示初始化列表 clas...

關於初始化和賦值

class test private char ptr pubic 若test test1 hello 則呼叫的是建構函式 若test test2 test1 則呼叫的是拷貝建構函式 若test test2 test1 則呼叫的是拷貝建構函式 若test test2 test2 test1 則呼叫的...

類初始化和例項初始化

乙個類要建立例項需要先載入並初始化該類 main方法所在的類需要先載入和初始化 乙個子類要初始化需要先初始化父類 乙個類初始化就是執行clinit 方法 clinit 方法由靜態類變數顯式賦值 和靜態 塊組成 類變數顯式賦值 和靜態 塊 從上到下順序執行 clinit 方法只執行一次 父類的初始化 ...