歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

C++構造函數/析構函數/賦值函數

在編寫C++程序的時候,我們會為特定某一類對象申明類類型,幾乎我們申明的每一個class都會有一個或多個構造函數、一個析構函數、一個賦值運算符重載=、以及拷貝構造函數。這些函數控制著類對象的基礎操作,確保新定義的對象的初始化、完成對象撤銷時的清理工作、賦予對象新值。如果這些函數的操作出錯,則會導致嚴重的後果,所以確保這些函數的操作行為正常是非常重要的。

一、編譯器默認生成的函數

      如果我們編寫一個空類,編譯器會為我們默認生成構造函數、析構函數、賦值運算符、拷貝構造函數。

      例如當我們定義

      class Empty{ };

      就好像我們寫下了如下代碼(紅色是編譯器默認生成)

  class Empty{

      public:

        Empty(){....}   //默認構造函數

        Empty(const Empty &rhs){....}   //默認拷貝構造函數

        ~Empty(){....}   //默認析構構造函數

        Empty& operator=(const Empty &rhs){....}   //賦值運算符

        Empty* operator&(){...} //取地址運算符
                          const Empty* operator&() const{...} //取地址運算法的const版本

     };

    1. 說明:(1)這些函數只有在被調用的時候,才會被編譯器創建出來;

      (2)四個函數都public且inline的;

      (3)如果顯示的定義了其中某一個函數,那麼編譯器就不會生成其對應的默認的版本;

      (4)自定義的拷貝構造函數不僅會覆蓋默認的拷貝構造函數,同時也會覆蓋默認的構造函數,下面的函數class構造函數,不能通過編譯

 
 1 #include <iostream>
 2 using  namespace std;
 3 class Empty
 4 {
 5 public:
 6       Empty(const Empty &Copy){};
 7 };
 8 int main(int argc, char** argv)
 9 {
10     Empty a;
11     return 0;
12 }
View Code

     2. 實例:

    下面的代碼會讓編譯器創建默認的構造函數  Empty e1; //默認構造函數

                         Empty e2(e1);//拷貝構造函數

                         e2 = e1;//賦值運算符             

 1 #include <iostream>
 2 using namespace std; 
 3 
 4 class Empty{
 5 
 6 public:
 7     Empty(){cout << "create" << endl;}
 8     Empty(const Empty &Copy){ cout << "copy" << endl;}
 9     Empty& operator=(const Empty &Assig){cout << "assign=" <<  
10                                  endl;}
11     Empty* operator&(){cout << "&" << endl;}
12     const Empty* operator&() const {cout << "&1" << endl;}
13     ~Empty(){cout << "delete" << endl;}
14     };
15 int main()
16 {
17     Empty *e = new Empty(); // create
18     delete e;   //delete
19     Empty e0; //create
20     const Empty e1; //create
21     Empty e2(e1); //copy
22     Empty e3; //create
23     e3 = e1;//assign=
24     cout << &e0 << endl;//& 0x602080
25     const Empty *p = &e1;//&1
26     cout << p << endl;  //0x602080
27     return 0;
28 }
29 //e0,e1,e2,e3對象被撤銷時候刪除
30 delete
31 delete
32 delete
33 delete
View Code

 二、構造函數

      1. 構造函數的作用

   構造函數是特殊的成員函數,用來在創建對象時完成對對象屬性的一些初始化等操作, 當創建對象時, 對象會自動調用它的構造函數。

      2. 默認構造函數

     正如第一部分所述,如果沒有為一個類顯示定義任何構造函數、編譯器將自動為這個類生成默認構造函數。默認構造函數將依據變量初始化的規則初始化類中的所有成員:

       (1)對於具有類類型的成員,會調用該成員所屬類自身的默認構造函數實現初始化;

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

       (3)默認構造函數一般適用於僅包含類類型的成員的類;

       (4)由於默認構造函數不會初始化內置類型的成員,所以必須顯示定義類的構造函數。       

 1 #include <iostream>
 2 using  namespace std;
 3 class Empty
 4 {
 5 public:
 6     int a;
 7     string s;
 8 };
 9 
