小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

我所理解的拷貝構(gòu)造函數(shù)和賦值函數(shù) -- 業(yè)余空間 -- 編程愛好者博客

 曾經(jīng)艱難走過 2010-04-05

3.1 拷貝構(gòu)造函數(shù)概述
現(xiàn)在我們來學習一種特殊的構(gòu)造函數(shù)——拷貝構(gòu)造函數(shù)。
對于普通類型的對象來說,他們之間的復制是很簡單的,例如:
int a = 10;
int b =a;
自己定義的類的對象同樣是對象,誰也不能阻止我們用以下的方式進行復制,例如:
#include <iostream>
using namespace std; 
 
class Test 
{  
    int p;
public: 
    Test(int temp) 
    { 
        p = temp; 
    } 
    void Show()
 { 
     cout << "p = " << p << endl;
 } 
}; 

int main() 

    Test a(99); 
 Test b = a;   
 
 a.Show();
 b.Show();
   
    getchar();
 return 0;
}
程序?qū)⒄_運行并輸出:
p = 99
p = 99
普通對象和類對象同為對象,他們之間的特性有相似之處也有不同之處,類對象內(nèi)部存在成員變量,而普通對象是沒有的,當同樣的復制方法發(fā)生在不同的對象上的時候,那么系統(tǒng)對他們進行的操作也是不一樣的,就類對象而言,相同類型的類對象是通過拷貝構(gòu)造函數(shù)來完成整個復制過程的,在上面的代碼中,我們并沒有看到拷貝構(gòu)造函數(shù),同樣完成了復制工作,這又是為什么呢?因為當一個類沒有自定義的拷貝構(gòu)造函數(shù)的時候系統(tǒng)會自動提供一個默認的拷貝構(gòu)造函數(shù),來完成復制工作。
例如:
#include <iostream>
using namespace std; 
 
class Test 
{  
    int p1;
    int p2;
   
public: 
    Test(int i, int j) 
    { 
  cout << "class Test:\n";
        p1 = i; 
        p2 = j; 
    } 
    void Show()
 { 
     cout << "p1 = " << p1 << endl;
     cout << "p2 = " << p2 << endl;
 } 
}; 

int main() 

    Test a(99, 100); 
 Test b = a;   
 
 a.Show();   
 b.Show();
   
    getchar();
 return 0;
}
程序?qū)⒄_運行并輸出:
class Test:
p1 = 99
p2= 100
p1 = 99
p2= 100
(請注意構(gòu)造函數(shù)只調(diào)用了1次)
下面,我們?yōu)榱苏f明情況,就普通情況而言(以上面的代碼為例),我們來自己定義一個與系統(tǒng)默認拷貝構(gòu)造函數(shù)一樣的拷貝構(gòu)造函數(shù),看看它的內(nèi)部是如何工作的!
#include <iostream>
using namespace std; 
 
class Test 
{  
    int p1;
    int p2;
   
public: 
    Test(int i, int j) 
    { 
  cout << "class Test:\n";
        p1 = i; 
        p2 = j; 
    } 
    Test(const Test &cT) //這里就是自定義的拷貝構(gòu)造函數(shù) 
    { 
        cout << "進入拷貝構(gòu)造函數(shù)" << endl; 
        p1 = cT.p1; //這句如果去掉就不能完成復制工作了,它是復制過程的核心語句
  p2 = cT.p2; //這句如果去掉就不能完成復制工作了,它是復制過程的核心語句 
    } 
    void Show()
 { 
     cout << "p1 = " << p1 << endl;
     cout << "p2 = " << p2 << endl;
 } 
}; 

int main() 

    Test a(99, 100); 
 a.Show();
 
 Test b = a;      
 b.Show();
   
    getchar();
 return 0;
}
程序?qū)⒄_運行并輸出:
class Test:
p1 = 99
p2= 100
進入拷貝構(gòu)造函數(shù)
p1 = 99
p2= 100
上面代碼中的Test(const Test &cT) 就是我們自定義的拷貝構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)的名稱必須與類名稱一致,函數(shù)的形式參數(shù)是本類型的一個引用變量,且必須是引用。
當用一個已經(jīng)初始化過了的自定義類類型對象去初始化另一個新構(gòu)造的對象的時候,拷貝構(gòu)造函數(shù)就會被自動調(diào)用。如果你沒有自定義拷貝構(gòu)造函數(shù)的時候系統(tǒng)將會提供給一個默認的拷貝構(gòu)造函數(shù)來完成這個過程,上面代碼的復制核心語句就是通過Test(const Test &cT) 拷貝構(gòu)造函數(shù)內(nèi)的 p1 = cT.p1; 和 p2 = cT.p2; 語句完成的。如果取掉這兩句代碼,那么b對象的p1,p2屬性將得到一個未知的隨機值。

3.2 淺拷貝和深拷貝
下面我們來討論一下關于淺拷貝和深拷貝的問題。
  就上面的代碼情況而言,很多人會問到,既然系統(tǒng)會自動提供一個默認的拷貝構(gòu)造函數(shù)來處理復制,那么我們沒有必要去自定義拷貝構(gòu)造函數(shù)呀,對,就普通情況而言這的確是沒有必要的,但在某些狀況下,類體內(nèi)的成員是需要開辟動態(tài)堆內(nèi)存的,如果我們不自定義拷貝構(gòu)造函數(shù)而讓系統(tǒng)自己處理,那么就會導致堆內(nèi)存的所屬權產(chǎn)生混亂。試想一下,已經(jīng)開辟的一端堆地址原來是屬于對象a的,由于復制過程發(fā)生,b對象取得是a已經(jīng)開辟的堆地址,一旦程序產(chǎn)生析構(gòu),釋放堆的時候,計算機不清楚這段地址是真正屬于誰的,當連續(xù)發(fā)生兩次析構(gòu)的時候就出現(xiàn)了運行錯誤。
  為了更詳細的說明問題,請看如下的代碼。
#include <iostream>
using namespace std;
 
class Internet 

 char name[20];  
    char *cname;
public: 
    Internet(char *name) 
    { 
        cout << "載入構(gòu)造函數(shù):" << endl; 
        strcpy(Internet::name, name); 
  
        cname = new char[strlen(name)+1]; 
        if (cname != NULL) 
        { 
            strcpy(Internet::cname, name); 
        } 
    } 
   
    ~Internet() 
    { 
        cout << "載入析構(gòu)函數(shù):"; 
        delete[] cname; 
        cin.get(); 
    } 
    void Show()
 { 
     printf("name的地址: %x ;name的字符串:  %s\n", name, name);//顯示name的地址和字符串
  printf("cname的地址: %x ;cname的字符串:  %s\n", cname, cname);//顯示cname的地址和字符串
 } 
 void SetMember(char *name)
 {
  cout << "修改成員:" << endl; 
        strcpy(Internet::cname, name);   
 }   
}; 

int main() 

    Internet a("中國"); 
    Internet b = a;
 
 a.Show();   
    b.Show();
   
    a.SetMember("地址");
    a.Show();   
    b.Show();
     
    getchar();
 return 0;
}
程序輸出:
載入構(gòu)造函數(shù):
name的地址:  23ff40;name的字符串:  中國
cname的地址:  33778;cname的字符串:  中國
name的地址:  23ff40;name的字符串:  中國
cname的地址:  33778;cname的字符串:  中國
修改成員:
name的地址:  23ff40;name的字符串:  中國
cname的地址:  33778;cname的字符串:  地址
name的地址:  23ff40;name的字符串:  中國
cname的地址:  33778;cname的字符串:  地址
載入析構(gòu)函數(shù):
載入析構(gòu)函數(shù):

