對象也可以作為函數的參數傳遞給函數,其轉遞方法與傳遞其他類型的數據一樣,可采用值傳遞和地址傳遞兩種方法。
值傳遞:是把對象的拷貝而不是本身傳遞給函數,函數中對參數對象的任何修改都不會影響調用該函數的對象本身;
地址傳遞:調用該函數的對象與參數對象共用同一個地址,所以,函數對參數對象的任何修改都會影響調用該函數的對象本身。
注意:在C++中,下面三種對象需要調用拷貝構造函數(有時也稱“復制構造函數”)
1) 一個對象作為函數參數,以值傳遞的方式傳入函數體
2) 一個對象作為函數返回值,以值傳遞的方式從函數返回
3) 一個對象用於給另外一個對象進行初始化(常稱為復制初始化)
當用引用變量做參數時,不調用拷貝構造函數,用傳遞引用的方式給函數傳遞一個對象的引用時,只傳遞了該對象的地址,系統消耗較小。在函數體內訪問 形參,實際是訪問了這個作為實參的對象。例如:void function(CTest & test);
Java中的引用傳遞是指: 例如:void function(CTest test),沒有&號
#include<iostream> using namespace std; class Tr{ public: //Tr(int n):i(n) //構造函數最好這么寫 //{} Tr(int n) { i=n; } void set_i(int n) {i=n;} int get_i() {return i;} private: int i; }; void sqr_it1(Tr ob) { ob.set_i(ob.get_i()*ob.get_i()); cout<<"在函數sqr_it1內,形參對象obj的數據成員i="<<ob.get_i(); cout<<endl; } void sqr_it2(Tr *obj) { obj->set_i(obj->get_i()*obj->get_i()); cout<<"在函數sqr_it2內,形參對象obj的數據成員i="<<obj->get_i(); cout<<endl; } int main() { Tr obj(10); cout<<"調用sqr_it前,實參對象obj的數據成員i="<<obj.get_i()<<endl; sqr_it1(obj); cout<<"調用sqr_it1后,實參對象obj的數據成員i="<<obj.get_i()<<endl; sqr_it2(&obj); cout<<"調用sqr_it2后,實參對象obj的數據成員i="<<obj.get_i()<<endl; return 0; }
調用sqr_it前,實參對象obj的數據成員i=10
在函數sqr_it1內,形參對象obj的數據成員i=100
調用sqr_it1后,實參對象obj的數據成員i=10
(此時,由於是以值傳遞,obj的數據成員i沒有變化,還是原先的10)
在函數sqr_it2內,形參對象obj的數據成員i=100
調用sqr_it2后,實參對象obj的數據成員i=100
(此時,由於是以地址傳遞,obj的數據成員i發生變化,為sqr_it2中的值100)
通常的原則是:①對於凡是包含動態分配成員或包含指針成員的類都應該提供拷貝構造函數;②在提供拷貝構造函數的同時,還應該考慮重載"="賦值操作符號。
復制初始化
以下討論中將用到的例子:
class CExample { public: CExample(){pBuffer=NULL; nSize=0;} ~CExample(){delete []pBuffer;} void Init(int n){ pBuffer=new char[n]; nSize=n;} private: char *pBuffer; //類的對象中包含指針,指向動態分配的內存資源 int nSize; };
這個類的主要特點是包含指向其他資源的指針,pBuffer指向堆中動態分配的一段內存空間。
int main(int argc, char* argv[]) { CExample theObjone; theObjone.Init(40); //現在需要另一個對象,並將它初始化為theObjone CExample theObjtwo=theObjone; ... }
語句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
所以C++語法中除了提供缺省形式的構造函數外,還規范了另一種特殊的構造函數:拷貝構造函數,一種特殊的構造函數重載。上面的語句中,如果類中定義了拷貝構造函數,在對象復制初始化時,調用的將是拷貝構造函數,而不是缺省構造函數。在拷貝構造函數中,可以根據傳入的變量,復制指針所指向的資源。
提供了拷貝構造函數后的CExample類定義為:
class CExample { public: CExample(){pBuffer=NULL; nSize=0;} ~CExample(){delete []pBuffer;} CExample(const CExample&); //拷貝構造函數 void Init(int n){ pBuffer=new char[n]; nSize=n;} private: char *pBuffer; //類的對象中包含指針,指向動態分配的內存資源 int nSize; }; CExample::CExample(const CExample& RightSides) //拷貝構造函數的定義 { nSize=RightSides.nSize; //復制常規成員 pBuffer=new char[nSize]; //復制指針指向的內容 memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char)); }
這樣,定義新對象,並用已有對象初始化新對象時,CExample(const CExample& RightSides)將被調用,而已有對象用別名RightSides傳給構造函數,以用來作復制。
對象按值傳遞
下面介紹拷貝構造函數的另一種調用:當對象直接作為參數傳給函數時,函數將建立對象的臨時拷貝,這個拷貝過程也將調用拷貝構造函數。例如:
BOOL testfunc(CExample obj); testfunc(theObjone); //對象直接作為參數。 BOOL testfunc(CExample obj) { //針對obj的操作實際上是針對復制后的臨時拷貝進行的 } 還有一種情況,也是與臨時對象有關:當函數中的局部對象作為返回值被返回給函數調者時,也將建立此局部對象的一個臨時拷貝,拷貝構造函數也將被調用。 CTest func() { CTest theTest; return theTest; }