1. 程式人生 > >C++下物件的賦值和複製的具體區別?

C++下物件的賦值和複製的具體區別?

拷貝建構函式,是一種特殊的建構函式,它由編譯器呼叫來完成一些基於同一類的其他物件的構建及初始化。其唯一的引數(物件的引用)是不可變的(const型別)。此函式經常用在函式呼叫時使用者定義型別的值傳遞及返回。拷貝建構函式要呼叫基類的拷貝建構函式和成員函式。如果可以的話,它將用常量方式呼叫,另外,也可以用非常量方式呼叫。

目錄

拷貝建構函式說明
一、拷貝函式
二、值的過載
三、注意事宜
四、拷貝構造與構造區別
展開
編輯本段
拷貝建構函式說明

呼叫拷貝建構函式的情形
  在C++中,下面三種物件需要呼叫拷貝建構函式:
  1) 一個物件以值傳遞的方式傳入函式體;
  2) 一個物件以值傳遞的方式從函式返回;
  3) 一個物件需要通過另外一個物件進行初始化;
  如果在前兩種情況不使用拷貝建構函式的時候,就會導致一個指標指向已經被刪除的記憶體空間。對於第三種情況來說,初始化和賦值的不同含義是建構函式呼叫的原因。事實上,拷貝建構函式是由普通建構函式和賦值操作符共同實現的。描述拷貝建構函式和賦值運算子的異同的參考資料有很多。
  拷貝建構函式不可以改變它所引用的物件,其原因如下:當一個物件以傳遞值的方式傳一個函式的時候,拷貝建構函式自動的被呼叫來生成函式中的物件。如果一個物件是被傳入自己的拷貝建構函式,它的拷貝建構函式將會被呼叫來拷貝這個物件這樣複製才可以傳入它自己的拷貝建構函式,這會導致無限迴圈直至棧溢位(Stack Overflow)。除了當物件傳入函式的時候被隱式呼叫以外,拷貝建構函式在物件被函式返回的時候也同樣的被呼叫。
隱式的拷貝建構函式
  如果在類中沒有顯式的宣告一個拷貝建構函式,那麼,編譯器會自動生成一個來進行物件之間的位拷貝(Bitwise Copy)。這個隱含的拷貝建構函式簡單的關聯了所有的類成員。注意到這個隱式的拷貝建構函式和顯式宣告的拷貝建構函式的不同在於對成員的關聯方式。顯式宣告的拷貝建構函式關聯的只是被例項化的類成員的預設建構函式,除非另外一個建構函式在類初始化或構造列表的時候被呼叫。
  拷貝建構函式使程式更有效率,因為它不用再構造一個物件的時候改變建構函式的引數列表。設計拷貝建構函式是一個良好的風格,即使是編譯系統會自動為你生成預設拷貝建構函式。事實上,預設拷貝建構函式可以應付許多情況。
示例
  以下討論中將用到的例子:
  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);
  //現在需要另一個物件,需要將他初始化稱物件一的狀態
  CExample theObjtwo=theObjone;
