對象作為函數參數時調用拷貝構造函數、引用做函數參數時只是傳遞地址


對象也可以作為函數的參數傳遞給函數,其轉遞方法與傳遞其他類型的數據一樣,可采用值傳遞和地址傳遞兩種方法

值傳遞:是把對象的拷貝而不是本身傳遞給函數,函數中對參數對象的任何修改都不會影響調用該函數的對象本身

地址傳遞:調用該函數的對象與參數對象共用同一個地址,所以,函數對參數對象的任何修改都會影響調用該函數的對象本身。

注意:在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。

     回顧一下此語句的具體過程:首先建立對象theObjtwo,並調用其構造函數,然后成員被復制初始化。
  其完成方式是內存拷貝,復制所有成員的值。完成后,theObjtwo.pBuffer==theObjone.pBuffer。
     即它們將指向同樣的地方,指針雖然復制了,但所指向的空間並沒有復制,而是由兩個對象共用了。這樣不符合要求,對象之間不獨立了,並為空間的刪除帶來隱患。所以需要采用必要的手段來避免此類情況:可以在構造函數中添加操作來解決指針成員的這種問題。

  所以C++語法中除了提供缺省形式的構造函數外,還規范了另一種特殊的構造函數:拷貝構造函數,一種特殊的構造函數重載。上面的語句中,如果類中定義了拷貝構造函數,在對象復制初始化時,調用的將是拷貝構造函數,而不是缺省構造函數。在拷貝構造函數中,可以根據傳入的變量,復制指針所指向的資源。

  拷貝構造函數的格式為:類名(const 類名& 對象名);//拷貝構造函數的原型,參數是常量對象的引用。由於拷貝構造函數的目的是成員復制,不應修改原對象,所以建議使用const關鍵字

  提供了拷貝構造函數后的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;
  }        
 總結:當某對象是按值傳遞(無論是作為函數參數,還是作為函數返回值),編譯器都會先建立一個此對象的臨時拷貝,而在建立該臨時拷貝時就會調用類的拷貝構造函數。
 
拷貝構造函數的實現:
 
  類名::類名(類名&對象名)//拷貝構造函數的實現/定義
 
  {函數體}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM