首先,大家要知道,c++類有以下這些極為重要的函式:
一:複製建構函式。
二:賦值函式。
我們先來講複製建構函式。什麼是複製建構函式呢?比如,我們可以寫下這樣的**:string test1(test2);這是進行初始化。我們知道,初始化物件要用建構函式。可這兒呢?按理說,應該有宣告為這樣的建構函式:string(const string &);可是,我們並沒有定義這個建構函式呀?答案是,c++提供了預設的複製建構函式,問題也就出在這兒。
(1):什麼時候會呼叫複製建構函式呢?(以string類為例。)
在我們提供這樣的**:string test1(test2)時,它會被呼叫;當函式的引數列表為按值傳遞,也就是沒有用引用和指標作為型別時,如:void show_string(const string),它會被呼叫。其實,還有一些情況,但在這兒就不列舉了。
(2):它是什麼樣的函式。
它的作用就是把兩個類進行複製。拿string類為例,c++提供的預設複製建構函式是這樣的:
string(const string& a)
在平時,這樣並不會有任何的問題出現,但我們用了new操作符,涉及到了動態記憶體分配,我們就不得不談談淺複製和深複製了。以上的函式就是實行的淺複製,它只是複製了指標,而並沒有複製指標指向的資料,可謂一點兒用也沒有。打個比方吧!就像乙個朋友讓你把乙個程式通過網路發給他,而你大大咧咧地把快捷方式發給了他,有什麼用處呢?我們來具體談談:
假如,a物件中儲存了這樣的字串:「c++」。它的位址為2000。現在,我們把a物件賦給b物件:string b=a。現在,a和b物件的str指標均指向2000位址。看似可以使用,但如果b物件的析構函式被呼叫時,則位址2000處的字串「c++」已經被從記憶體中抹去,而a物件仍然指向位址2000。這時,如果我們寫下這樣的**:cout<<a<<endl;或是等待程式結束,a物件的析構函式被呼叫時,a物件的資料能否顯示出來呢?只會是亂碼。而且,程式還會這樣做:連續對位址2000處使用兩次delete操作符,這樣的後果是十分嚴重的!
本例中,有這樣的**:
string* string1=new string(test1);
cout<<*string1<<endl;
delete string1;
假設test1中str指向的位址為2000,而string中str指標同樣指向位址2000,我們刪除了2000處的資料,而test1物件呢?已經被破壞了。大家從執行結果上可以看到,我們使用cout<<test1時,一點反應也沒有。而在test1的析構函式被呼叫時,顯示是這樣:「這個字串將被刪除:」。
再看看這段**:
cout<<"使用錯誤的函式:"<<endl;
show_string(test2);
cout<<test2<<endl;//這一段**出現嚴重的錯誤!
show_string函式的引數列表void show_string(const string a)是按值傳遞的,所以,我們相當於執行了這樣的**:string a=test2;函式執行完畢,由於生存週期的緣故,物件a被析構函式刪除,我們馬上就可以看到錯誤的顯示結果了:這個字串將被刪除:?=。當然,test2也被破壞了。解決的辦法很簡單,當然是手工定義乙個複製建構函式嘍!人力可以勝天!
string::string(const string& a)
我們執行的是深複製。這個函式的功能是這樣的:假設物件a中的str指標指向位址2000,內容為「i am a c++ boy!」。我們執行**string b=a時,我們先開闢出一塊記憶體,假設為3000。我們用strcpy函式將位址2000的內容拷貝到位址3000中,再將物件b的str指標指向位址3000。這樣,就互不干擾了。
大家把這個函式加入程式中,問題就解決了大半,但還沒有完全解決,問題在賦值函式上。我們的程式中有這樣的段**:
string string3;
string3=test4;
經過我前面的講解,大家應該也會對這段**進行尋根摸底:憑什麼可以這樣做:string3=test4???原因是,c++為了使用者的方便,提供的這樣的乙個操作符過載函式:operator=。所以,我們可以這樣做。大家應該猜得到,它同樣是執行了淺複製,出了同樣的毛病。比如,執行了這段**後,析構函式開始大展神威^_^。由於這些變數是後進先出的,所以最後的string3變數先被刪除:這個字串將被刪除:第四個範例。很正常。最後,刪除到test4的時候,問題來了:這個字串將被刪除:?=。原因我不用贅述了,只是這個賦值函式怎麼寫,還有一點兒學問呢!大家請看:
平時,我們可以寫這樣的**:
x=y=z
。(均為整型變數。)而在類物件中,我們同樣要這樣,因為這很方便。而物件
a=b=c
就是a.operator=(b.operator=(c))
。而這個
operator=
函式的引數列表應該是:
const string& a
,所以,大家不難推出,要實現這樣的功能,返回值也要是
string&
,這樣才能實現a=
b=c。我們先來寫寫看:
string& string::operator=(const string& a)
是不是這樣就行了呢?我們假如寫出了這種**:a=a,那麼大家看看,豈不是把a物件的資料給刪除了嗎?這樣可謂引發一系列的錯誤。所以,我們還要檢查是否為自身賦值。只比較兩物件的資料是不行了,因為兩個物件的資料很有可能相同。我們應該比較位址。以下是完好的賦值函式:
string& string::operator=(const string& a)
把這些**加入程式,問題就完全解決,下面是執行結果:
下面分別輸入三個範例:
第乙個範例
第二個範例
第三個範例
第乙個範例
這個字串將被刪除:第乙個範例。
第乙個範例
使用正確的函式:
第二個範例。
第二個範例。
使用錯誤的函式:
第二個範例。
這個字串將被刪除:第二個範例。
第二個範例。
string2: 第三個範例。
string3: 第四個範例。
下面,程式結束,析構函式將被呼叫。
這個字串將被刪除:第四個範例。
這個字串將被刪除:第三個範例。
這個字串將被刪除:第四個範例。
這個字串將被刪除:第三個範例。
這個字串將被刪除:第二個範例。
這個字串將被刪除:第乙個範例。
拷貝建構函式和賦值操作符
類有預設的建構函式 拷貝建構函式 析構函式 賦值操作運算子 和取位址運算子 預設的賦值操作運算子和預設的拷貝建構函式類似,對於每個非static成員,都是執行逐個成員賦值 memberwise assignment 同時也是按位賦值 bitwise copy 即,只是簡單地將每個類成員的記憶體中的直...
賦值操作符和拷貝建構函式
最近開發乙個專案,用到了單例模式,標頭檔案大概如下 class crecguard crecguard private void guard void unguard private critical section cs template class csingleton 禁止建構函式 csing...
複製控制 拷貝構造 賦值操作符
呼叫拷貝構造 teacher t2 t1 類型別,複製初始化時呼叫拷貝建構函式,直接初始化呼叫對應建構函式呼叫賦值運算子 teacher t2 t2 t1 單形參 且形參型別為該類型別的引用 常const修飾 的建構函式 class test test int d data d test test ...