... } 語句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。 其完成方式是記憶體拷貝,複製所有成員的值。 完成後,theObjtwo.pBuffer==theObjone.pBuffer。 即它們將指向同樣的地方,指標雖然複製了,但所指向的空間並沒有複製,而是由兩個物件共用了。這樣不符合要求,物件之間不獨立了,併為空間的刪除帶來隱患。所以需要採用必要的手段來避免此類情況。 回顧一下此語句的具體過程:首先建立物件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; } 編輯本段 二、值的過載 下面的程式碼與上例相似 int main(int argc, char* argv[]) { CExample theObjone; theObjone.Init(40); CExample theObjthree; theObjthree.Init(60); //現在需要一個物件賦值操作,被賦值物件的原內容被清除,並用右邊物件的內容填充。 theObjthree=theObjone;
return 0; }
  也用到了"="號,但與"一、"中的例子並不同,"一、"的例子中,"="在物件宣告語句中,表示初始化。更多時候,這種初始化也可用括號表示。
  例如 CExample theObjone(theObjtwo);
  而本例子中,"="表示賦值操作。將物件theObjone的內容複製到物件theObjthree;,這其中涉及到物件theObjthree原有內容的丟棄,新內容的複製。
  但"="的預設操作只是將成員變數的值相應複製。舊的值被自然丟棄。
  由於物件內包含指標,將造成不良後果:指標的值被丟棄了,但指標指向的內容並未釋放。指標的值被複制了,但指標所指內容並未複製。
  因此,包含動態分配成員的類除提供拷貝建構函式外,還應該考慮過載"="賦值操作符號。
  類定義變為:
  class CExample
  {
  ...
  CExample(const CExample&); //拷貝建構函式
  CExample& operator = (const CExample&); //賦值符過載
  ...
  };
  //賦值操作符過載
  CExample & CExample::operator = (const CExample& RightSides)
  {
  if (this == &RightSides) // 如果自己給自己賦值則直接返回
  {
  return *this;
  }
  nSize=RightSides.nSize; //複製常規成員
  char *temp=new char[nSize]; //複製指標指向的內容
  memcpy(temp,RightSides.pBuffer,nSize*sizeof(char));
  delete []pBuffer; //刪除原指標指向內容(將刪除操作放在後面,避免X=X特殊情況下,內容的丟失)
  pBuffer=NULL;
  pBuffer=temp; //建立新指向
  return *this
  }
編輯本段
三、注意事宜

  拷貝建構函式和賦值函式的功能是相同的,為了不造成重複程式碼,拷貝建構函式實現如下:
  CExample::CExample(const CExample& RightSides)
  {
  *this=RightSides; //呼叫過載後的"="
  }
編輯本段
四、拷貝構造與構造區別

  class 類名
  {
  public:
  類名(形參引數)//建構函式
  類名(類名&物件名)//拷貝建構函式
  ,,,,,,,,,,,,,,,,,,,,,
  };
  拷貝建構函式的實現:
  類名::類名(類名&物件名)//拷貝建構函式的實現
  {函式體}
  不完整的例子
  拷貝建構函式:
  Class Point
  {
  Public:
  Point(int xx=0,int yy=m)(X=xx;Y=yy;)
  Point(Point& p);
  Int getX() {return X;}
  Int getY(){ return Y;}
  Private :
  Int X,Y;
  }
  Point::Point(Point& p)
  {
  X=p.X;
  Y=p.Y;
  Cout<<"拷貝建構函式呼叫"<<endl;
  }

相關推薦

C++物件複製具體區別

拷貝建構函式,是一種特殊的建構函式,它由編譯器呼叫來完成一些基於同一類的其他物件的構建及初始化。其唯一的引數(物件的引用)是不可變的(const型別)。此函式經常用在函式呼叫時使用者定義型別的值傳遞及返回。拷貝建構函式要呼叫基類的拷貝建構函式和成員函式。如果可以的話,它將用

C++第三章(類物件)下篇 (動態建立與釋放,物件複製,靜態資料成員,友元)

一,物件的動態建立與釋放 new (程式設計師怎麼會沒有朋友??? 來讓我給你new 一個) 在我學過的c中 我們一般都用 malloc 來動態申請空間。現在我們可以用new 來申請空間 Box *pt; pt = new Box; c++允許在new時進行賦值 Box *

物件複製

物件賦值的一般形式為 物件名1=物件名2 物件的賦值程式碼舉例 #include<iostream> using namespace std; class box { public: box(int=10,int=10,int=10); int v

PHP的類物件clone的區別

1. 類物件的賦值方式 PHP的類的賦值方式,有3種情況,一種是$a = new class()操作,建立類;一種是普通的物件賦值$a = $b; 一種取地址的賦值方式$a = &$b。那麼,這三種賦值,指向的記憶體是否是同一塊呢?好吧,我們一起來做一個實驗。 程式碼如下: &l

python編程之拷貝的區別概述及操作excel數據庫(圖)

