天天看點

拷貝構造函數和指派構造函數的異同

     我之是以認為指派構造函數不應該被稱為一個構造函數,是因為在調用指派構造函數的時候,類對象已經存在,自然談不上構造類對象,它隻是對該已經存在的類對象的成員變量進行修改(所謂的指派)操作。而拷貝構造函數是利用一個已經存在的類對象構造出一個新的類對象。

         由于并非所有的對象都會使用拷貝構造函數和指派函數,程式員可能對這兩個函數有些輕視。請先記住以下的警告,在閱讀正文時就會多心: 如果不主動編寫拷貝構造函數和指派函數,編譯器将以“位拷貝”的方式自動生成預設的函數。倘若類中含有指針變量,那麼這兩個預設的函數就隐含了錯誤。以類String 的兩個對象a,b 為例,假設a.m_data 的内容為“hello”,b.m_data 的内容為“world”。現将a 賦給b,預設指派函數的“位拷貝”意味着執行b.m_data = a.m_data。

這将造成三個錯誤:

            一是b.m_data 原有的記憶體沒被釋放,造成記憶體洩露;

            二是b.m_data 和a.m_data 指向同一塊記憶體,a 或b 任何一方變動都會影響另一方;

            三是在對象被析構時,m_data 被釋放了兩次。

拷貝構造函數和指派函數非常容易混淆,常導緻錯寫、錯用。

        拷貝構造函數是在對象被建立時調用的,而指派函數隻能被已經存在了的對象調用。以下程式中,第三個語句和第四個語句很相似,你分得清楚哪個調用了拷貝構造函數,哪個調用了指派函數嗎?

  String a(“hello”);

  String b(“world”);

  String c = a; // 調用了拷貝構造函數,最好寫成 c(a);

  c = b; // 調用了指派函數

     本例中第三個語句的風格較差,宜改寫成String c(a) 以差別于第四個語句。

類String 的拷貝構造函數與指派函數

 // 拷貝構造函數

 String::String(const String &other)

 {

  // 允許操作other 的私有成員m_data

  int length = strlen(other.m_data);

  m_data = new char[length+1];

  strcpy(m_data, other.m_data);

 }

 // 指派函數

 String & String::operate =(const String &other)

 {

  // (1) 檢查自指派

  if(this == &other)

  return *this;

  // (2) 釋放原有的記憶體資源

  delete [] m_data;

  // (3)配置設定新的記憶體資源,并複制内容

  int length = strlen(other.m_data);

  m_data = new char[length+1];

  strcpy(m_data, other.m_data);

  // (4)傳回本對象的引用

  return *this;

 }

           類String 拷貝構造函數與普通構造函數的差別是:在函數入口處無需與NULL 進行比較,這是因為“引用”不可能是NULL,而“指針”可以為NULL。

         類String 的指派函數比構造函數複雜得多,分四步實作:

 (1)第一步,檢查自指派。你可能會認為多此一舉,難道有人會愚蠢到寫出 a = a 這樣的自指派語句!的确不會。但是間接的自指派仍有可能出現,例如

  // 内容自指派

  b = a;

  …

  c = b;

  …

  a = c;

  // 位址自指派

  b = &a;

  …

  a = *b;

  也許有人會說:“即使出現自指派,我也可以不理睬,大不了化點時間讓對象複制自己而已,反正不會出錯!”他真的說錯了。看看第二步的delete,自殺後還能複制自己嗎?是以,如果發現自指派,應該馬上終止函數。注意不要将檢查自指派的if 語句

  if(this == &other)

  錯寫成為

  if( *this == other)

  (2)第二步,用delete 釋放原有的記憶體資源。如果現在不釋放,以後就沒機會了,将造成記憶體洩露。

  (3)第三步,配置設定新的記憶體資源,并複制字元串。注意函數strlen 傳回的是有效字元串長度,不包含結束符‘\0’。函數strcpy 則連‘\0’一起複制。

  (4)第四步,傳回本對象的引用,目的是為了實作象 a = b = c 這樣的鍊式表達。注意不要将 return *this 錯寫成 return this 。那麼能否寫成return other 呢?效果不是一樣嗎?不可以!因為我們不知道參數other 的生命期。有可能other 是個臨時對象,在指派結束後它馬上消失,那麼return other 傳回的将是垃圾。偷懶的辦法處理拷貝構造函數與指派函數如果我們實在不想編寫拷貝構造函數和指派函數,又不允許别人使用編譯器生成的預設函數,怎麼辦?偷懶的辦法是:隻需将拷貝構造函數和指派函數聲明為私有函數,不用編寫代碼。

  例如:

  class A

  { …

  private:

  A(const A &a); // 私有的拷貝構造函數

  A & operate =(const A &a); // 私有的指派函數

  };

  如果有人試圖編寫如下程式:

  A b(a); // 調用了私有的拷貝構造函數

  b = a; // 調用了私有的指派函數編譯器将指出錯誤,因為外界不可以操作A 的私有函數。

一、拷貝構造

       是一個的對象來初始化一邊記憶體區域,這邊記憶體區域就是你的新對象的記憶體區域指派運算,對于一個已經被初始化的對象來進行operator=操作。

        class   A;     

        A  a;  

        A  b=a;   //拷貝構造函數調用  

       //或  

       A  b(a);   //拷貝構造函數調用  

        ///     

        A  a;  

        A  b;  

        b =a;   //指派運算符調用   

      你隻需要記住,在C++語言裡,  

      String   s2(s1);  

      String   s3   =   s1;  

     隻是文法形式的不同,意義是一樣的,都是定義加初始化,都調用拷貝構造函數。

二、

        一般來說是在資料成員包含指針對象的時候,應付兩種不同的處理需求的 一種是複制指針對象,一種是引用指針對象 copy大多數情況下是複制,=則是引用對象的     

例子:  

     class   A  

    {  

          int   nLen;  

          char   *   pData;  

    }  

    顯然  

    A   a,   b;  

    a=b的時候,對于pData資料存在兩種需求  

    第一種copy  

          a.pData   =   new   char   [nLen];  

          memcpy(a.pData,   b.pData,   nLen);  

   另外一種(引用方式):  

          a.pData   =   b.pData  

  通過對比就可以看到,他們是不同的  

  往往把第一種用copy使用,第二種用=實作

  你隻要記住拷貝構造函數是用于類中指針,對象間的COPY 。

三、和拷貝構造函數的實作不一樣。

           拷貝構造函數首先是一個構造函數,它調用的時候産生一個對象,是通過參數傳進來的那個對象來初始化,産生的對象。  

          operator=();是把一個對象指派給一個原有的對象,是以如果原來的對象中有記憶體配置設定要先把記憶體釋放掉,而且還要檢查一下兩個對象是不是同一個對象,如果是的話就不做任何操作。還要注意的是拷貝構造函數是構造函數,不傳回值   

而指派函數需要傳回一個對象自身的引用,以便指派之後的操作 。 

 你肯定知道這個:   

        int   a,   b;   

        b   =   7;   

        Func(a   =   b);   //   把i指派後傳給函數Func(   int   )   

    同理:   

    CMyClass   obj1,   obj2;   

       obj1.Initialize();       

       Func2(   obj1   =   obj2   );   //如果沒有傳回引用,是不能把值傳給Func2的  

      注:   

   CMyClass&  CMyClass::   operator= (CMyClass   &   other)   

      {   

             if(   this   ==   &other   )   

                      return   *this;   

              //   指派操作...   

              return   *this   

      }