雞啄米上一節中給大家講解了類的聲明、成員的訪問控制和對象,今天雞啄米給大家講C++編程入門時同樣必須掌握的構造函數和析構函數。從上一講開始已經涉及到了很多面向對象設計的細節,大家慢慢跟著學習思考吧,實際上跟我們現實中的很多做事的思想是一致的,這也正是面向對象語言的特點,它是以現實中的事物和圍繞事物處理問題的思路為基礎的。

       某個類的對象之間都有哪些不同呢?首先是對象名不同,其次就是對象的數據成員的值不同。我們在聲明一個對象時,也可以同時給它的數據成員賦初值,稱為對象的初始化。

       1.構造函數

       我們在聲明一個變量時,如果對它進行了初始化,那么在為此變量分配內存空間時還會向內存單元中寫入變量的初始化。聲明對象有相似的過程,程序執行時遇到對象聲明語句時會向操作系統申請一定的內存空間來存放這個對象,但是它能像一般變量那樣初始化時寫入指定的初始值嗎?類的對象太復雜了,要實現這一點不太容易,這就需要構造函數來實現。

       構造函數的作用就是在對象被創建時利用特定的初始值構造對象,把對象置于某一個初始狀態,它在對象被創建的時候由系統自動調用,我們只需要使用默認的構造函數或者自己定義構造函數,而不用管怎么調用的。

       構造函數也是類的成員函數,除了有成員函數的所有特征外,還有一些不同之處:構造函數的函數名跟類名一樣,而且沒有返回值。構造函數一般被聲明為公有函數,除非我們不允許某個類生成對象則將它聲明為private或protected屬性。編譯器碰到對象聲明語句時,會自動生成對構造函數的調用語句,所以我們常說構造函數是在對象聲明時由系統自動調用的。

       上一講那個時鐘例子中,雞啄米沒有定義Clock類的構造函數,編譯器編譯時會自動生成一個默認形式的構造函數,這個構造函數不做任何事,那么為什么還要生成它呢?因為C++在對象建立時都會調用構造函數,所以如果沒有自己定義構造函數,那么即使是什么都不做的構造函數也是要有的。雞啄米現在在Clock類中加入自己定義的構造函數:

        class Clock
        {
        public:
                  Clock(int NewH, int NewM, int NewS);              //構造函數
                  void SetTime(int NewH, int NewM, int NewS);
                  void ShowTime();
        private:
                  int Hour, Minute, Second;
        };

        構造函數的實現:

        Clock::Clock(int NewH, int NewM, int NewS)
        {
                 Hour=NewH;
                 Minute=NewM;
                 Second=NewS;
        }

        建立對象時構造函數的作用:

        int main()
        {
                Clock c(0,0,0); //隱含調用構造函數,將初始值作為實參。
                c.ShowTime();
                return 0;
        }

       main函數中,創建對象c時,其實隱含了構造函數的調用,將初始值0,0,0作為構造函數的實參傳入。因為上面Clock類定義了構造函數,那么編譯器就不會再為它生成默認構造函數了。這里的構造函數有三個形參,那么建立對象就必須給出初始值了。

       因為構造函數也是一個成員函數,所以它可以直接訪問類的所有數據成員,可以是內聯函數,可以帶有形參表,可以帶默認的形參值,也可以重載,就是有若干個名字相同但形參個數或者類型不同的構造函數。

       2.拷貝構造函數

       我們可以將一個變量的值賦給另一個同類型的變量,那么可以將一個對象的內容拷貝給相同類的另一個對象嗎?可以,我們可以將第一個對象的數據變量的值分別賦給另一個對象的數據變量,但是,如果數據變量數很多的話那將是很麻煩的,這時候我們就需要有拷貝構造函數。

       拷貝構造函數是一種特殊的構造函數,因為它也是用來構造對象的。它具有構造函數的所有特性。拷貝構造函數的作用是用一個已經存在的對象去初始化另一個對象,這兩個對象的類類型應該是一樣的。定義拷貝構造函數的形式是:

       class 類名
       {
        public :
                   類名(形參);                    //構造函數
                   類名(類名 &對象名);   //拷貝構造函數
           ...
       };
       類名::類(類名 &對象名)    //拷貝構造函數的實現
       {  
                 函數體   
       }

       拷貝構造函數的形參是本類的對象的引用。

