c++ copy和operator =

目錄(?)[+]函數

  1. 構造函數
  2. 拷貝構造函數
  3. 賦值函數
 

C++中通常建立對象,拷貝或賦值的方式有構造函數,拷貝構造函數,賦值函數這三種方法。下面就詳細比較下三者之間的區別以及它們的具體實現
this

1.構造函數

構造函數是一種特殊的類成員函數,是當建立一個類的對象時,它被調用來對類的數據成員進行初始化和分配內存。(構造函數的命名必須和類名徹底相同)spa

首先說一下一個C++的空類,編譯器會加入哪些默認的成員函數.net

·默認構造函數和拷貝構造函數指針

·析構函數code

·賦值函數(賦值運算符)對象

·取值函數blog

**即便程序沒定義任何成員,編譯器也會插入以上的函數!
內存

注意:構造函數能夠被重載,能夠多個,能夠帶參數;字符串

析構函數只有一個,不能被重載,不帶參數

 

而默認構造函數沒有參數,它什麼也不作。當沒有重載無參構造函數時,

  A a就是經過默認構造函數來建立一個對象

下面代碼爲構造函數重載的實現

 

  1. <span style="font-size:14px;">class A  
  2. {  
  3. int m_i;  
  4. Public:  
  5.   A()   
  6. {  
  7.  Cout<<」無參構造函數」<<endl;  
  8. }  
  9. A(int i):m_i(i) {}  //初始化列表  
  10. }</span>  
<span style="font-size:14px;">class A
{
int m_i;
Public:
  A() 
{
 Cout<<」無參構造函數」<<endl;
}
A(int i):m_i(i) {}  //初始化列表
}</span>

2.拷貝構造函數

 

拷貝構造函數是C++獨有的,它是一種特殊的構造函數,用基於同一類的一個對象構造和初始化另外一個對象。

當沒有重載拷貝構造函數時,經過默認拷貝構造函數來建立一個對象

A a;

A b(a);

A b=a;  都是拷貝構造函數來建立對象b

強調:這裏b對象是不存在的,是用a 對象來構造和初始化b的!!

 

先說下何時拷貝構造函數會被調用:

在C++中,3種對象須要複製,此時拷貝構造函數會被調用

1)一個對象以值傳遞的方式傳入函數體

2)一個對象以值傳遞的方式從函數返回

3)一個對象須要經過另外一個對象進行初始化

 

何時編譯器會生成默認的拷貝構造函數:

1)若是用戶沒有自定義拷貝構造函數,而且在代碼中使用到了拷貝構造函數,編譯器就會生成默認的拷貝構造函數。但若是用戶定義了拷貝構造函數,編譯器就不在生成。

2)若是用戶定義了一個構造函數,但不是拷貝構造函數,而此時代碼中又用到了拷貝構造函數,那編譯器也會生成默認的拷貝構造函數。

 

由於系統提供的默認拷貝構造函數工做方式是內存拷貝,也就是淺拷貝。若是對象中用到了須要手動釋放的對象,則會出現問題,這時就要手動重載拷貝構造函數,實現深拷貝。

下面說說深拷貝與淺拷貝:

淺拷貝:若是複製的對象中引用了一個外部內容(例如分配在堆上的數據),那麼在複製這個對象的時候,讓新舊兩個對象指向同一個外部內容,就是淺拷貝。(指針雖然複製了,但所指向的空間內容並無複製,而是由兩個對象共用,兩個對象不獨立,刪除空間存在)

深拷貝:若是在複製這個對象的時候爲新對象製做了外部對象的獨立複製,就是深拷貝。

 

拷貝構造函數重載聲明以下:

A (const A&other)

下面爲拷貝構造函數的實現:

 

  1. <span style="font-size:14px;">class A  
  2. {  
  3.   int m_i  
  4.   A(const A& other):m_i(other.m_i)  
  5. {  
  6.   Cout<<」拷貝構造函數」<<endl;  
  7. }  
  8. }</span>  
<span style="font-size:14px;">class A
{
  int m_i
  A(const A& other):m_i(other.m_i)
{
  Cout<<」拷貝構造函數」<<endl;
}
}</span>

 

 

3.賦值函數

當一個類的對象向該類的另外一個對象賦值時,就會用到該類的賦值函數。

當沒有重載賦值函數(賦值運算符)時,經過默認賦值函數來進行賦值操做

A a;

A b;

b=a; 

強調:這裏a,b對象是已經存在的,是用a 對象來賦值給b的!!

 

賦值運算的重載聲明以下:

 A& operator = (const A& other)

 

一般你們會對拷貝構造函數和賦值函數混淆,這兒仔細比較二者的區別:

1)拷貝構造函數是一個對象初始化一塊內存區域,這塊內存就是新對象的內存區,而賦值函數是對於一個已經被初始化的對象來進行賦值操做。

 

  1. <span style="font-size:14px;">class  A;  
  2. A a;  
  3. A b=a;   //調用拷貝構造函數(b不存在)  
  4. A c(a) ;   //調用拷貝構造函數  
  5.   
  6. /****/  
  7.   
  8. class  A;  
  9. A a;  
  10. A b;     
  11. b = a ;   //調用賦值函數(b存在)</span>  
<span style="font-size:14px;">class  A;
A a;
A b=a;   //調用拷貝構造函數(b不存在)
A c(a) ;   //調用拷貝構造函數

/****/

class  A;
A a;
A b;   
b = a ;   //調用賦值函數(b存在)</span>

 

2)通常來講在數據成員包含指針對象的時候,須要考慮兩種不一樣的處理需求:一種是複製指針對象,另外一種是引用指針對象。拷貝構造函數大多數狀況下是複製,而賦值函數是引用對象

3)實現不同。拷貝構造函數首先是一個構造函數,它調用時候是經過參數的對象初始化產生一個對象。賦值函數則是把一個新的對象賦值給一個原有的對象,因此若是原來的對象中有內存分配要先把內存釋放掉,並且還要檢察一下兩個對象是否是同一個對象,若是是,不作任何操做,直接返回。(這些要點會在下面的String實現代碼中體現)

 

!!!若是不想寫拷貝構造函數和賦值函數,又不容許別人使用編譯器生成的缺省函數,最簡單的辦法是將拷貝構造函數和賦值函數聲明爲私有函數,不用編寫代碼。如:

 

  1. <span style="font-size:14px;">class A  
  2. {  
  3.  private:  
  4.  A(const A& a); //私有拷貝構造函數  
  5.  A& operate=(const A& a); //私有賦值函數  
  6. }</span>  
<span style="font-size:14px;">class A
{
 private:
 A(const A& a); //私有拷貝構造函數
 A& operate=(const A& a); //私有賦值函數
}</span>

若是程序這樣寫就會出錯:

 

  1. <span style="font-size:14px;">A a;  
  2. A b(a); //調用了私有拷貝構造函數,編譯出錯  
  3.   
  4. A b;  
  5. b=a; //調用了私有賦值函數,編譯出錯</span>  
<span style="font-size:14px;">A a;
A b(a); //調用了私有拷貝構造函數,編譯出錯

A b;
b=a; //調用了私有賦值函數,編譯出錯</span>

因此若是類定義中有指針或引用變量或對象,爲了不潛在錯誤,最好重載拷貝構造函數和賦值函數。

 

下面以string類的實現爲例,完整的寫了普通構造函數,拷貝構造函數,賦值函數的實現。String類的基本實現見我另外一篇博文。

 

  1. <span style="font-size:14px;">String::String(const char* str)    //普通構造函數  
  2.   
  3. {  
  4.   
  5.  cout<<construct<<endl;  
  6.   
  7.  if(str==NULL)        //若是str 爲NULL,就存一個空字符串「」  
  8.   
  9. {  
  10.  m_string=new char[1];  
  11.  *m_string ='\0';  
  12. }  
  13.   
  14.  else  
  15.   
  16. {  
  17.   
  18.   m_string= new char[strlen(str)+1] ;   //分配空間  
  19.   strcpy(m_string,str);  
  20.   
  21. }  
  22.   
  23. }  
  24.   
  25.    
  26. String::String(const String&other)   //拷貝構造函數  
  27.   
  28. {  
  29.  cout<<"copy construct"<<endl;  
  30.  m_string=new char[strlen(other.m_string)+1]; //分配空間並拷貝  
  31.  strcpy(m_string,other.m_string);  
  32. }  
  33.   
  34. String & String::operator=(const String& other) //賦值運算符  
  35. {  
  36.  cout<<"operator =funtion"<<endl ;  
  37.  if(this==&other) //若是對象和other是用一個對象,直接返回自己  
  38.  {  
  39.   return *this;  
  40.  }  
  41.  delete []m_string; //先釋放原來的內存  
  42.  m_string= new char[strlen(other.m_string)+1];  
  43.  strcpy(m_string,other.m_string);  
  44.  return * this;  
  45. }</span>  
<span style="font-size:14px;">String::String(const char* str)    //普通構造函數

{

 cout<<construct<<endl;

 if(str==NULL)        //若是str 爲NULL,就存一個空字符串「」

{
 m_string=new char[1];
 *m_string ='\0';
}

 else

{

  m_string= new char[strlen(str)+1] ;   //分配空間
  strcpy(m_string,str);

}

}

 
String::String(const String&other)   //拷貝構造函數

{
 cout<<"copy construct"<<endl;
 m_string=new char[strlen(other.m_string)+1]; //分配空間並拷貝
 strcpy(m_string,other.m_string);
}

String & String::operator=(const String& other) //賦值運算符
{
 cout<<"operator =funtion"<<endl ;
 if(this==&other) //若是對象和other是用一個對象,直接返回自己
 {
  return *this;
 }
 delete []m_string; //先釋放原來的內存
 m_string= new char[strlen(other.m_string)+1];
 strcpy(m_string,other.m_string);
 return * this;
}</span>

 

 

一句話記住三者:對象不存在,且沒用別的對象來初始化,就是調用了構造函數;

                對象不存在,且用別的對象來初始化,就是拷貝構造函數(上面說了三種用它的狀況!)

                 對象存在,用別的對象來給它賦值,就是賦值函數。

以上爲本人結合不少資料和圖書整理出來的,將核心的點都系統的理出來,全本身按條理寫的,如今你們對普通構造函數,拷貝構造函數,賦值函數的區別和實現應該都清楚了。

相關文章
相關標籤/搜索