c++本質:類的賦值運算子=的過載,以及深拷貝和淺拷貝
在物件導向程式設計中,物件間的相互拷貝和賦值是經常進行的操作。 如果物件在申明的同時馬上進行的初始化操作,則稱之為拷貝運算。例如:
class1 a("af"); class1 b=a;
此時其實際呼叫的是b(a)這樣的淺拷貝操作。
如果物件在申明之後,在進行的賦值運算,我們稱之為賦值運算。例如:
class1 a("af"); class1 b;
b=a;
此時實際呼叫的類的預設賦值函式b.operator=(a);
不管是淺拷貝還是賦值運算,其都有預設的定義。也就是說,即使我們不overload這兩種operation,仍然可以執行。 那麼,我們到底需不需要overload這兩種operation 呢?
答案就是:一般,我們我們
需要手動編寫析構函式的類,都需要overload 拷貝函式和賦值運算子。
下面介紹類的賦值運算子
1.c++中物件的記憶體分配方式
在c++中,物件的例項在編譯的時候,就需要為其分配記憶體大小,因此,系統都是在stack上為其分配記憶體的。這一點和c#完全不同!千 萬記住:在c#中,所有類都是reference type,要建立類的實體,必須通過new在heap上為其分配空間,同時返回在stack上指向其位址的reference.
因此,在c++中,只要申明該例項,在程式編譯後,就要為其分配相應的記憶體空間,至於實體內的各個域的值,就由其建構函式決定了。
例如:
class a
public:
a()
name = new char[1]; //these code is added by cr
*name = '/0';
a(int id,char*t_name)
_id=id;
name=new char[strlen(t_name)+1];
strcpy(name,t_name);
private:
char *name;
int _id;
int main()
a a(1,"herengang");
a b;
在程式編譯之後,a和b在stack上都被分配相應的記憶體大小。只不過物件a的域都被初始化,而b則都為隨機值。
其記憶體分配如下:
2. 預設情況下的賦值運算子
如果我們執行以下: b=a; 則其執行的是預設定義的預設的賦值運算。所謂預設的賦值運算,是指物件中的所有位於stack中的域,進行相應的複製。但是,如果物件有位於heap
上的域的話,其不會為拷貝物件分配heap上的空間,而只是指向相同的heap。
執行b=a這樣的預設的賦值運算後,其記憶體分配如下:
因此,對於預設的賦值運算,如果物件域內沒有heap上的空間,其不會產生任何問題。但是,heap,那麼在析構物件的時候,就會連續兩次釋放heap上的同一塊記憶體區域,從而導致異常。
~a()
delete name;
3.解決辦法--過載(overload)賦值運算子
因此,對於物件的域在heap上分配記憶體的情況,,我們必須過載賦值運算子。當物件
間進行拷貝的時候,我們必須讓不同物件的成員域指向其不同的heap位址--如果成員域屬於heap。
因此,過載賦值運算子後的**如下:
class
a public:
a()
a(int id,char *t_name)
_id=id;
name=new char[strlen(t_name)+1];
strcpy(name,t_name);
a&operator =(a& a) //注意:此處一定要返回物件的引用,否則返回後其值立即消失!
if(name!=null)
delete name;
this->_id=a._id;
int len=strlen(a.name);
name=new char[len+1];
strcpy(name,a.name);
return *this;
~a()
cout<
int _id;
char *name;
int main()
a a(1,"herengang");
a b;
b=a;
其記憶體分配如下:
這樣,在物件a,b退出相應的作用域,其呼叫相應的析構函式,然後釋放分別屬於不同heap
空間的記憶體,程式正常結束。
references:
類的深拷貝函式的過載
public class a
public:
a(a &a);//過載拷貝函式
a& operator=(a &b);//過載賦值函式
//或者 我們也可以這樣過載賦值運算子 void operator=(a &a);即不返回任何值。如果這
//樣的話,他將不支援客戶代買中的鏈式賦值 ,例如a=b=c will be prohibited!
private:
int _id;
char *username;
a::a(a &a)
_id=a._id;
username=new char[strlen(a.username)+1];
if(username!=null)
strcpy(username,a.username);
a& a::operator=(a &a)
if(this==&a)// 問:什麼需要判斷這個條件?(不是必須,只是優化而已)。答案
return *this;
if(username!=null)
delete username;
_id=a._id;
username=new char[strlen(a.username)+1];
if(username!=null)
strcpy(username,a.username);
return *this;
//另外一種寫法:
//void a::operator=(a &a)
// if(username!=null)
// delete username;
// _id=a._id;
// username=new char[strlen(a.username)+1];
// if(username!=null)
// strcpy(username,a.username);
其實,從上可以看出,賦值運算子和拷貝函式很相似。不過賦值函式最好有返回值(進行鏈式賦值),返回也最好是物件的引用(為什麼不是物件本身呢?note2有講解), 而拷貝函式不需要返回任何。同時,賦值函式首先要釋放掉物件自身的堆空間(如果需要的話),然後進行其他的operation.而拷貝函式不需要如此,因為物件此時還沒有分配堆空間。
運算子過載 類的賦值運算子過載
下面介紹類的賦值運算子 1.c 中物件的記憶體分配方式 在c 中,物件的例項在編譯的時候,就需要為其分配記憶體大小,因此,系統都是在stack上為其分配記憶體的。這一點和c 完全不同!千 萬記住 在c 中,所有類都是reference type,要建立類的實體,必須通過new在heap上為其分配空間...
運算子過載 賦值運算子的過載
有時候希望賦值運算子兩邊的型別可以不匹配,比如,把乙個int型別變數賦值給乙個complex物件,或把乙個 char 型別的字串賦值給乙個字串物件,此時就需要過載賦值運算子 注意 賦值運算子 只能過載為成員函式 賦值運算子過載例項示例 include include using namespace ...
C 運算子過載賦值運算子
自定義類的賦值運算子過載函式的作用與內建賦值運算子的作用類似,但是要要注意的是,它與拷貝建構函式與析構函式一樣,要注意深拷貝淺拷貝的問題,在沒有深拷貝淺拷貝的情況下,如果沒有指定預設的賦值運算子過載函式,那麼系統將會自動提供乙個賦值運算子過載函式。賦值運算子過載函式的定義與其它運算子過載函式的定義是...