雞啄米:C++編程入門系列之十四(類與對象:構造函數和析構函數)

       程序中如果沒有定義拷貝構造函數系統會生成一個默認的拷貝構造函數,它會將作為初始值的對象的數據成員的值都拷貝到要初始化的對象中。下面雞啄米給大家一個坐標點類的例子,X和Y數據成員分別為點的橫坐標和縱坐標:

       class Point
       {
       public:
                   Point(int xx=0,int yy=0)    {X=xx; Y=yy;}
                   Point(Point &p);
                   int GetX() {return X;}
                   int GetY() {return Y;}
       private:
                   int  X, Y;
       };

      此類中聲明了內聯構造函數和拷貝構造函數??截悩嬙旌瘮档膶崿F如下:

      Point::Point(Point &p)
      {
                  X=p.X;
                  Y=p.Y;
                  cout<<"拷貝構造函數被調用"<<endl;
      }

      拷貝構造函數在以下三種情況下會被調用:

      a.當用類的一個對象去初始化該類的另一個對象時系統自動調用拷貝構造函數實現拷貝賦值。

       int main()
       { 
                Point A(1,2);
                Point B(A); //拷貝構造函數被調用
                cout<<B.GetX()<<endl;
                return 0;
       }

       b.若函數的形參為類對象,調用函數時,實參賦值給形參,系統自動調用拷貝構造函數。例如:

       void fun1(Point p)
       {  
                cout<<p.GetX()<<endl;
       }
       int main()
       {  
               Point A(1,2);
               fun1(A); //調用拷貝構造函數
               return 0;
       }     

       c.當函數的返回值是類對象時,系統自動調用拷貝構造函數。例如:

       Point fun2()
       {   
              Point A(1,2);
              return A; //調用拷貝構造函數
       }
       int main()
       {
             Point B;
             B=fun2();
             return 0;
       }

       最后這種情況怎么調用的拷貝構造函數呢?對象A是局部對象,在fun2函數執行完就釋放了,那怎么將它拷貝給對象B呢?編譯器在執行B=fun2()時會創建一個臨時的無名對象,在執行return A時實際上是調用了拷貝構造函數將A的值拷貝到了臨時對象中,A就釋放了,然后將臨時對象的值再拷貝到對象B中。

       3.析構函數

       自然萬物都是有生有滅的,類的對象也一樣是有生命周期的,一樣會消亡。雞啄米給大家講一種情況:如果在函數中聲明了一個對象,那么在這個函數運行完返回調用函數時,聲明的對象也會釋放,就像上面說的fun2函數中對象A那樣。

       在對象釋放時都有什么工作要做呢?我們經常遇到的情況就是:構造函數時動態申請了一些內存單元,在對象釋放時就要同時釋放這些內存單元。動態分配內存的知識后面雞啄米會講。

       析構函數和構造函數的作用是相反的,它會在對象被刪除之前做一些清理工作。析構函數是在對象要被刪除時由系統自動調用的,它執行完后對象就消失了,分配的內存空間也釋放了。

       析構函數是類的一個公有函數成員,它的名稱是在類名前加“~”形成,不能有返回值,大家注意下,它和構造函數不同的是它不能有任何形參。如果沒有定義析構函數系統也會自動生成一個默認析構函數,默認析構函數也不會做任何工作。一般如果我們想在對象被刪除之前做什么工作就可以把它寫到析構函數里。

       雞啄米還是在上面那個坐標點類中加入析構函數給大家看下析構函數怎么用:

       class Point
       {    
       public:
                   Point(int xx, int yy);
                   ~Point();
                   //...其他函數原型
       private:
                  int X, int Y;
                  char *p;
       };

       下面是構造函數和析構函數的實現:

       Point::Point(int xx,int yy)
       {     
                 X=xx;   
                 Y=yy;
                 p=new char[20];     // 構造函數中動態分配char型內存
       }
       Point::~Point()
       {
                delete []char;      // 在類析構時釋放之前動態分配的內存
       }
       //...其他函數的實現略

       new和delete的用法雞啄米后面會講,這里只是讓大家看下析構函數有什么作用。

       構造函數和析構函數是大家C++編程入門時必須掌握的內容,很多公司筆試或面試時都會考到,大家掌握好啊,呵呵。雞啄米謝謝大家關注我的教程!我們一起努力!

 

除非特別注明,雞啄米文章均為原創
轉載請標明本文地址:http://www.028keji.com/software/51.html
2011年9月22日
作者:雞啄米 分類:軟件開發 瀏覽: 評論:18