C++ 類的構造函數
默認構造函數
如果你定義一個類,并且沒有給它定義構造函數。編譯器會為這個類提供默認的構造函數。如果你提供了構造函數,編譯器是不會再為你提供一個默認構造函數的。編譯器提供的默認構造函數什么都沒做。類的成員變量將遵守默認的初始化規則。
編譯器提供的默認構造函數的初始化規則:
在棧和堆中的類對象的內置或復合類型成員變量將為臟數據;
在全局變量區的類對象的內置或復合類型成員變量初始化為0;
類對象成員將調用默認的構造函數來初始化;
#include <iostream> using namespace std; class Box { public: int length; int width; }; Box box1; int main() { Box box2; Box *pbox3 = new Box; cout<<"box1.length == "<<box1.length<<" box1.width == "<<box1.width<<endl; cout<<"box2.length == "<<box2.length<<" box2.width == "<<box2.width<<endl; cout<<"box3.length == "<<pbox3->length<<" box3.width == "<<pbox3->width<<endl; return 0; }
上面代碼的結果為:
box1.length == 0 box1.width == 0
box2.length == 2686792 box2.width == 1987092020
box3.length == 3811912 box3.width == 3801284
帶默認實參的構造函數
就像對普通函數一樣可以為構造函數的參數指定默認值。
如果你為類定義了一個默認構造函數,又定義了一個所有參數都有默認的值的構造函數。(技術上來說,這是重載了)用默認構造函數構造類對象時將會產生編譯錯誤。因為編譯器不知道選擇哪個重載函數。
構造函數的初始化列表
除了在構造函數的函數體中用明確的賦值表達式給類成員賦值(從嚴格的概念上來說這不是初始化),推薦的做法是使用初始化列表。初始化列表以一個冒號開始,緊接著一個一個用逗號分隔的數據成員列表,每個數據成員后跟一個放在圓括號中的初始化式。構造函數的初始化列表只能在實現中指定而不能在定義體中指定。而類的成員函數(構造函數也不例外)的實現既可以在類的定義體中(內聯函數),也可以在類的實現中。
成員的初始化次序
每個成員只能在初始化列表中指定一次。而且成員在初始化列表中出現的順序并不代表成員的實際初始化順序。成員的初始化順序是按照它們在類定義中出現的順序來的。所以成員的初始化最好不要相互依賴,如果你確定它們要相互依賴,你得清楚它們在類定義中的出現順序。
構造函數的構造的兩個階段
(1)初始化階段(根據默認的變量初始化規則和初始化列表來執行);(2)構造函數中的函數體執行階段(這時構造函數體內的賦值語句才會執行)。
為什么推薦使用初始化列表?
1.在許多類中,初始化和賦值嚴格來講都是低效率的:數據成員可能已經被直接初始化了,還要對它進行初始化和賦值。
2.比第一點提到的效率更重要的是,某些類型的數據成員必須要初始化。
有些類型的成員必須在初始化列表中進行初始化,比如const對象和引用類型對象。它們只能初始化而不能賦值。在執行構造函數體之前必須完成初始化。在函數體內對它們賦值會引發編譯錯誤。
類類型的成員變量也要特別注意,如果你不對它在初始化列表中的初始化,編譯器將會嘗試在初始化階段調用它的默認構造函數給他初始化。如果它沒有默認的構造函數,這將會導致運行時錯誤。另一種情況是你只在構造函數體內對類對象的成員進行了賦值。初始化階段將會調用該類對象成員的默認構造函數,計算階段將會調用構造函數體內指定構造函數。意思是該類對象成員調用了兩次構造函數,第二次的會覆蓋第一次的。
構造函數與隱式類型轉換、explicit
C++支持類型自動轉換??梢远x如何將其他類的對象隱式轉換為我們的類類型,也可以將我們的類類型對象隱式的轉換為其他類型。構造函數有個隱含規則:可以用單個實參類調用的構造函數定義了一個從該形參類型到該類類型的一個隱式轉換。有時候這不是你想要的,并且會引發錯誤。例如你定義了下面的類。
class Box { public: Box(int x=1,int y=1); int length; int width; }; Box::Box(int x,int y):length(x),width(y) { }
如果你Box box= 2來初始化一個Box對象。編譯器將2隱式轉換為一個Box對象,相當于調用了構造函數Box(2)。
如果你在需要Box類型參數的函數調用中傳入的是一個int實參,將會構造一個臨時的Box對象再傳入函數作參數。函數結束后,這Box對象也就消失了,這有什么用呢?這幾乎肯定是一個錯誤。對此我們可以:
1.用關鍵字explicit阻止構造函數定義的隱式轉換
在類構造函數的聲明前加上explicit關鍵字(注意不能在定義中加),可以阻止隱式轉換。
class Box { public: explicit Box(int x=1,int y=1); int length; int width; };
如果你再這樣定義一個Box對象:Box box = 2或者將int類型對象作為參數當做Box對象傳給某個函數,將會引發編譯錯誤。
2.每次轉換,自己顯示的使用構造函數。這樣可以防止隱式轉換。
在需要Box對象實參的的函數調用中用func(Box(2))來調用類的構造函數創建一個臨時對象,防止自動的隱式轉換。
建議:除非有明確的理由允許隱式轉換,可以用單個參數調用的構造函數都應該定義為explicit。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
新聞熱點
疑難解答