預設建構函式

2021-06-23 02:31:50 字數 4334 閱讀 9130

只要定義乙個物件時沒有提供初始化式,就使用預設建構函式。為所有形參提供預設實參的建構函式也定義了預設建構函式。

乙個類哪怕只定義了乙個建構函式,編譯器也不會再生成預設建構函式。這條規則的根據是,如果乙個類在某種情況下需要控制物件初始化,則該類很可能在所有情況下都需要控制。

合成的預設建構函式(synthesized default constructor)使用與變數初始化相同的規則來初始化成員。具有類型別的成員通過執行各自的預設建構函式來進行初始化。內建和復合型別的成員,如指標和陣列,只對定義在全域性作用域中的物件才初始化。當物件定義在區域性作用域中時,內建或復合型別的成員不進行初始化。

如果類包含內建或復合型別的成員,則該類不應該依賴於合成的預設建構函式。它應該定義自己的建構函式來初始化這些成員。

此外,每個建構函式應該為每個內建或復合型別的成員提供初始化式。沒有初始化內建或復合型別成員的建構函式,將使那些成員處於未定義的狀態。除了作為賦值的目標之外,以任何方式使用乙個未定義的成員都是錯誤的。如果每個建構函式將每個成員設定為明確的已知狀態,則成員函式可以區分空物件和具有實際值的物件。

class a;

a a;  //a中的a=0,str=「」,b=都被初始化

int _tmain(int argc, _tchar* argv)

在某些情況下,預設建構函式是由編譯器隱式應用的。如果類沒有預設建構函式,則該類就不能用在這些環境中。為了例示需要預設建構函式的情況,假定有乙個 nodefault 類,它沒有定義自己的預設建構函式,卻有乙個接受乙個string 實參的建構函式。因為該類定義了乙個建構函式,因此編譯器將不合成預設建構函式。nodefault 沒有預設建構函式,意味著:

1. 具有 nodefault 成員的每個類的每個建構函式,必須通過傳遞乙個初始的 string 值給 nodefault 建構函式來顯式地初始化 nodefault 成員。

2. 編譯器將不會為具有 nodefault 型別成員的類合成預設建構函式。如果這樣的類希望提供預設建構函式,就必須顯式地定義,並且預設建構函式必須顯式地初始化其 nodefault 成員。

3. nodefault 型別不能用作動態分配陣列的元素型別。

4. nodefault 型別的靜態分配陣列必須為每個元素提供乙個顯式的初始化式。

5. 如果有乙個儲存 nodefault 物件的容器,例如 vector,就不能使用接受容器大小而沒有同時提供乙個元素初始化式的建構函式。

實際上,如果定義了其他建構函式,則提供乙個預設建構函式幾乎總是對的。通常,在預設建構函式中給成員

提供的初始值應該指出該物件是「空」的。

初級 c++ 程式設計師常犯的乙個錯誤是,採用以下方式宣告乙個用預設建構函式初始化的物件:

// oops! declares a function, not an object

sales_item myobj();

被編譯器解釋為乙個函式的宣告,該函式不接受引數並返回乙個 sales_item 型別的物件——與我們的意圖大相徑庭!使用預設建構函式定義乙個物件的正確方式是去掉最後的空括號:

// ok: defines a class object ...

sales_item myobj;

另一方面,下面這段**也是正確的:

// ok: create an unnamed, empty sales_itemand use to initialize myobj

sales_item myobj = sales_item();

在這裡,我們建立並初始化乙個 sales_item 物件,然後用它來按值初始化myobj。編譯器通過執行 sales_item 的預設建構函式來按值初始化乙個sales_item。

為了定義到類型別的隱式轉換,需要定義合適的建構函式。

可以用單個實參來呼叫的建構函式定義了從形參型別到該類型別的乙個隱式轉換。

讓我們再看看定義了兩個建構函式的 sales_item 版本:

class sales_item

sales_item(std::istream &is);