程序員希望用對象a復制出另一個具有相同結(jié)構(gòu)和內(nèi)容的對象b,特別希望所有b中的成員都要獨占內(nèi)存空間,以便修改b內(nèi)的成員時不要影響到a的成員。
但是在執(zhí)行Internet b = a;的結(jié)果證明,系統(tǒng)在執(zhí)行默認的拷貝構(gòu)造函數(shù)后,獲得了對象a所需的靜態(tài)內(nèi)存資源,并將全部處在對象a內(nèi)的成員數(shù)據(jù)按位拷貝到對象b中。因此對象a和b的指針成員cname的值都是相同的(即都指向了相同的地址),這也就必然導致在執(zhí)行程序中a.Show(); 和b.Show(); 語句時所顯示的值是相同的;執(zhí)行語句a.SetMember("地址");后,不但修改了a成員的值,也修改了b成員的值。
 這不僅沒有達到設計要求,而且當程序結(jié)束分別自動執(zhí)行a和b的析構(gòu)函數(shù)時,將以 delete[] cname; 語句對同一地址的對象釋放兩次。而很有可能在釋放首次后,該地址又為另外的進程所占用,所以第二次釋放該地址時會造成不可預料的結(jié)果,屬于不安全的安排,甚至可能危及整個系統(tǒng)的安全。(引自〈〈c++STL程序開發(fā)指南〉〉)
那么將如何解決這個問題呢?我們可以創(chuàng)建一個拷貝構(gòu)造函數(shù)來解決。
代碼如下:
#include <iostream>
using namespace std;
 
class Internet 

 char name[20];  
    char *cname;
public: 
    Internet(char *name) 
    { 
        cout << "載入構(gòu)造函數(shù):" << endl; 
        strcpy(Internet::name, name); 
  
        cname = new char[strlen(name)+1]; 
        if (cname != NULL) 
        { 
            strcpy(Internet::cname, name); 
        } 
    } 
    Internet(const Internet &temp) 
    { 
        cout << "載入拷貝構(gòu)造函數(shù)" << endl; 
        strcpy(Internet::name, temp.name); 
       
        cname = new char[strlen(name)+1];//這里注意,深拷貝的體現(xiàn)! 
       
        if (cname != NULL) 
        { 
            strcpy(cname, name); 
        } 
    } 
    ~Internet() 
    { 
        cout << "載入析構(gòu)函數(shù):"; 
        delete[] cname; 
        cin.get(); 
    } 
    void Show()
 { 
     printf("name的地址: %x ;name的字符串:  %s\n", name, name);//顯示name的地址和字符串
  printf("cname的地址: %x ;cname的字符串:  %s\n", cname, cname);//顯示cname的地址和字符串
 } 
 void SetMember(char *name)
 {
  cout << "修改成員:" << endl; 
        strcpy(Internet::cname, name);   
 }   
}; 

int main() 

    Internet a("中國"); 
    Internet b = a;
 
 a.Show();   
    b.Show();
   
    a.SetMember("地址");
    a.Show();   
    b.Show();
     
    getchar();
 return 0;
}
程序輸出:
載入構(gòu)造函數(shù):
載入拷貝構(gòu)造函數(shù)
name的地址:  23ff40;name的字符串:  中國
cname的地址:  33778;cname的字符串:  中國
name的地址:  23ff20;name的字符串:  中國
cname的地址:  3377e0;cname的字符串:  中國
修改成員:
name的地址:  23ff40;name的字符串:  中國
cname的地址:  33778;cname的字符串:  地址
name的地址:  23ff20;name的字符串:  中國
cname的地址:  3377e0;cname的字符串:  中國
載入析構(gòu)函數(shù):
載入析構(gòu)函數(shù):
上面代碼就演示了深拷貝的問題,對對象b的cname屬性采取了新開辟內(nèi)存的方式避免了內(nèi)存歸屬不清所導致析構(gòu)釋放空間時候的錯誤。最后我必須提一下,對于上面的程序我的解釋并不多,就是希望讀者本身運行程序觀察變化,進而深刻理解。
  深拷貝和淺拷貝的定義可以簡單理解成:如果一個類擁有資源(堆,或者是其它系統(tǒng)資源),當這個類的對象發(fā)生復制過程的時候(復制指針所指向的值),這個過程就可以叫做深拷貝,反之對象存在資源但復制過程并未復制資源(只復制了指針所指的地址)的情況視為淺拷貝。
  淺拷貝資源后在釋放資源的時候會產(chǎn)生資源歸屬不清的情況導致程序運行出錯,這點尤其需要注意!    
原則上,應該為所有包含動態(tài)分配成員的類都提供拷貝構(gòu)造函數(shù)。

