5.初始化調用的是拷貝初始化構造函數,而賦值運算調用的是賦值操作。雖然都是把一個對象的數據成員拷貝到另一個對象的數據成員上,但二者在實現上確有很大的不同。當初始化操作被調用時,被初始化的對象正在被建立,初始化的主要工作是初始化對象的數據成員。當賦值操作被執行時,被更新的對象已經被建立。它的數據成員已具有特定的值,對對象的更新主要反映在對它的數據成員的更新上。
6.3在函數中使用對象
類作為一種特殊的用戶自定義類型,它的對象也可以像其它類型的變量一樣應用到函數中。既可以把對象作為函數的參數,也可以把對象作為函數的返回值。把對象傳遞給函數的規則也與其它的數據類型相同。下麵我們分別以傳值的方式,傳地址的方式和傳引用的方式來討論這個問題。
一、以傳值方式傳遞對象
在以傳值的方式傳遞對象時,對象在傳遞給函數時生成了一個拷貝,這說明形式參數隻是實際參數的一個拷貝。所以,對形式參數的任何修改不會影響到用作實際參數的對本例中,函數myFun()是以傳值的方式來傳遞對象,其形式參數為X。當執行語句myFun(ob)時,對象ob作為實際參數被傳遞給函數。但此時,編譯器創建了對象ob的一個拷貝,並把這個拷貝傳給形式參數x。當myFun()函數改變x.i的值,即執行語句:x.Set(20)時,x.i的值被賦為20,但是由於x和ob並不是同一個對象,所以ob並沒有被變化,即ob.i的值還是為10。
應該注意,編譯器在根據實際參數ob創建形式參數x時,為x調用拷貝構造函數,實際參數ob被傳遞給拷貝構造函數,這樣就創建了形式參數X。由於拷貝構造函數並不一定是把ob逐位地拷貝給X(比如在類中的數據成員為指針類型時),所以,函數myFun0所獲得的x的內容要取決於拷貝構造函數。
拷貝構造函數的參數不能采用傳值方式,否則,為了傳遞這個值參數,又必須調用本拷貝構造函數。這樣,將形成死循環。
綜上所述,當函數以傳值方式傳遞對象時,有以下結論:
1.以傳值方式傳遞對象時,形式參數是實際參數的一個拷貝。它們是兩個不同的對象,所以改變形式參數的值,並不會影響實際參數的值。這一點,在編寫要求改變實際參數的值的函數時,應注意。
2.編譯器調用拷貝構造函數來創建用作形式參數的對象,並把用作實際參數的對象傳遞給拷貝構造函數。因此,形式參數的內容取決於拷貝構造函數。
3.函數結束返回時,程序會為用作形式參數的對象調用析構函數。
4.拷貝構造函數的參數不能采用傳值方式,而應采用引用方式。否則,會引起拷貝構造函數的循環調用。
二、以傳地址方式傳遞對象
在以傳地址的方式傳遞對象時,形式參數是一個指針類型,它的值是用作實際參數的對象的地址。
本例中,函數myFun()是以傳地址的方式傳遞對象的。形式參數x是一個指向My-class()類型對象的指針。
當執行語句myFun(&ob)時,實際參數是對象ob的地址。這時,編譯器把ob的地址賦給形式參數x,也就是使x指向ob。
由於x是指向Myclass類型對象的指針,所以,x必須通過運算符(->)去訪問它所指向的對象的成員。並且,由於x是指向ob的指針,所以語句x->Set(20)實際調用的就是對象ob的成員函數Set(),即ob.Set()。這樣,對象ob的數據成員被賦為20。
綜上所述,當函數以傳地址方式傳遞對象時,有以下結論:
1.在傳地址方式中,實際參數是對象的地址,形式參數是指向對象的指針。
2.形式參數訪問它指向的對象成員,必須用運算符->。
三、以傳引用方式傳遞對象
在以傳引用方式傳遞對象時,形式參數是一個引用類型,它是對用作實際參數的對象的引用。通過傳遞對象的引用,既可以達到按值傳遞的效果,又能避免調用構造函數的開銷。由於拷貝函數不被調用,所以不用創建一個新的對象。
本例中,函數myFun()是以傳引用的方式傳遞對象,其形式參數x是對Myclass類型對象的引用。
當執行語句myFun(&ob)時,其形式參數x用作實際對象ob的別名,所以,語句x.Set(20)實際調用的就是對象ob的成員函數Set(),也即執行ob.Set(20)。這樣,對象ob的數據成員被賦值為20。
綜上所述,當函數以傳引用的方式傳遞對象時,有以下結論:
1.在傳引用的方式中,形式參數是對實際參數的引用。因此,形式參數和實際參數是同一個對象。這樣,對形式參數的操作會影響到實際參數。
2.有時,為了提髙參數傳遞的效率,而采用以傳引用的方式傳遞對象。如果不希望函數改變實際參數,最好把形式參數用const來修飾。
這樣,編譯器會檢查myFun()函數是否修改了x的值;若是,則給出出錯信息。
四、從函數返回對象
像其它的數據類型一樣,也可以從函數中返回對象。並且,從函數中返回對象也有三種方式:
1.以傳值方式返回對象。例如:
函數myFun()返回一個Myclass類型的對象。當執行函數中的return語句時,編譯器將調用拷貝構造函數去初始化調用函數內部一個隱藏的臨時對象,然後,這個隱藏的臨時對象又被用來給某一對象賦值。也就是說,編譯器執行了與下麵等價的任務:Myclasstemp(ob);obi=temp;
所以,還需要調用一次拷貝構造函數,否則,臨時對象與ob共享同一緩衝區。當函數執行結束時,由於臨時對象的刪除,而使ob的值消失,對obi的賦值將不能達到預期效果。
2.以傳地址方式返回對象。似乎我們可以這樣做:
但是,這些操作顯然是不安全的。因為對象ob的作用域在函數myFun()結束時即中止,這意謂著:函數返回了一個指向不再存在的對象的指針。之所以要介紹此種方式,是因為返回指向動態分配變量的指針是安全的。動態分配的對象在被刪除之前一直是存在的,即使在函數退出後,指向它們的指針也是有效的。
3.以傳引用方式返回對象
從函數中返回引用有時比直接返回對象更加有效。回憶一下0perat0r=函數的例子:
但請注意,在返回非*this變量的引用時必須謹慎。返回引用要遵循的規則與返回指針的規則是類似的,要防止出現返回不存在指針的錯誤。
6.4對象數組
如同其它的數據類型可以用數組將相同類型的數據組合在一起一樣,所有相同類的對.象也可以利用數組結合在一起,這樣的以對象為元素的數組就叫對象數組。
一、對象數組概念
可以用說明其它數據類型數組一樣的形式去說明一個對象數組。