// as before

};這裡的每個建構函式都定義了乙個隱式轉換。因此,在期待乙個 sales_item型別物件的地方,可以使用乙個 string 或乙個 istream:

string null_book = "9-999-99999-9";

// ok: builds a sales_itemwith 0 units_soldand revenue from

// and isbn equal to null_book

item.same_isbn(null_book);

這段程式使用乙個 string 型別物件作為實參傳給 sales_item 的same_isbn 函式。該函式期待乙個 sales_item 物件作為實參。編譯器使用接受乙個 string 的 sales_item 建構函式從 null_book 生成乙個新的sales_item 物件。新生成的(臨時的)sales_item 被傳遞給 same_isbn。

更成問題的是從 istream 到 sales_item 的轉換:

這個行為是否我們想要的,依賴於我們認為使用者將如何使用這個轉換。在這種情況下,它可能是乙個好主意。book 中的 string 可能代表乙個不存在的isbn,對 same_isbn 的呼叫可以檢測 item 中的 sales_item 是否表示乙個空的 sales_item。另一方面,使用者也許在 null_book 上錯誤地呼叫了 same_isbn。

// ok: uses the sales_item istream constructor to build an object

// to pass to same_isbn

item.same_isbn(cin);

這段**將 cin 隱式轉換為 sales_item。這個轉換執行接受乙個 istream的 sales_item 建構函式。該建構函式通過讀標準輸入來建立乙個(臨時的)sales_item 物件。然後該物件被傳遞給 same_isbn。

這個 sales_item 物件是乙個臨時物件。一旦 same_isbn 結束,就不能再訪問它。實際上,我們構造了乙個在測試完成後被丟棄的物件。這個行為幾乎肯定是乙個錯誤。

可以通過將建構函式宣告為 explicit,來防止在需要隱式轉換的上下文中使用建構函式:

class sales_item

explicit sales_item(std::istream &is);

// as before

};

explicit 關鍵字只能用於類內部的建構函式宣告上。在類的定義體外部所做的定義上不再重複它:

// error: explicit allowed only on constructor declaration in class header

explicit sales_item::sales_item(istream& is)

現在,兩個建構函式都不能用於隱式地建立物件。前兩個使用都不能編譯:

item.same_isbn(null_book); // error: string constructor is explicit

item.same_isbn(cin); // error: istream constructor is explicit

當建構函式被宣告 explicit 時,編譯器將不使用它作為轉換

操作符。

只要顯式地按下面這樣做,就可以用顯式的建構函式來生成轉換:

string null_book = "9-999-99999-9";

// ok: builds a sales_itemwith 0 units_soldand revenue from

// and isbn equal to null_book

item.same_isbn(sales_item(null_book));

在這段**中,從 null_book 建立乙個 sales_item。儘管建構函式為顯式的,但這個用法是允許的。顯式使用建構函式只是中止了隱式地使用建構函式。

任何建構函式都可以用來顯式地建立臨時物件。通常,除非有明顯的理由想要定義隱式轉換,否則,單形參建構函式應該為 explicit。將建構函式設定為explicit 可以避免錯誤,並且當轉換有用時,使用者可以顯式地構造物件。

預設建構函式和合成預設建構函式

當我們沒有為類中的物件提供初始值,此時就會執行預設初始化,類會通過乙個特殊的建構函式來控制預設初始化過程,這個函式叫做預設建構函式,這個函式並不需要任何的實參,但是如果我們的類沒有顯式地定義建構函式,那麼編譯器就會為我們隱式地定義乙個預設建構函式 只要沒有顯式定義建構函式,編譯器就會提供預設建構函式...

預設建構函式

原諒我是個菜鳥,總在些小地方不經意出錯。include using namespace std class x x x private int a int main 你是否看得出 出錯了呢?接下來讓我們編譯看看 constructfunc.cpp in function int main const...

預設建構函式

如果定義了其他建構函式,最好也提供乙個預設建構函式 不帶引數或帶預設引數的建構函式 include include include using namespace std class nodefault struct a struct b nodefault b member 錯誤 沒有合適的預設建...