may 格式 work val 原子 註意 更遠 當我 就是 python編程之賦值和拷貝的區別概述及操作excel數據庫(圖)一、賦值在Python中,對象的賦值就是簡單的對象引用,這點和C++不同,如下所示:a = [1,2,”hello”,[‘python’, ‘C+

python字符串,is的區別

如何 不同 span 中文 gbk 不能 print python code 1. == 與 is 的區別 賦值 == 比較值是否相等,is 比較,比較的是內存地址。 小數據池的作用是節省內存空間 數字的範圍:-5 到 256 共用一個數據池 字符串範圍:1.不能有特殊字符

C++ primer 13.1:拷貝、析構

拷貝構造函數 阻止 需要 函數 static let default 個數 調用 1.拷貝構造函數 基本形式:Name(const Name&) 對於類類型成員,會調用拷貝構造函數拷貝,對於內置類型成員就直接拷貝。對於數組,合成的拷貝構造函數會逐個拷貝都另一

C# List (一) --引用類型的復制

地址 修改 引用 進行 urn ont pre 詳細 理論 最近項目維護中遇到一個問題,確切的說應該是兩個月前的問題也是因為這裏引起的,可惜當時困於業務不熟悉,也沒有更多時間允許查詢根源,導致再次引發了新的問題!!! 問題場景:基礎數據存於List類型的BOMs中,計算過程

Java中字串直接new一個物件,得到的地址是什麼樣子的

  public class StringDemo2 { public static void main(String[] args) { String s1 = "hello"; String s2 = new String("hello"); String s3 = "hello"

String 直接new 物件

用一個簡單的例子表述Stirng直接賦值和new物件的區別 String a = new String(“a”); String b = new String(“a”); //此處是比較的是物件引用的地址 System.out.println(“比較記憶體地址是

java中new出來一個物件定義一個物件為空有什麼不同

new 一個物件出來,比如SomeClass sc=new SomeClass();這個時候已經為sc這個物件分配了指向 new SomeClass() 所建立的記憶體空間。即對這個物件sc進行了例項化。而SomeClass sc=null,則sc物件未進行例項化,是一個空的物件,未能指向任何記憶體空間。

Effective C++ 條款12 物件時勿忘其每一個成分

請記住: 賦值函式應該確保複製物件內的所有成員變數以及所有基類成分; 例如: #include<iostream> using namespace std; class Base { public: Base(){} Base(int x,int y):m

ES6 物件解構陣列結構

1、解構物件     1.1、解構單層物件         1.1.1、解構單層物件——不賦值 let data = { id: 1, name: 'Jack' } let { id, name } = data

c# 遍歷物件屬性給物件

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls

python中的物件(等號、深複製、淺複製)

程式碼: import copy class Obj(): def __init__(self,arg): self.x=arg if __name__ == '__main__': obj1=Obj(1) obj2=Obj(2)

c++建構函式成員初始化中初始化列表兩種方式的區別

先總結下: 由於類成員初始化總在建構函式執行之前 1)從必要性:      a. 成員是類或結構,且建構函式帶引數:成員初始化時無法呼叫預設(無參)建構函式      b. 成員是常量或引用:成員無法賦值,只能被初始化 2)從效率上:     如果在類建構函式裡賦值:在成員

js陣列及物件引用

   簡要歸納:    1、普通的賦值是複製棧區內容。    2、基本型別的資料在棧區存放資料自身,var a=b; //a與b無關。         引用型別資料在棧區存放資料地址。  var a=b; //a,b聯動    3、基本資料型別包括:undefined,bo

C# 反射給物件遇到的問題——型別轉換

建立物件例項的兩種方法:  1. 1 var obj = Assembly.Load("AssemblyName").CreateInstance("AssemblyName"+"ClassFullName"); 2. 1 var obj = Activator.Creat

C++中的宣告、定義、初始化

定義:生成資料型別的一個例項。定義某型別的變數或物件時,系統會為該變數或物件分配記憶體。 賦值:讓已經定義好的變數或物件具有某個數值。 初始化:在定義的同時進行賦值。初始化有兩種形式:=和(),前者叫做複製初始化,複製初始化呼叫建構函式建立一個臨時物件,然後將該臨時物件作為

C++物件C#物件----C++與C#傳遞與引用傳遞淺析

在C#中,看下面一段程式: class A     {         public int value;         public A(int x)         {             value = x;         }         public