从一个例子说明浅拷贝是什么
#include <iostream>
using namespace std;
class string
{
public:
string(const char *str="")
{
if (str == NULL)
{
data = new char[1];
*data = '\0';
}
else
{
data = new char(strlen(str) + 1);
strcpy(data, str);
}
}
~string()
{
delete[]data;
data = NULL;
}
private:
char *data;
};
当类中的私有数据 有指针 并且如果让程序默认生成 拷贝构造和 赋值方法 会发生浅拷贝现象 ,即
void main()
{
String s1("hello");
String s2 = s1;
}
默认的拷贝构造 只拷贝指针的指向,就是 s1和s2现在都指向同一块存着?“hello”的地址空间。
当析构的时候,先析构s1,之后再析构s2会发现这块内存已经被释放了,故浅拷贝造成非法的地址访问。
解决方法是自己写拷贝构造和赋值方法。
String(const String &str)
{
data = new char(strlen(str.data) + 1);
strcpy(data, str.data);
}
String& operator=(const String &str)
{
if (this != &str)
{
delete[]data;
data = new char(strlen(str.data) + 1);
strcpy(data, str.data);
}
return *this;
}
自己实现的方法,就是先申请一块内存,进行拷贝,拷贝指针指向的值到新空间。
但是这种深拷贝会造成多块内存存的数据都是一样的,出现浪费内存的现象。
于是使用写时拷贝技术。
写时拷贝技术指的是,当在拷贝构造和赋值的时候,如果对象只是进行读操作,那么采用浅拷贝,多个对象共用一块内存,
当对象需要对内存进行写操作时,立即使用深拷贝,另开辟新内存,拷贝内存上的数据,在新内存上进行写操作。
#include <iostream>
#include <string>
using namespace std;
class String;
class String_rep//全部实现深拷贝 实现一个引用计数器
{
friend class String;
public:
String_rep(const char *str=""):count(0)//需要初始化 引用计数器
{
if (str == NULL)
{
data = new char[1];
*data = '\0';
}
else
{
data = new char(strlen(str) + 1);
strcpy(data, str);
}
}
String_rep(const String_rep &str) :count(0) //需要初始化 引用计数器
{
data = new char(strlen(str.data) + 1);
strcpy(data, str.data);
}
String_rep& operator=(const String_rep &str)
{
if (this != &str)
{
delete[]data;
data = new char(strlen(str.data) + 1);
strcpy(data, str.data);
data[strlen(str.data)] = '\0';
}
return *this;
}
~String_rep()
{
delete[]data;
data = NULL;
}
public:
void plus()
{
++count;
}
void dec()
{
if (--count == 0) //当引用计数器为0 时,就可以释放空间了
{
delete this;//调用析构
}
}
char& write(int n) //为了重载 []
{
if (n > -1 && n < strlen(data))
return data[n];
}
private:
char *data;
int count;
};
class String
{
public:
String(const char *str = "")
{
rep = new String_rep(str); //全部交给引用计数器来做,
rep->plus(); //在string类中只是维护 引用计数器的个数
}
String(const String &str)
{
rep = str.rep; //全部用浅拷贝
rep->plus();
}
String& operator =(const String &str)
{
if (this != &str)
{
rep->dec(); //旧的引用计数器减一
rep = str.rep; //浅赋值 指向新的引用计数器
rep->plus();//已经更改了引用计数器 并加一
}
return *this;
}
char& operator [](int pos) //重载[] 此时需要写 需要更改了 使用深拷贝
{
if (rep->count > 1)
{
String_rep *new_rep = new String_rep(rep->data); //重开辟一块空间
rep->dec();//原来的减少
rep = new_rep;//更改指向 新空间
rep->plus(); // 新空间的引用计数器增加
}
return rep->write(pos);
}
~String()
{
rep->dec(); //只负责减少 在上面的类中对减少到0 的空间释放
}
private:
String_rep *rep;
};
void main()
{
String s1("hello");
String s2 (s1);
s2[0] = 'H';
}