程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++默認構造函數

C++默認構造函數

編輯:C++入門知識

C++默認構造函數


C++ primer中的三個地方講解了默認構造函數:

P44變量初始化規則

P227函數(構造函數)

P388類(構造函數初始化式)

P392默認構造函數

 

一, 變量初始化規則(P44和P227)

1,對於類類型的成員,調用該成員所屬類自身的默認構造函數實現初始化。

2,內置類型成員的初值依賴於對象定義的位置,如果對象在全局作用域中定義(既不在任何函數中)或定義為靜態局部對象,則這些成員將被初始化為0;

3,如果對象在局部作用域中定義,則這些成員沒有初始化

 

二,初始化列表

由P49和P50可知:const和引用類型的成員,不能對它們賦值,所以必須在定義時初始化!

所以,對於const對象或引用類型的對象,在開始執行構造函數的函數體之前,要完成初始化!(P389)

初始化 const 引用類型數據成員的唯一機會是在構造函數初始化列表中

同時,由於沒有默認構造函數的類類型的成員,編譯器嘗試使用默認構造函數將會失敗,所以也必須在初始化列表中初始化!

 

三, 默認構造函數的定義

在P225定義了默認構造函數:沒有形參。

再看看百度百科的定義:默認構造函數(default constructor)就是在沒有顯式提供初始化式時調用的構造函數。它由不帶參數的構造函數,或者為所有的形參提供默認實參的構造函數定義。如果定義某個類的變量(對象)時沒有提供初始化式就會使用默認構造函數。

在P227中說:如果沒有為一個類顯示定義任何構造函數,編譯器將自動為這個類生成默認構造函數,通常稱為合成的默認構造函數,它一般用於僅包含類類型成員的類!它不會自動初始化內置類型或復合類型的成員。所以,對於含有內置類型或復合類型成員的類,通常應該定義他們自己的默認構造函數初始化這些成員。

 

問題:是不是所有的類都有默認構造函數?答案是否定的。

1, 雖然編譯器會為類自動生成一個合成的默認構造函數,但是它僅對於包含類類型成員的類。所以對於只含有內置類型或復合類型成員的類,如果不自定義他們的構造函數,編譯器不會自動為其生成一個合成的默認構造函數的!

 

class Sales_item{
public:
        ......
        //Sales_item():units_sold(0), revenue(0.0){}
private:
        //std::string isbn;//去掉它,編譯器就不會為本類自動生成默認構造函數!
        unsigned units_sold;
        double revenue;
};

 

2, 當然,如果類顯示定義了默認構造函數,但是沒有為其類類型的成員在初始化列表中顯示初始化,那麼會調用該成員所屬類自身的默認構造函數實現初始化!這部分工作也是由編譯器自動添加到當前默認函數完成的!

 

class Sales_item{
public:
        ......
        Sales_item():units_sold(0), revenue(0.0){}//如果為isbn成員也在這裡顯示初始化,那麼編譯器將忽略string的默認構造函數~
private:
        std::string isbn;//雖然顯示定義的默認構造函數,但是它的初始化是由編譯器自動添加完成的!
        unsigned units_sold;
        double revenue;
};

3, 如果顯示定義帶參數的構造函數(任意的構造函數),那麼編譯器不會再生成默認構造函數!

(通常也會自動生成一個合成的默認構造函數,此時,類有兩個構造函數!同時編譯器也完成上面第2點的工作。這句話是×的,見P392

 

class Sales_item{
public:
        ......
        Sales_item(double rev):units_sold(0), revenue(rev){}
private:
        std::string isbn;
        unsigned units_sold;
        double revenue;
};

關於這一點需要上機測試(TODO:測試結果之後附上),因為百科上解釋如下:

一個類顯式地聲明了任何構造函數,編譯器不生成公有的默認構造函數。這這種情況下,如果程序需要一個默認構造函數,需要由類的設計者提供。

 

4,如果派生列的基類中有自定義的nontrivial default constructor,那麼編輯器會為每一個派生類合成一個nontrivial default constructor,以調用基類自定義的nontrivial default constructor。

5,如果一個類裡隱式的含有Virtual tabel(Vtbl)或者pointer member(vptr)

6,如果一個類虛繼承與其他類

編譯器會為每個有虛函數的類創建一個虛函數表,該虛函數表將被該類的所有對象共享。類的每個虛成員占據虛函數表中的一行。如果類中有N個虛函數,那麼其虛函數表將有N*4字節的大小。 虛函數(Virtual Function)是通過一張虛函數表(Virtual Table)來實現的。簡稱為V-Table。在這個表中,主要是一個類的虛函數的地址表,這張表解決了繼承、覆蓋的問題,保證其真實反應實際的函數。這樣,在有虛函數的類的實例中這個表被分配在了這個實例的內存中,所以,當用父類的指針來操作一個子類的時候,這張虛函數表就顯得由為重要了,它就像一個地圖一樣,指明了實際所應該調用的函數。 編譯器應該是保證虛函數表的指針存在於對象實例中最前面的位置(這是為了保證取到虛函數表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。 這意味著可以通過對象實例的地址得到這張虛函數表,然後就可以遍歷其中函數指針,並調用相應的函數。

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved