- 定义
在数据第一次写入到某个存储位置时,首先将原有内容拷贝出来,写到另一位置处,然后再将数据写入到存储设备中,该技术只拷贝在拷贝初始化开始之后修改过的数据。
- C++提供两个拷贝控制函数
拷贝构造函数
拷贝赋值运算符重载 - 实例
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;
};
6.2 深拷贝与浅拷贝
- 概念
浅拷贝:只拷贝指针地址。
通常默认拷贝构造函数与赋值运算符重载都是浅拷贝。
深拷贝:重现分配堆内存,拷贝指针指向内容。
例如:
String::String(const char* str){
if(NULL == str){
data = new char[1];
data[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(str.data) << ':' << str.data;
}
- 比较
| 优点 | 缺点 |
|---|---|
| 浅拷贝 | 只有一份数据,节省空间。 因为多个指针指向同一个空间,容易引发同一内存多次释放的问题。 |
| 深拷贝 | 每个指针指向不同地址,没有同一内存多次释放的问题。 存在多份相同数据,浪费空间。 |
浅拷贝与深拷贝的优缺点分别互为彼此的优缺点。有什么办法可以兼有二者的优点?
主要解决问题:
数据相同时只有一份内存。
不会出现多次释放问题。
计数器技术就是兼有浅拷贝与深拷贝优点的一种技术。
在类声明中添加了计数器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] = ' ';
}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] = ' ';
}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(str.base) << ':' << str.base->data;
}
6.4 写时拷贝技术
以上都是拷贝复制操作,如果字符串发生改变,那么才是真正的写时拷贝。
例如:实现+=操作
String& String::operator+=(const String& str){
base.count--;
// 一些具体操作...
base = new StringBase();
base.count++;
return *this;
}
- 实例
#include#include #include using namespace std; class String { shared_ptr ptr; // char* s; // ptr.get() public: String(const char* s) { char* t = new char[strlen(s)+1]; strcpy(t,s); ptr = shared_ptr (t); //t智能指针 } ~String() { cout << __func__ << endl; } size_t size()const { return strlen(ptr.get()); } friend ostream& operator<<(ostream& os,const String& s) { return os << (void*)s.ptr.get() << ":" << s.ptr.get(); } char& operator[](int i); String operator+(const String& s)const { char* t = new char[size()+s.size()+1]; strcpy(t,ptr.get()); strcat(t,s.ptr.get()); return String(t); } }; int main() { String s("Hello,World"); cout << s << endl; String t = s; t = "def"; //String("def")为临时对象,用完即析构 cout << s << endl; cout << t << endl; cout << s+t << endl; }
结果:
[root@localhost 5]# ./a.out 0x1d59e70:Hello,World ~String 0x1d59e70:Hello,World 0x1d5a2c0:def 0x1d5a320:Hello,Worlddef ~String ~String ~String