3.2 拷貝構(gòu)造函數(shù)的另一種調(diào)用
當對象直接作為參數(shù)傳給函數(shù)時,函數(shù)將建立對象的臨時拷貝,這個拷貝過程也將調(diào)用拷貝構(gòu)造函數(shù)。
例如:
#include <iostream>
using namespace std;

class Date{
 int n;
public:
 Date(int i = 0) 
 { 
  cout << "載入構(gòu)造函數(shù)" << endl;   
     n = i;
 }
 Date(const Date &d) 
 { 
  cout << "載入拷貝構(gòu)造函數(shù)" << endl; 
     n = d.n;
 }
 int GetMember()
 {
  return n;
 }
};

void Display(Date obj) //針對obj的操作實際上是針對復制后的臨時拷貝進行的
{
 cout << obj.GetMember() << endl; 
}

int main()            
{
 Date a;  
 Date b(99);  
 
 Display(a);  //對象直接作為參數(shù)
 Display(b);  //對象直接作為參數(shù)
 
 getchar();
 return 0;
}
程序輸出:
載入構(gòu)造函數(shù):
載入構(gòu)造函數(shù):
載入拷貝構(gòu)造函數(shù)
0載入拷貝構(gòu)造函數(shù)
99
還有一種情況,也是與臨時對象有關的。
當函數(shù)中的局部對象被用作返回值,返回給函數(shù)調(diào)用時,也將建立此局部對象的一個臨時拷貝,此時拷貝構(gòu)造函數(shù)也將被調(diào)用?!墒墙?jīng)測試發(fā)現(xiàn)情況有異。
代碼如下:
#include <iostream>
using namespace std;

class Date{
 int n;
public:
 Date(int i = 0) 
 { 
  cout << "載入構(gòu)造函數(shù)" << endl;   
     n = i;
 }
 Date(const Date &d) 
 { 
  cout << "載入拷貝構(gòu)造函數(shù)" << endl; 
     n = d.n;
 }
 void Show()
 {
  cout << "n = " << n << endl;
 }
};

Date GetClass(void)  //函數(shù)中的局部對象被用作返回值,按理說應該引用拷貝構(gòu)造函數(shù)
{
 Date temp(100);
  
 return temp;
}

int main()            
{
 Date a;
 a.Show();
 
 a = GetClass();//這里GetClass()函數(shù)中的局部對象被用作返回值
 a.Show();
 
 Date b = GetClass();//這里GetClass()函數(shù)中的局部對象被用作返回值
 b.Show();
 
 getchar();
 return 0;
}
程序輸出:
載入構(gòu)造函數(shù):
n = 0
載入構(gòu)造函數(shù):
n = 100
載入構(gòu)造函數(shù):
n = 100
按理第2個和第3個應該輸出'載入拷貝構(gòu)造函數(shù)"才對,這個結(jié)果與預想的不一樣,到底是哪里出問題了呢?
注:后來有論壇上的朋友告訴我說這是因為編譯器的不同而導致不同的輸出。
有人得到這樣的輸出結(jié)果:
載入構(gòu)造函數(shù)
n = 0
載入構(gòu)造函數(shù)
載入拷貝構(gòu)造函數(shù)
n = 100
載入構(gòu)造函數(shù)
載入拷貝構(gòu)造函數(shù)
n = 100
還有人得到這樣的輸出結(jié)果:
載入構(gòu)造函數(shù)
n = 0
載入構(gòu)造函數(shù)
載入拷貝構(gòu)造函數(shù)
n = 100
載入構(gòu)造函數(shù)
載入拷貝構(gòu)造函數(shù)
載入拷貝構(gòu)造函數(shù)
n = 100
(用的是vc++)

3.3 無名對象
現(xiàn)在我們來說一下無名對象。什么是無名對象?利用無名對象初始化對象系統(tǒng)不會調(diào)用拷貝構(gòu)造函數(shù)?這是我們需要回答的兩個問題。  
首先我們來回答第一個問題。很簡單,如果在程序的main函數(shù)中有:
  Internet ("中國");  //Internet表示一個類
