c++類構造函數理解

記錄一下我對C++類構造函數的理解。

首先,構造函數分成兩種,默認構造函數和非默認構造函數(好吧,就這麼叫它)。

默認構造函數只能有一個,如果沒有自定義構造函數,那麼編譯器將自動生成一個默認構造函數,當然這個構造函數不會做任何事情。如果程序定義了構造函數(包括默認和非默認),編譯器都不再自動提供默認構造函數。

如class C, 對應自動生成的默認構造函數爲C() { };

程序員可以自定義默認構造函數,而且只能定義一個默認構造函數。如定義class A.

Class A

{

    public:

//       A() { }  //constructor1, do nothing,默認構造函數

//       A(int m=1):v1(m);//construct2,默認構造函數,提供了部分的默認初始化值

//       A(int m=1, int n=2):v1(m), v2(n);//construct3, 默認構造函數,提供了所有的默認初始化值

//       A(int m); //construct4,非默認構造函數, 提供一個參數

//       A(int m, int n); //construct5, 非默認構造函數, 提供兩個參數


    private:

        int v1;

        int v2;

}

下面具體分析一下各種定義方式會帶來的問題:

1)不定義任何構造函數,這個上面說過,編譯器會自動構造一個默認構造函數,等價於定義了construct1。

2) 只定義construct2, 可以正確編譯運行。但是無法以形式A a(1,2)定義對象,這是顯然的。

3)    只定義construct3, 可以正確編譯運行。

4)    定義2個及以上默認構造函數的情況。

       想來同時提供construct2和construct3是不會矛盾的,而且符合重載的要求,但是事實並非如此。

       同時定義construct2和construct3,  以形式 A a;定義對象將會導致編譯器報錯。

       error: call of overloaded ‘A()’ is ambiguous
       note: candidates are: A::A(int, int)
       note:                              A::A(int)

       以形式A a(1);定義對象將會導致報錯:

        error: call of overloaded ‘A(int)’ is ambiguous
        note: candidates are: A::A(int, int)
        note:                 A::A(int)

        note:                 A::A(const A&)

        然而,以形式 A a(1,2);定義對象則可以正確通過編譯並運行。   這是什麼原因?編譯器工作的原理是什麼?不知道....

5) 從重載的角度分析,construct2和construct3簽名一致,construct4和construct5簽名一致,不能同時定義。實驗結果證實確實如此。

6) construct4和5恰爲構造函數重載的典型實例,當然能正確編譯運行。

7 )  只提供非默認構造函數,只有construct4或5或同時有4和5而無1/2/3

       這將導致無法使用 A  a;的形式來聲明對象。因爲以A a的形式聲明對象是調用默認構造函數來構造對象的。編譯器找不到默認構造函數自然會報錯。

       與此同時對象數組的聲明A  a[10];也將得到報錯,原理同上。

      那麼如下聲明並初始化對象數組會是什麼結果呢?

      A  a[3] = {

           A(1,1),  A(2,2), A(3,3)

     }   //(再只定義了construct5的情況下)

      在C++ Primer Plus中有如下語句:“要創建類對象數組,則這個類必須有默認構造函數。”,“初始化對象數組的方案位,首先使用默認構造函數創建數組元素,然後花括號中的構造函數將創建臨時對象,然後將臨時對象的內容複製到相應的元素中。因此要創建類對象數組,則這個類必須有默認構造函數。”

      在我看來這話不盡然正確,或許Stephen寫書的時候使用的編譯器是如他所說,但是我在g++中做的實現不是如此。上述對象數組的定義在g++下能夠通過編譯,並運行正確。g++的策略是,如果花括號中明確調用了構造函數來初始化對象,且該構造函數已被定義,那麼將直接使用構造函數來創建對象元素,無臨時對象和複製的過程。然而,如果花括號中有任一元素沒有顯示的使用已定義的構造函數初始化,那麼編譯器將調用默認構造函數來初始化該對象元素,這時如果沒有定義默認構造函數,將無法通過編譯。

      那麼在上述情形中   A  a[4] = { A(1,1), A(2,2), A(3,3)}, 將無法通過編譯,因爲A[3]實際上要通過調用默認構造函數來初始化,而這裏又沒有定義默認構造函數。


關於默認構造函數的調用。

實際上在g++中,未提供任何構造函數時,初始化一個對象,編譯器並不去調用自動提供的那個什麼也不做的構造函數,而僅僅是在內存中爲對象分配了空間。(這應該是編譯器的優化手段,不同編譯器不同。)如果程序自己定義了一個A(){};雖然也什麼都不做,但是初始化對象時,該構造函數卻會被執行。這就帶來了額外的開銷,所以如果真的不定義任何構造函數的話,那麼就乾脆也別定義A(){};這麼個空構造函數了,無意義,反而降低效率。


構造函數的參數傳遞,參數的第一個值爲隱藏的this指針,指向對象的首地址,其次纔是傳進去的參數。

類對象的大小:無任何屬性域的對象,大小爲1, 即類class A{};  有屬性域的類,大小爲屬性大小之和。this指針不佔內存。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章