String类的写时拷贝

来源:互联网 发布:如何评价网络架构 编辑:程序博客网 时间:2024/05/19 15:44

1、Copy-On-Write的工作原理是什么?

答:是引用计数

2、String类在什么情况下才共享内存?

答:这意思就是你用我的,才会出现共享。在使用别的类的数据时,只有两种情况,第一种就是以别的类构造自己,这个会触发拷贝构造函数;第二种就是以别的类赋值,这个会触发赋值运算符的重载。

注意:不要看见“=”就以为是赋值,人家可能是调用了拷贝构造,尤其是前面带了类名的。还有要注意不要看见后面对了有类名,有对象,有“=”

,就以为对了,还要看开头所给数据的类型,万一是指针或者字符什么的,不就错了。

3、String类在什么情况下才触发写时拷贝?

答:当然是“共享同一内存的类的内容发生改变”的时候,修改数据就会触发写时拷贝。

4、写时拷贝具体怎样实现的?

String s1="hello";

String s2=s1;

String s3;

s3=s2;


String w1="world";

String w2("");

w2=w1;

在这里想要s1、s2、s3共用一块内存,让w1、w2共用一块内存,在s1、s2、s3中要定义一个引用计数refCount,在w1、w2中要定义另一个引用计数refCount。


当我们要为String类分配内存时,就多分配一个空间用来存放引用计数,只有发生拷贝构造或赋值,内存值就加1,当内容修改时,String类先查看这个引用计数是否为0,如果不为0,说明现在有其他对象在共享这块空间,此刻就自己先将内存中的内容做一份拷贝,并且要将引用计数减1,然后将拷贝的数据放到新空间,如下图:


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;


class String
{
public:
String(const char *str = "")//构造函数,先来个空字符串
:_str(new char[strlen(str)+1+4])//分配一个空间,前面开辟四个字节存放引用计数
{
cout << "构造函数" << endl;
_str += 4;//前四个字节存放引用计数,让_str指向第四个往后
refCount() = 1;//引用计数的初始值设置为1
strcpy(_str, str);
}
String(String& s)//拷贝构造
:_str(s._str)
{
cout << "拷贝构造" << endl;
refCount()++;//s也指向_str,共享_str,所以引用计数要++
}
String& operator=(String& s)//赋值运算符重载
{
cout << "赋值运算符重载" << endl;
if (this != &s)//避免自己给自己赋值,如s1=s1
{
Release();//先释放一下空间,释放谁的?s的空间?应该是s的空间
_str = s._str;
refCount()++;//又有一个对象要共享这个空间了
}
return *this;//这个this指针就是要被赋值的值
}
~String()
{
cout << "析构函数" << endl;
Release();//直接释放空间
}
public:
char& operator[](size_t index)//干嘛呢?
{
if(refCount()== 1)//如果计数器为1,则直接返回
{
return _str[index];
}
refCount()--;//如果计数器不为1,就减1
char *tmp = _str;//定义一个指针tmp
_str = new char[strlen(tmp) + 1 + 4];//开辟空间
_str += 4;//_str指向数据段
strcpy(_str, tmp);
refCount() = 1;//计数器初始值设置为1
return _str[index];
}
private:
int & refCount()//引用计数
{
return *(int*)(_str - 4);//整形指针减4指向开头再解引用就是开头
}
void Release()
{
if (--refCount() == 0)//原本只有一个对象用这块内存
{
cout << "释放" << endl;
delete[](_str - 4);//释放的时候还要释放前面存储引用计数的四个字节
_str = NULL;//
}
}
private:
char *_str;
};

原创粉丝点击