拷貝建構函式的作用
如果你沒有為你的程式新增一個拷貝建構函式,那麼編譯器也會向你的程式新增一個預設的拷貝建構函式.在一般情況下使用編譯器的預設拷貝建構函式就可以了,但是如果你的類的成員資料有指標變數的存在,那麼你最好自己構造一個拷貝建構函式,否則你的程式可能隱藏著許多錯誤.
請看下面這一個程式,存在多少錯誤?
#include "stdafx.h"
#include "iostream.h"
#include "string.h"
class String
{
int nLen;
char *cArray;
public:
String(){nLen=0; cArray=NULL;}
~String(){delete []cArray; nLen=0;}
String(const char *str)
{
int len=strlen(str)+1;
cArray=new char[len];
strcpy(cArray,str);
nLen=len;
}
void operator=(const char *str)
{
if(nLen)
delete []cArray;
int len=strlen(str)+1;
cArray=new char[len];
strcpy(cArray,str);
nLen=len;
}
char *GetBuffer()const{return cArray;}
//----------------------------------------------------
String operator=(String p)
{
if(nLen)
delete []cArray;
strcpy(p.cArray,p.GetBuffer());
int len=strlen(p.GetBuffer())+1;
cArray=new char[len];
nLen=len;
strcpy(cArray,p.GetBuffer());
return *this;
}
//---------------------------------------------------
};
int main(int argc, char* argv[])
{
String str("meihaofeng");
char *p=str.GetBuffer();
cout<<p<<endl;
str="mei";
p=str.GetBuffer();
cout<<p<<endl;
String s="hello!";
str=s;//這裡開始出錯
p=str.GetBuffer();
cout<<p<<endl;
return 0;
}
初看也許你會覺得沒有什麼錯誤啊,但你將這段完整的程式拿去執行一下(編譯連結是能通過的),啊!那些錯誤有些驚人.下面讓我們來分析一下,出現那些錯誤的原因.
加粗的那個函式是出錯的根本,主要就是針對這個函式進行分析.在主函式是從“str=s;”這一句開始出錯的。執行str=s對應String operator=(String p)函式上。首先將“s”作為引數拷貝給String operator=(String p)的形參p。在這個過程中,s的兩個成員資料
nLen和cArray也相應的拷貝給p的nLen和cArray.但是cArray是一個指標,拷貝之後p.cArray的值和s.cArray的值是相等的,可以知道這兩個指標變數都指向同一片記憶體區域,這一個記憶體區域就是儲存“hello!”的記憶體區域。當String operator=(String p)這個函式執行完後,由於物件p超出了它的作用範圍,所以呼叫物件p的解構函式,在解構函式中釋放cArray所指向的記憶體,此時釋放cArray所指向的的記憶體相當於釋放p.cArray或主函式中s.cArray所指向的內在區域。釋放完後返回*this物件,注意,返回的*this這個物件將拷貝給主函式的一個臨時的物件,之後就釋放這個臨時物件的記憶體。在將*this拷貝給一個臨時物件的過程如同將s作為實參傳給形參p一樣,使臨時物件的兩個資料成員nLen和cArray與(*this)物件的nLen和cArray相等,同理(*this).cArray與臨時物件的cArray指向現一片記憶體區域。在釋放這個臨時物件時先呼叫這它的解構函式,從上面的程式可以知道這個*this代表主函式的一個物件str,所以在解構函式中就釋放str.cArray所指向的記憶體。
從String operator=(String p)中由前面的分析可知,主函式中s.cArray以及str.cArray所指向的記憶體已經被釋放了。返回到主函式通過p=str.GetBuffer();cout<<p<<endl;列印str.cArray的值也就列印出了錯誤的值。當主函式中str與s兩個物件的生存週期結束後,這兩個物件的解構函式會被再次呼叫。在呼叫解構函式時,又會釋放s.cArray和str.cArray所指向的記憶體,由於在String operator=(String p)函式中這兩個指標所指向的記憶體區域已經被釋放了,所以在主函式中再次釋放就會出錯。這些就是程式中隱藏著的錯誤。
最好的解決方法就是不要編譯器新增的拷貝建構函式,而是自己手動新增一個,並在這個拷貝建構函式中執行一此操作。下面的程式修補了上面程式的錯誤,程式如下:
#include "stdafx.h"
#include "iostream.h"
#include "string.h"
class String
{
int nLen;
char *cArray;
public:
String(){nLen=0; cArray=NULL;}
~String()
{
cout<<"~String()"<<endl;
delete []cArray;
nLen=0;
}
String(const char *str)
{
int len=strlen(str)+1;
cArray=new char[len];
strcpy(cArray,str);
nLen=len;
}
void operator=(const char *str)
{
if(nLen)
delete []cArray;
int len=strlen(str)+1;
cArray=new char[len];
strcpy(cArray,str);
nLen=len;
}
char *GetBuffer()const{return cArray;}
//----------------------------------------------------
String(const String & s)//自己新增的拷貝建構函式。
{
cout<<"拷貝建構函式"<<endl;
char *temp;
temp=new char[s.nLen];
strcpy(temp,s.GetBuffer());
cArray=new char[s.nLen];
strcpy(cArray,temp);
delete temp;
}
//----------------------------------------------------
String operator=(String p)
{
if(nLen)
delete []cArray;
strcpy(p.cArray,p.GetBuffer());
int len=strlen(p.GetBuffer())+1;
cArray=new char[len];
nLen=len;
strcpy(cArray,p.GetBuffer());
return *this;
}
//---------------------------------------------------
};
int main(int argc, char* argv[])
{
String str("meihaofeng");
char *p=str.GetBuffer();
cout<<p<<endl;
str="mei";
p=str.GetBuffer();
cout<<p<<endl;
String s="hello!";
str=s;
p=str.GetBuffer();
cout<<p<<endl;
return 0;
}
在拷貝建構函式中要為目標物件中的指標分配空間,並讓指標指向分配的空間。並將源物件的指標所指向的空間裡的值複製到分配的空間中。注意這裡的目標物件,對應到上面的程式“str=s”中,str即為目標物件,s為源物件。在String operator=(String p)返回時臨時物件為目標物件,*this為源物件。
下面是程式的執行結果:
---------------------------------
meihaofeng
mei
拷貝建構函式
拷貝建構函式
~String()
~String()
hello!
~String()
~String()
---------------------------------
從執行結果為可以看到拷貝建構函式被呼叫了兩次,第一次是,執行str=s時,將實參s拷貝給String operator=(String p)函式的形參p.
第二次是,在String operator=(String p)返回裡,將*this拷貝給一個臨時物件。解構函式被呼叫了四次,從程式中也不難分析出是那四次.
第一、二是String operator=(String p)中發生的。第三、四次是在主函式發生的。
相關文章
- 拷貝建構函式函式
- C++拷貝建構函式(深拷貝,淺拷貝)C++函式
- 拷貝建構函式中的陷阱函式
- 拷貝建構函式(比較全的)函式
- C++拷貝建構函式詳解C++函式
- C++ 拷貝建構函式詳解C++函式
- C++語言之結構體、類、建構函式、拷貝建構函式C++結構體函式
- C++中建構函式,拷貝建構函式和賦值函式的詳解C++函式賦值
- C++複製控制:拷貝建構函式C++函式
- 建構函式,拷貝賦值函式的N種呼叫情況函式賦值
- c/c++ 拷貝控制 建構函式的問題C++函式
- C++/CLI思辨錄之拷貝建構函式C++函式
- 建構函式的作用函式
- 類的陣列初始化後會呼叫拷貝建構函式陣列函式
- C++的一點基本自我修養(二)--拷貝建構函式C++函式
- C++之Big Three:拷貝構造、拷貝賦值、解構函式探究C++賦值函式
- 作用域安全的建構函式函式
- 如何寫一個作用域安全的建構函式函式
- 批註:C++中複製建構函式與過載賦值操作符總結:預設淺拷貝,帶指標的需要深拷貝C++函式賦值指標
- 預設建構函式、引數化建構函式、複製建構函式、解構函式函式
- 類的建構函式和解構函式函式
- 虛解構函式的作用函式
- 第五篇:明確拒絕不想編譯器自動生成的拷貝建構函式和賦值運算子過載函式編譯函式賦值
- C++知識點49——類繼承與類的構造、拷貝、operator=和解構函式C++繼承函式
- 自己寫的unix檔案拷貝指令cp實現函式函式
- C語言實現字串拷貝函式的幾種方法C語言字串函式
- 建構函式與解構函式函式
- js的深拷貝和淺拷貝JS
- 物件的深拷貝與淺拷貝物件
- ## 建構函式函式
- 建構函式函式
- vue深拷貝淺拷貝Vue
- Java的建構函式Java函式
- [譯] 建構函式已死,建構函式萬歲!函式
- python 指標拷貝,淺拷貝和深拷貝Python指標
- C/C++——建構函式、複製建構函式和解構函式的執行時刻C++函式
- JavaScript中的淺拷貝與深拷貝JavaScript
- VUE 中 的深拷貝和淺拷貝Vue