這樣的一句語句就會產(chǎn)生一個無名對象。
無名對象會調(diào)用構(gòu)造函數(shù),但利用無名對象初始化對象時系統(tǒng)不會調(diào)用拷貝構(gòu)造函數(shù)!
下面的代碼是常見的利用無名對象初始化對象的例子。
#include <iostream>
using namespace std;
 
class Internet 

 char name[20];  
    char *cname;
public: 
    Internet(char *name) 
    { 
        cout << "載入構(gòu)造函數(shù):" << endl; 
        strcpy(Internet::name, name); 
  
        cname = new char[strlen(name)+1]; 
        if (cname != NULL) 
        { 
            strcpy(Internet::cname, name); 
        } 
    } 
    Internet(const Internet &temp) 
    { 
        cout << "載入拷貝構(gòu)造函數(shù)" << endl; 
        strcpy(Internet::name, temp.name); 
       
        cname = new char[strlen(name)+1];//這里注意,深拷貝的體現(xiàn)! 
       
        if (cname != NULL) 
        { 
            strcpy(cname, name); 
        } 
    } 
    ~Internet() 
    { 
        cout << "載入析構(gòu)函數(shù):"; 
        delete[] cname; 
        cin.get(); 
    } 
    void Show()
 { 
     printf("name的地址: %x ;name的字符串:  %s\n", name, name);//顯示name的地址和字符串
  printf("cname的地址: %x ;cname的字符串:  %s\n", cname, cname);//顯示cname的地址和字符串
 } 
 
}; 

int main() 

    Internet a = Internet("中國"); 
   
 a.Show();   
     
    getchar();
 return 0;
}
程序輸出:
載入構(gòu)造函數(shù):
name的地址:  23ff40;name的字符串:  中國
cname的地址:  33778;cname的字符串:  中國
載入析構(gòu)函數(shù):

上面代碼的運行結(jié)果有點“出人意料”,從思維邏輯上說,當無名對象創(chuàng)建了后,是應該調(diào)用自定義拷貝構(gòu)造函數(shù),或者是默認拷貝構(gòu)造函數(shù)來完成復制過程的,但事實上系統(tǒng)并沒有這么做,因為無名對象使用過后在整個程序中就失去了作用。對于這種情況c++會把代碼看成是: Internet a ("中國");  省略了創(chuàng)建無名對象這一過程,所以說不會調(diào)用拷貝構(gòu)造函數(shù)。

4.1賦值符的重載
 由于并非所有的對象都會使用拷貝構(gòu)造函數(shù)和賦值函數(shù),程序員可能對這兩個函數(shù)有些輕視。請先記住以下的警告,在閱讀正文時就會多心:
本章開頭講過,如果不主動編寫拷貝構(gòu)造函數(shù)和賦值函數(shù),編譯器將以“位拷貝”的方式自動生成缺省的函數(shù)。倘若類中含有指針變量,那么這兩個缺省的函數(shù)就隱含了錯誤。以類String的兩個對象a,b為例,假設a.m_data的內(nèi)容為“hello”,b.m_data的內(nèi)容為“world”。
 現(xiàn)將a賦給b,缺省賦值函數(shù)的“位拷貝”意味著執(zhí)行b.m_data = a.m_data。這將造成三個錯誤:一是b.m_data原有的內(nèi)存沒被釋放,造成內(nèi)存泄露;二是b.m_data和a.m_data指向同一塊內(nèi)存,a或b任何一方變動都會影響另一方;三是在對象被析構(gòu)時,m_data被釋放了兩次。
拷貝構(gòu)造函數(shù)和賦值函數(shù)非常容易混淆,常導致錯寫、錯用??截悩?gòu)造函數(shù)是在對象被創(chuàng)建時調(diào)用的,而賦值函數(shù)只能被已經(jīng)存在了的對象調(diào)用。以下程序中,第三個語句和第四個語句很相似,你分得清楚哪個調(diào)用了拷貝構(gòu)造函數(shù),哪個調(diào)用了賦值函數(shù)嗎?
String  a(“hello”);
String  b(“world”);
String  c = a; // 調(diào)用了拷貝構(gòu)造函數(shù),最好寫成 c(a);
 c = b;  // 調(diào)用了賦值函數(shù)