10 int main(int argc, char** argv)
11 {
12     Empty a;
13     cout << a.a << endl;//輸出a的值隨機
14     cout << a.s.size() << endl;//s是類類型被初始化為空串
15 }
View Code

      3. 構造函數的特點

         (1)在對象被創建時自動執行;

    (2)構造函數的函數名與類名相同;

    (3)沒有返回值類型、也沒有返回值;

    (4)構造函數不能被顯式調用;

      4. 重載構造函數

    可以為一個類申明的構造函數的數量沒有限制,只要每個構造函數的形參表示唯一的。定義類對象的時候,實參指定使用哪個構造函數。比如我們定義類Sales_item,它的構造函數有三個,在定義類的新對象時,可以使            用這些構造函數中的任意 一個。

 
 1 Class Sales_item{
 2 public:
 3         Sales_item(const std::string&);
 4         Sales_item(std::istream&);
 5         Sales_item();
 6 };    
 7 int main()
 8 {
 9   Sales_item empty;//使用缺省的無參構造函數
10   Sales_item Primer_3rd_Ed("0-201-82470-1");
11   Sales_item Primer_4th_ed(cin);
12   return 0;  
13 }
View Code

      5. 構造函數自動執行

     只要創建對應類類型的一個對象,編譯器就運行一個構造函數。

1 Sales_item Primer_2nd("0-201-54848-8");//運行帶string參數的構造函數
2 Sales_item *p = new Sales_item();//通過默認構造函數初始化該對象
View Code

      6. 構造函數初始化列表

         對象中的一些數據成員除了在構造函數體中進行初始化外,還可以通過構造函數初始化列表進行初始化,構造函數初始化列表只在構造函數的定義中而不是聲明中指定。從概念上將講,可以認為構造函數分兩個階段執                行:(1)初始化階段;(2)普通計算階段,計算階段由構造函數函數體中的所有語句組成;(3)構造函數就是按照成員定義的次序初始化成員的次序。

         不管成員是否在構造函數初始化列表中顯式初始化,類類型的數據成員總是在初始化階段初始化。初始化階段發生在計算階段開始之前。   

1  Sales_item::Sales_item(const string &book): isbn(book), units_sold(0),            revenue(0.0){}
View Code

        說明:對於const類型成員、引用類型的成員變量都必須在構造函數初始化列表中進行初始化,例如下面的代碼就是錯誤的,必須在初始化列表中對類成員變量進行初始化。   

 
 1 class ConstRef{
 2     public:
 3              ConstRef(int ii);
 4     private:
 5             int i;
 6             const int ci;
 7             int &ri;
 8 };
 9 ConstRef::ConstRef(int ii)
10 {
11    //賦值
12    i = ii;
13    ci = ii; //錯誤,不能對const成員賦值
14    ri = i;//不能對引用變量賦值
15 }
16 記住,可以初始化const對象或引用類型的對象,但不能對它們賦值。在開始執行構造函數體之前,要完成初始化。初始化const或引用類型的唯一機會是在構造函數初始化列表中。編寫以上構造函數的正確方式為
17    ConstRef::ConstRef(int ii):i(ii), ci(i), ri(ii)
View Code

 二、析構函數

         構造函數的一個作用是自動獲取資源。例如,構造函數可以分配一個緩沖區或打開一個文件,在構造函數中分配了資源之後,需要一個對應操作自動回收或釋放資源。析構函數就是這樣一個特殊函數,它可以完成所需資     源的回收,作為類的構造函數的補充。

       1.何時調用析構函數

    a.刪除指向動態分配對象的指針

    b.實際對象(而不是對象的引用)超出作用域時

              c.撤銷一個容器(不管是標准庫容器還是內置數組)時,即超出容器的作用范圍時

       2.缺省析構函數

             a.編譯器總會為我們合成一個析構函數,其按照對象創建時的逆序撤銷每個非static成員,因此,它按照成員在類中申明的次序的逆序撤銷成員。

        b.缺省的析構函數並不刪除指針成員指向的對象

             c.析構函數與賦值操作符和復制構造函數之間的一個重要區別是,及時我們自己編寫了自己的析構函數,缺省的析構函數任然運行

        d.對於類類型的對象,合成析構函數調用其析構函數完成對象的釋放;對於內置類型的對象,合成析構函數則不做什麼操作

       3.何時編寫顯式析構函數

    許多類不需要顯式析構函數,尤其具有構造函數的類不一定需要定義自己的析構函數。僅在有些僅在有些工作需要析構函數完成時,才需要析構函數(顯式的)。析構函數並不僅限於用來釋放資源,一般而言,析構函數可以執行任意操作,該操作是類設計者希望該類對象在使用完畢後執行的。

Copyright © Linux教程網 All Rights Reserved