<form id="hz9zz"></form>
  • <form id="hz9zz"></form>

      <nobr id="hz9zz"></nobr>

      <form id="hz9zz"></form>

    1. 明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

      溫故知新----再談構造函數(轉:不轉了)

      [摘要]溫故知新 ----再談構造函數作者:HolyFire如果不知道構造函數的請先看一下《由始至終----構造與析構》,看過的我就不再多言,直接轉入話題。定義一個類的的實例的時候,可以看到這樣的形式cla...
      溫故知新

       ----再談構造函數

      作者:HolyFire

      如果不知道構造函數的請先看一下《由始至終----構造與析構》,看過的我就不再多言,直接轉入話題。

      定義一個類的的實例的時候,可以看到這樣的形式

      classA a;//構造函數不需要參數

      不需要參數的構造函數稱之為缺省構造函數。

      不需要參數有兩種情況

      1:構造函數沒有參數

      2:構造函數有參數但可以不給出

      class A{

      public:

       A();//構造函數沒有參數

       A( int I = 10 );//構造函數的參數有缺省值,可以不用給出

      };

      這兩種情況都是缺省構造函數,但是由于缺省構造函數的特殊性(他是被自動調用的),編譯器無法判斷需要調用那一個,所以規定缺省構造函數只能有一個。

      缺省構造函數的出現,意味著一個類型可以不依賴約束條件而被創建,就象一些細小的單元,質子,中子和電子,他們的有很大的類似性,不需要用條件來分辨他們被創建的信息。當然不需要用條件來分辨他們被創建的信息也包含了第二種情況,從流水線上生產的統一品種的產品很多都是用同一種方式的,那么創建他們的信息基本一致,也就是所符合第二種情況,參數可以采用缺省值。

      這個例子我們可以舉一個例子,我們創建一個指針類的時候,常常把他指向的內容置為空值,這很容易理解,我們需要一個指針,但是現在還不知道指向誰,等到我們要使用它的時候,不一定是知道他是否指向過別的對象,為了簡化問題,一開始就將他置空,但是有時候我們需要用參數在創建的時候就給出指向的對象,特別是在產生臨時對象的時候尤為管用,那么,我們使用一個參數缺省值為空的缺省構造函數。

      classA a( a1 );//構造函數有參數,而參數為一個相同的類型

      這樣的構造函數叫做拷貝構造函數,意思就是將類一個實例的內容復制到新創建的實例中去,為什么要這么做呢。我們來研究一下。

      我們平時使用基本類型的時候,可以使用賦值語句,將相同類型的某個對象的內容賦給另一個對象

      int a = 3;

      int b;

      b = a; //這樣的話,b中就有和a一樣的內容了

      還可在允許的情況下使用不同類型的賦值

      int a = 3;

      long b;

      b = a;//這樣的話,b也能包含有和a一樣的內容了

      我們在設計類的時候應該也是將一個類作為一個個體,一個類型來處理,而且在現實中這樣的行為也是存在的,一個人的個人資料可以寫在不同的紀錄簿上,一個軟件可以拷貝好幾份。

      所以在面向對象編程中,這個問題不容忽視。

      回到基本類型上,基本類型的處理編譯器完成了,在C++中很簡單,基本類型占用存儲空間是連續的,所以不管原來的內容是什么,只要照搬照抄就可以了,這種負值方式叫做逐位拷貝,簡稱位拷貝。

      int a = 3;

      int b;

      假設:對象在內存中的存儲順序是先高后低,每個內存單元為1字節(BYTE)=8位(BIT)

      //假設這是a(int)的存儲空間

      0
      3


      //假設這是b(int)的存儲空間

      ?
      ?


      b =a ;

      //將a的內容拷貝到b中

      0
      3


       

      ?
      ?


      //a

      0
      3


      //b

      0
      3


      我們設計的類在內存中也是連續的,使用這樣的拷貝方法會得到一個一模一樣的同類型實例。而且編譯器我們處理了這一件事(C++的編譯器真好,它能解決的事,就不用麻煩我們了),也就是說即使我們沒有定義拷貝構造函數,編譯器也會在需要使用的時候,自己產生一個拷貝構造函數,使用的方法就是位拷貝。但是這樣好嗎,使用這種方法產生的新類可以安全的工作嗎,應該有不少朋友已經產生了疑問。

      什么時候可以讓編譯器自己處理拷貝構造函數。

      #include <iostream>

      using namespace std;

      class A{

      private:

      int x;

      int y;

      int z;

      public:

      A():x(0),y(0),z(0){ }

      A( int _x = 0 , int _y = 0 , int _z = 0 ):x(_x),y(_y),z(_z){ }

      friend ostream& operator <<( ostream& , A const& );

      };

      ostream& operator <<( ostream& out , A const& arg )

      {

      out << "This is a Instance of A" << endl;

      out << "Member Data x is : " << arg.x << endl;

      out << "Member Data y is : " << arg.y << endl;

      out << "Member Data z is : " << arg.z << endl;

      return out;

      }

      void main()

      {

      A a( 1 , 12 ,123 );

      A b(a);

      cout << "This is a!" << endl;

      cout << a << endl;

      cout << "b is a copy of a!" << endl;

      cout << b;

      }

      結果是:

      This is a!

      This is a Instance of A

      Member Data x is : 1

      Member Data y is : 12

      Member Data z is : 123

      b is a copy of a!

      This is a Instance of A

      Member Data x is : 1

      Member Data y is : 12

      Member Data z is : 123

      可以看出,位拷貝得出的結果是正確的。

      上面的例子中成員變量都是在編譯期間決定的,在內存中的位置也相對固定,如果成員變量的內容是在運行期間決定的呢,比如字符串成員變量,他需要在堆中動態分配內存。還能正常工作嗎,繼續看例子。

      #include <iostream>

      #include <string.h>

      #include <mem.h>

      using namespace std;

      class A{

      private:

       char * data;

      public:

       A():data(NULL){ }

       A( char * _data ):data(NULL)

      {

      if( !_data )

       return;

      int length = strlen(_data) +1;

      data = new char[length];

      memcpy( data , _data , length );

      }

       ~A()

      {

      if( data )

       delete data;

      }

       void Clear( void )

      {

      if( data )

       {

       memset( data , 0 , strlen( data ) );

       delete data;

       }

      data = NULL;

      }



       friend ostream& operator <<( ostream& , A const& );

      };

      ostream& operator <<( ostream& out , A const& arg )

      {

       out << "This is a Instance of A" << endl;

       if( arg.data && *arg.data )

      out << "Member Data data is : " << arg.data << endl;

       else

      out << "Member Data data is : NULL" << endl;

       return out;

      }

      void main()

      {

       A a( "abcdefg" );

       A b(a);

       cout << "This is a!" << endl;

       cout << a << endl;

       cout << "b is a copy of a!" << endl;

       cout << b << endl;

       a.Clear();

       cout << "Where a's mem clear!" << endl;

       cout << a;

       cout << "God! b's mem clear!" << endl;

       cout << b << endl;

      }

      結果是:

      This is a!

      This is a Instance of A

      Member Data data is : abcdefg

      b is a copy of a!

      This is a Instance of A

      Member Data data is : abcdefg

      Where a's mem clear!

      This is a Instance of A

      Member Data data is : NULL

      God! b's mem clear!

      This is a Instance of A

      Member Data data is : NULL//不!a中釋放了內存連帶著b的一起釋放掉了。

      這是當然的由于位拷貝,b中的data只是將a中的data復制過來了而已,并沒有分配內存,拷貝字符串的內容。顯而易見,使用位拷貝不能滿足我們的要求,原來只需要簡單的將成員變量的值簡單的復制,這種我們稱之為:淺拷貝。現在我們需要處理對應成員變量,用其他方法來得到我們需要的結果,這種我們稱之為:深拷貝。

      這樣我們就需要自己寫拷貝構造函數來實現深拷貝了。

      #include <iostream.h>

      #include <string.h>

      #include <mem.h>

      class A{

      private:

       char * data;

      public:

       A():data(NULL){ }

       A( char * _data ):data(NULL)

      {

      if( !_data )

       return;

      int length = strlen(_data) +1;

      data = new char[length];

      memcpy( data , _data , length );

      }

       A( A const& arg )

      {

      if( !arg.data )

       return;

      int length = strlen(arg.data) +1;

      data = new char[length];

      memcpy( data , arg.data , length );

      }

       ~A()

      {

      if( data )

       delete data;

      }

       void Clear( void )

      {

      if( data )

       {

      memset( data , 0 , strlen( data ) );

       delete data;

       }

      data = NULL;

      }

       friend ostream& operator <<( ostream& , A const& );

      };

      ostream& operator <<( ostream& out , A const& arg )

      {

       out << "This is a Instance of A" << endl;

       if( arg.data && *arg.data )

      out << "Member Data data is : " << arg.data << endl;

       else

      out << "Member Data data is : NULL" << endl;

       return out;

      }

      void main()

      {

       A a( "abcdefg" );

       A b(a);

       cout << "This is a!" << endl;

       cout << a << endl;

       cout << "b is a copy of a!" << endl;

       cout << b << endl;

       a.Clear();

       cout << "Where a's mem clear!" << endl;

       cout << a;

       cout << "Good! b's mem not clear!" << endl;

       cout << b << endl;

      }

      結果是:

      This is a!

      This is a Instance of A

      Member Data data is : abcdefg

      b is a copy of a!

      This is a Instance of A

      Member Data data is : abcdefg

      Where a's mem clear!

      This is a Instance of A

      Member Data data is : NULL

      Good! b's mem not clear!

      This is a Instance of A

      Member Data data is : abcdefg //哈哈,這正是我想得到的結果。

      如果能使用位拷貝,盡量讓編譯器自己用位拷貝的方式處理,這樣會提高效率。但是一定要謹慎,不然會產生不可預料的結果,如果你的類中有一個成員變量也是類,它使用了深拷貝,那么你也一定要使用深拷貝。

      另外,我在《白馬非馬----繼承》中說到,一個類型的的派生類是該類型的一種。那么。

      class A;

      class B: public A{

      };

      B b;

      A a(b);

      這樣的形式是正確的。事實上,b先切片退化成一個臨時變量tempb,類型是class A,有關A的部分原封不動的保留下來,然后使用A a(tempb)這樣的方式成功的調用了。

      拷貝構造函數并非可有可無!不能用其他函數來替代

      看這樣的例子

      void function( A a);

      在函數調用的時候按值傳遞參數,那么將在棧里產生一個class A的臨時變量,如果沒有拷貝構造函數,這個過程就無法自動完成,如果沒用設計好淺拷貝或深拷貝,那么可能得不到正確結果。如果拷貝構造函數正確,那么我們可以輕松的獲得我們想要的結果----按值傳遞的參數在函數執行后不受影響。

      classA a = a1;//拷貝構造函數

      事實上就是這樣的形式。

      ClassA a(a1);//可以改成這種形式




      日韩精品一区二区三区高清