本例中第三個語句的風格較差,宜改寫成String c(a) 以區(qū)別于第四個語句。
請看下面的代碼:
#include <iostream>
using namespace std;

class Date{
 int n;
public:
 Date(int i = 0) 
 { 
  cout << "載入構(gòu)造函數(shù)" << endl;   
     n = i;
 }
 Date(const Date &d) 
 { 
  cout << "載入拷貝構(gòu)造函數(shù)" << endl; 
     n = d.n;
 }
 void Show()
 {
  cout << "n = " << n << endl;
 }
};

int main()            
{
 Date a(100);
 a.Show();
 
 Date b = a;  //"="在對象聲明語句中,表示初始化,調(diào)用拷貝構(gòu)造函數(shù)
 b.Show();
 
 Date c;
 c.Show(); 
 
 c = a;  //"="在賦值語句中,表示賦值操作,調(diào)用賦值函數(shù)
 c.Show();
 
 getchar();
 return 0;
}
程序輸出:
載入構(gòu)造函數(shù):
n = 100
載入拷貝構(gòu)造函數(shù)
n = 100
載入構(gòu)造函數(shù):
n = 0
n = 100

在程序中語句Date b = a; 和c = a; 都用到了"="號,但兩者的意義不同。在對象聲明語句Date b = a;中,"="表示初始化,調(diào)用拷貝構(gòu)造函數(shù)。而在賦值語句c = a;中,表示賦值操作,調(diào)用賦值函數(shù)。將對象a的內(nèi)容復制到對象b,這其中涉及到對象b原有內(nèi)容的丟棄,新內(nèi)容的復制。
 C++編譯器將自動產(chǎn)生默認的賦值函數(shù)A & operate =(const A &a);但"="的默認操作只是將成員變量的值相應復制。舊的值被自然丟棄。
由于對象內(nèi)包含指針,將造成不良后果:指針的值被丟棄了,但指針指向的內(nèi)容并未釋放;指針的值被復制了,但指針所指內(nèi)容并未復制——這與系統(tǒng)默認拷貝構(gòu)造函數(shù)相似。
因此,包含動態(tài)分配成員的類除提供拷貝構(gòu)造函數(shù)外,還應該考慮重載"="賦值操作符號。
 示例:類String的拷貝構(gòu)造函數(shù)和賦值函數(shù):
// 拷貝構(gòu)造函數(shù)
 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);
}

 // 賦值函數(shù)
 String & String::operator =(const String &other)
 { 
  // (1) 檢查自賦值
  if(this == &other)
   return *this;
  
  // (2) 釋放原有的內(nèi)存資源
  delete [] m_data;
  
  // (3)分配新的內(nèi)存資源,并復制內(nèi)容
 int length = strlen(other.m_data); 
 m_data = new char[length+1];
  strcpy(m_data, other.m_data);
  
  // (4)返回本對象的引用
  return *this;
 } 
 
 類String拷貝構(gòu)造函數(shù)與普通構(gòu)造函數(shù)的區(qū)別是:在函數(shù)入口處無需與NULL進行比較,這是因為“引用”不可能是NULL,而“指針”可以為NULL。
 類String的賦值函數(shù)比構(gòu)造函數(shù)復雜得多,分四步實現(xiàn):
(1)第一步,檢查自賦值。你可能會認為多此一舉,難道有人會愚蠢到寫出 a = a 這樣的自賦值語句!的確不會。但是間接的自賦值仍有可能出現(xiàn),例如
 
// 內(nèi)容自賦值
b = a;

c = b;

a = c;  // 地址自賦值
b = &a;

a = *b; 
 
 也許有人會說:“即使出現(xiàn)自賦值,我也可以不理睬,大不了花點時間讓對象復制自己而已,反正不會出錯!”
 他真的說錯了??纯吹诙降膁elete,自殺后還能復制自己嗎?所以,如果發(fā)現(xiàn)自賦值,應該馬上終止函數(shù)。注意不要將檢查自賦值的if語句
 if(this == &other)
錯寫成為
 if( *this == other)
