拷貝控制
C++提供兩個(gè)拷貝控制函數(shù)
- 拷貝構(gòu)造函數(shù)
- 拷貝賦值運(yùn)算符重載
例如:String
類(lèi)
class String{
public:
String(const char* str = NULL);
String(const String& str);
String& operator=(const String& str);
~String();
size_t size()const;
friend ostream& operator<<(ostream& os,String const& str);
private:
char* data;
};
C++11增加了移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符重載。
深拷貝與淺拷貝
概念
淺拷貝:只拷貝指針地址伟阔。
通常默認(rèn)拷貝構(gòu)造函數(shù)與賦值運(yùn)算符重載都是淺拷貝。深拷貝:重現(xiàn)分配堆內(nèi)存跷车,拷貝指針指向內(nèi)容链方。
例如:String類(lèi)
String::String(const char* str){
if(NULL == str){
data = new char[1];
data[0] = '\0';
}else{
data = new char[strlen(str)+1];
strcpy(data,str);
}
}
String::~String(){
delete [] data;
data = NULL;
}
String::String(const String& str){
data = new char[str.size()+1];
strcpy(data,str.data);
}
String& String::operator=(const String& str){
if(this != &str){
delete [] data;
data = new char[str.size()+1];
strcpy(data,str.data);
}
return *this;
}
inline size_t String::size()const{
return strlen(data);
}
ostream& operator<<(ostream& os,const String& str){
return os << static_cast<const void *>(str.data) << ':' << str.data;
}
測(cè)試代碼
int main(){
String s1("Hello World");
String s2 = s1;
String s3;
s3 = s1;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
}
比較
優(yōu)點(diǎn) | 缺點(diǎn) | |
---|---|---|
淺拷貝 | 只有一份數(shù)據(jù),節(jié)省空間。 | 因?yàn)槎鄠€(gè)指針指向同一個(gè)空間元咙,容易引發(fā)同一內(nèi)存多次釋放的問(wèn)題既们。 |
深拷貝 | 每個(gè)指針指向不同地址,沒(méi)有同一內(nèi)存多次釋放的問(wèn)題概漱。 | 存在多份相同數(shù)據(jù),浪費(fèi)空間喜喂。 |
淺拷貝與深拷貝的優(yōu)缺點(diǎn)分別互為彼此的優(yōu)缺點(diǎn)瓤摧。有什么辦法可以兼有二者的優(yōu)點(diǎn)竿裂?
主要解決問(wèn)題:
- 數(shù)據(jù)相同時(shí)只有一份內(nèi)存。
- 不會(huì)出現(xiàn)多次釋放問(wèn)題照弥。
計(jì)數(shù)器技術(shù):數(shù)據(jù)相同共享一份內(nèi)存
計(jì)數(shù)器技術(shù)就是兼有淺拷貝與深拷貝優(yōu)點(diǎn)的一種技術(shù)腻异。
在類(lèi)聲明中添加了計(jì)數(shù)器
s_count
。
class String{
public:
String(const char* str = NULL);
String(const String& str);
String& operator=(const String& str);
~String();
size_t size()const;
friend ostream& operator<<(ostream& os,String const& str);
private:
char* data;
static int s_count;
};
實(shí)現(xiàn)中增加計(jì)數(shù)處理
int String::s_count = 0;
String::String(const char* str){
if(NULL == str){
data = new char[1];
data[0] = '\0';
}else{
data = new char[strlen(str)+1];
strcpy(data,str);
}
++s_count;
}
String::~String(){
if(--s_count == 0){
delete [] data;
data = NULL;
}
}
String::String(const String& str):data(str.data){
++s_count;
}
String& String::operator=(const String& str){
if(this != &str){
data = str.data;
++s_count;
}
return *this;
}
問(wèn)題:構(gòu)造新對(duì)象的計(jì)數(shù)是幾这揣?計(jì)數(shù)器技術(shù)會(huì)導(dǎo)致構(gòu)造新對(duì)象計(jì)數(shù)錯(cuò)誤悔常。
解決:每個(gè)空間應(yīng)該具有自己的引用計(jì)數(shù),而不能所有空間共享一個(gè)引用計(jì)數(shù)给赞。
頭文件
class String{
struct StringBase{
StringBase(const char* str);
~StringBase();
char* data;
int count;
};
public:
String(const char* str = NULL);
String(const String& str);
String& operator=(const String& str);
~String();
size_t size()const;
friend ostream& operator<<(ostream& os,String const& str);
private:
StringBase* base;
};
實(shí)現(xiàn)
String::StringBase::StringBase(const char* str){
if(NULL == str){
data = new char[1];
data[0] = '\0';
}else{
data = new char[strlen(str)+1];
strcpy(data,str);
}
++count;
}
String::StringBase::~StringBase(){
if(--count == 0){
delete [] data;
data = NULL;
}
}
String::String(const char* str){
base = new StringBase(str);
}
String::~String(){
}
String::String(const String& str):base(str.base){
base->count++;
}
String& String::operator=(const String& str){
if(this != &str){
base->count--;
base = str.base;
base->count++;
}
return *this;
}
inline size_t String::size()const{
return strlen(base->data);
}
ostream& operator<<(ostream& os,const String& str){
return os << static_cast<const void *>(str.base) << ':' << str.base->data;
}
寫(xiě)時(shí)拷貝技術(shù)
以上都是拷貝復(fù)制操作机打,如果字符串發(fā)生改變,那么才是真正的寫(xiě)時(shí)拷貝片迅。
例如:實(shí)現(xiàn)+=
操作
String& String::operator+=(const String& str){
base.count--;
// 一些具體操作...
base = new StringBase();
base.count++;
return *this;
}
優(yōu)化
上面寫(xiě)時(shí)拷貝存在兩次層動(dòng)態(tài)處理残邀,其實(shí)可以合為一層處理。