拷贝控制
C++提供两个拷贝控制函数
- 拷贝构造函数
- 拷贝赋值运算符重载
例如:String
类
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增加了移动构造函数和移动赋值运算符重载。
深拷贝与浅拷贝
概念
浅拷贝:只拷贝指针地址。
通常默认拷贝构造函数与赋值运算符重载都是浅拷贝。深拷贝:重现分配堆内存,拷贝指针指向内容。
例如:String类
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;
}
测试代码
int main(){
String s1("Hello World");
String s2 = s1;
String s3;
s3 = s1;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
}
比较
优点 | 缺点 | |
---|---|---|
浅拷贝 | 只有一份数据,节省空间。 | 因为多个指针指向同一个空间,容易引发同一内存多次释放的问题。 |
深拷贝 | 每个指针指向不同地址,没有同一内存多次释放的问题。 | 存在多份相同数据,浪费空间。 |
浅拷贝与深拷贝的优缺点分别互为彼此的优缺点。有什么办法可以兼有二者的优点?
主要解决问题:
- 数据相同时只有一份内存。
- 不会出现多次释放问题。
计数器技术:数据相同共享一份内存
计数器技术就是兼有浅拷贝与深拷贝优点的一种技术。
在类声明中添加了计数器
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;
};
实现中增加计数处理
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;
}
问题:构造新对象的计数是几?计数器技术会导致构造新对象计数错误。
解决:每个空间应该具有自己的引用计数,而不能所有空间共享一个引用计数。
头文件
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;
};
实现
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;
}
写时拷贝技术
以上都是拷贝复制操作,如果字符串发生改变,那么才是真正的写时拷贝。
例如:实现+=
操作
String& String::operator+=(const String& str){
base.count--;
// 一些具体操作...
base = new StringBase();
base.count++;
return *this;
}
优化
上面写时拷贝存在两次层动态处理,其实可以合为一层处理。