(2)第二步,用delete釋放原有的內(nèi)存資源。如果現(xiàn)在不釋放,以后就沒機會了,將造成內(nèi)存泄露。
(3)第三步,分配新的內(nèi)存資源,并復制字符串。注意函數(shù)strlen返回的是有效字符串長度,不包含結(jié)束符‘\0’。函數(shù)strcpy則連‘\0’一起復制。
(4)第四步,返回本對象的引用,目的是為了實現(xiàn)象 a = b = c 這樣的鏈式表達。注意不要將 return *this 錯寫成 return this 。那么能否寫成return other 呢?效果不是一樣嗎?
不可以!因為我們不知道參數(shù)other的生命期。有可能other是個臨時對象,在賦值結(jié)束后它馬上消失,那么return other返回的將是垃圾。(引自〈〈高質(zhì)量c++編程指南〉〉)

4.2 在拷貝構(gòu)造函數(shù)中使用賦值函數(shù)
為了簡化程序,我們通常在拷貝構(gòu)造函數(shù)中使用賦值函數(shù)。
例如:#include <iostream>
using namespace std;

class Date{
 int da, mo, yr;
public:
 Date(int d = 0, int m = 0, int y = 0)
 { 
  cout << "載入構(gòu)造函數(shù)" << endl;   
  da = d;
  mo = m;
  yr = y;
 }
 Date(const Date &other);
 Date & operator =(const Date &other);
 
 void Show()
 {
  cout << mo << "-" << da << "-" << yr << endl; 
 }
};

Date::Date(const Date &other) //拷貝構(gòu)造函數(shù)中使用賦值函數(shù)

 cout << "載入拷貝構(gòu)造函數(shù)" << endl; 
 *this = other;
}

Date & Date::operator =(const Date &other)
{
 cout << "載入賦值函數(shù)" << endl; 
  
 if(this == &other)
  return *this;
   
 da = other.da;
 mo = other.mo;
 yr = other.yr; 
  
 return *this;
}

int main()            
{
 Date a(1, 3, 6);
 a.Show();
 
 Date b = a;
 b.Show();
 
 Date c;
 c.Show();
 
 c = a;
 c.Show();
 
 getchar();
 return 0;
}
程序輸出:
載入構(gòu)造函數(shù):
3-1-6
載入拷貝構(gòu)造函數(shù)
載入賦值函數(shù)
3-1-6
載入構(gòu)造函數(shù):
0-0-0
載入賦值函數(shù)
3-1-6
請注意:程序輸出了兩次“載入賦值函數(shù)”,這是因為我們在拷貝構(gòu)造函數(shù)中使用了賦值函數(shù),這樣使程序變得簡潔。如果把拷貝構(gòu)造函數(shù)改寫為:
Date::Date(const Date &other)

 cout << "載入拷貝構(gòu)造函數(shù)" << endl; 
 da = other.da;
 mo = other.mo;
 yr = other.yr; 
}
則程序?qū)⑤敵觯?br>載入構(gòu)造函數(shù):
3-1-6
載入拷貝構(gòu)造函數(shù)
3-1-6
載入構(gòu)造函數(shù):
0-0-0
載入賦值函數(shù)
3-1-6

4.3 偷懶的辦法處理拷貝構(gòu)造函數(shù)和賦值函數(shù)
 如果我們實在不想編寫拷貝構(gòu)造函數(shù)和賦值函數(shù),又不允許別人使用編譯器生成的缺省函數(shù),怎么辦?
 偷懶的辦法是:只需將拷貝構(gòu)造函數(shù)和賦值函數(shù)聲明為私有函數(shù),不用編寫代碼。
 例如:
 class A
 { …
   private:
  A(const A &a);    // 私有的拷貝構(gòu)造函數(shù)
  A & operator =(const A &a); // 私有的賦值函數(shù)
 };
 
 如果有人試圖編寫如下程序:
 A  b(a); // 調(diào)用了私有的拷貝構(gòu)造函數(shù)
 b = a;  // 調(diào)用了私有的賦值函數(shù)
編譯器將指出錯誤,因為外界不可以操作A的私有函數(shù)。(引自〈〈高質(zhì)量c++編程指南〉〉)


    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多