本章我們將進一步討論對象的其它用法,特別是對象數組和對象指針的用法。

6.1拷貝構造函數

拷貝構造函數是這樣的一種構造函數,它取一個與某一參數具有相同類型的對象。每當用一個對象的值初始化另一個對象時,它就被調用。它的一般形式為:

類名(const類名&參數名);

一、缺省拷貝構造函數

在沒有為類聲明拷貝構造函數時,編譯器就會自動生成一個具有上述形式的公有的缺省拷貝構造函數,它把用作初始值對象的每個數據成員逐位地拷貝給正在創建對象的對應數據成員。請看例子:

程序執行結果為:1,2

在類Location中,我們並沒有為它定義自己的拷貝構造函數。所以,編譯器會自動生成缺省拷貝構造函數。當程序執行語句LocationB(A)時,首先為B分配內存空間,然後再自動調用缺省拷貝構造函數,並把A作為實際參數傳遞給缺省拷貝構造函數。缺省拷貝構造函數把對象A的數據成員的值逐位地拷貝給對象B,這樣就完成了對象B的創建和初始化。

二、自定義拷貝構造函數

但是,在某些情況下,尤其是當類中包含有指針成員時,我們必須為類定義自己的拷貝構造函數。否則,可能會引發某些錯誤。

例6.2類Message用來保存一個字符串,數據成員buffer是字符型指針,指向被保存的字符串。

在上麵的例子中,當執行語句Messageob2(obi);時,程序將會出錯,原因是我們沒有在類中定義自己的拷貝構造函數,所以編譯器就會自動生成一個缺省的拷貝構造函數,把對象obi的數據成員的值逐位地拷貝到對象ob2的數據成員上,這樣,對象obi的數據成員buffer的值也被賦給了對象ob2的數據成員buffer。由於缺省拷貝構造函數隻是進行簡單的位拷貝,所以,obl.buffer和ob2.buffei的值相同。也就是說它們指向同一塊內存空間。這樣,當程序撤銷對象ob2時,執行析構函數,該析構函數將釋放ob2.buffer所指向的內存空間;而當程序撤銷對象obi時,它的析構函數將釋放obi.buffer所指向的內存空間。這樣同一塊內存就被釋放了兩次,程序將出現錯誤。所以,為防止這種情況發生,就應該為類Message定義自己的拷貝構造函數。如下例:

這樣,當執行Messageob2(obi);語句時,編譯器就會調用上麵的拷貝構造函數。該拷貝構造函數將重新為對象ob2.buffer分配內存空間,並把ofcl.buffer所指向內存的內容拷貝到ob2.buffer所指向的內存裏。這樣就不會發生兩個指針指向同一塊內存而引發的錯誤。對拷貝構造函數有下麵的注意事項:

1.如果沒有定義自己的拷貝構造函數,編譯器就會自動生成一個缺省拷貝構造函數。缺省拷貝構造函數把用作初始化的對象逐位地拷貝給正在創建的對象。

2.有些時候,必須定義自己的拷貝構造函數,以免發生錯誤。

6.2對象初始化和對象賦值

一、對象初始化

我們先看一個普通變量初始化的情況:int i=3;

這裏,在說明整形變量的同時,用一個值對它進行了初始化。同樣道理,也可以用相同的方式對類進行初始化。假設有下麵的類定義:

語句LocationA;表明:在說明對象A時,沒有提供任何初始值。編譯器會為對象A自動調用缺省構造函數Location()。

語句LocationB(A);表明:對象B的初始值是一個同類型的對象A的值,所以編譯器自動調用銠省拷貝構造函數,並把對象A怍為實際參數傳遞給缺省拷貝構造函數。這樣就創建了對象Bo

語句LocationC=A;表明:要通過一個同類型的對象A來創建C,它去尋找一個參數為Loc財ion類型的構造函數,即拷貝構造函數.Location(constLocation&),並把對象A作為實際參數傳遞給拷貝構造函數,這樣就創建了對象C。

二、對象賦值

請看下麵的例子:

在這個例子中,整型變量通過賦值運算符“=”被賦予了一個值。當然,我們也可以用同樣方式為一個類的對象賦值,賦值用於更新對象的值。對於類類型的對象,為使它能夠被更新,在該對象的類中應該定義有一個賦值操作。

一個類(假設為類X)的賦值操作的一般形式為:

X&X::operator=(X&X);

注意:在C++中,函數的名字可以是一個標識符,也可以是一個操作符。當使用一個操作符來命名函數或將一個操作符用在函數調用表達式中時,必須與關鍵字operator合用。

如果在類的定義中,沒有為其聲明賦值操作,則編譯器將為類生成一個具有上述形式的公有的賦值操作。其執行結果是進行逐成員賦值,即把一個對象的每個數據成員的值賦給另一個對象的相應的數據成員。

但與拷貝構造函數一樣,當類中聲明有指針數據成員時,編譯器所生成的公有賦值操作的執行語義將引起程序運行的錯誤。請看例子:

當使用對象a更新對象b之後,對象b的數據成員p被更新為a的數據成員P的值。這將導致兩個問題:

1.對象b的數據成員p原先指向的空間(用於存放10)將丟失。

2.當對象a和b被撤銷時,兩次調用析構函數,而使存儲的對象被刪除兩次,從而引起運行錯誤。

它調用成員函數:A&A::operator=(A&r);並把a作為實際參數傳遞給該函數。

其中,this的值是對象b的地址,&r的值是對象a的地址。由於b和a不是同一個對象,所以該函數繼續執行語句;把a.p指向的值賦給b指向的空間。最後,該函數返回對b的引用。

賦值操作最好返回被賦值的對象的引用。這樣,就可以像C++預定義的賦值操作語義那樣,進行下述形式的賦值操作。

三、對象初始化和對象賦值的區別

假設有類My class,那麼,說明語句Myclassob2=obi用來創建對象ob2並用對象obi去初始化它,而語句中的等號(=)並不是賦值運算符,而是進行對象的創建和初始化。它為對象ob2分配內存空間,並調用拷貝構造函數,把對象obi的值作為實際參數傳遞給拷貝構造函數。

而語句ob4=ob3是把對象ob3賦給對象ob4。這裏的等號(=)是賦值運算符。它將調用賦值操作把對象ob3的值賦給對象ob4。

所以我們得出對象初始化和對象賦值的區別:

1.在對象初始化中的等號(=)不是賦值運算符,它隻是告知編譯器,應該用等號右邊的對象來初始化等號左邊的對象。

2.賦值語句中的等號(=)是賦值運算符,執行的是對象賦值運算。

3.初始化一個對象是指初始化一個剛創建的對象的成員,隻能在對象的說明語句中初始化對象。

4.對象賦值是指把一個對象的值賦給另一個已存在的對象,它隻是修改另一個對象的值。