写时拷贝小结
要想深刻了解写时拷贝,首先得先知道写时拷贝解决了什么问题,所以我们先来谈谈浅拷贝和深拷贝:
浅拷贝就是让当前的指针指向一块已存在的内存区域,和其它指针共享同一块地址空间。浅拷贝带来的问题是当程序结束时,对象d1和d2都会去调用析构函数去清空这块空间,而一块空间析构两次就会导致程序奔溃。
而深拷贝可以在每一次拷贝时为当前指针重新分配空间,不用再去共享那块空间。虽说深拷贝可以很好的解决这个问题,但它也存在自身的问题。比如相同的数据内容如果每次都重新分配空间去放置数据的话,就会造成空间的浪费。为了克服能同时解决这两个问题,就引出了写时拷贝,写时拷贝在浅拷贝的基础上加入了引用计数,当拷贝构造的时候只把引用计数加1,并不重新开辟这块空间,析构的时候只需把引用计数减1,当引用计数为1时才真正析构这块空间。
部分参考:http://blog.csdn.net/zhang1308299607/article/details/74933134
#define _CRT_SECURE_NO_WARNINGS//屏蔽错误
#include<iostream>
using namespace std;
class Out_of_range
{
};
class String
{
struct StrNode
{
int ref; //对象引用的个数
int len;//当前字符串的长度
int size;//总的空间大小
char data[];//存放的数据
};
private:
StrNode* ptr;
public:
String() :ptr(NULL) {}
String(const char* p) :ptr(NULL)//此处是首次初始化有参对象时调用的
{
cout << "String(const char*p)" << endl;
if (p != NULL)
{
int n = (strlen(p) + 1);
ptr = (StrNode*)malloc(sizeof(StrNode) + sizeof(char) * n * 2);
if (NULL == ptr) exit(1);
ptr->ref = 1;
ptr->len = n - 1;
ptr->size = n * 2;
strcpy(ptr->data, p);
}
}
String(const String& _X) :ptr(NULL)
{
cout << "String(const String& _X)" << endl;
if (_X.ptr != NULL)
{
ptr = _X.ptr;
ptr->ref += 1;
}
}
String& operator=(const String& _X)
{
if (this == &_X || this->ptr == _X.ptr)
return *this;
}
// s1.ptr != NULL = s2.ptr != NULL;
// NULL != NULL
//!= NULL NULL;
//!
const char& operator[](const int index) const
{
if (ptr == NULL || index < 0 || index > ptr->len)
{
throw Out_of_range();
}
return ptr->data[index];
}
char& operator[](const int index)//此处分配空间是共享空间中有对象想改变数据时调用的
{
if (ptr == NULL || index < 0 || index > ptr->len)
{
throw Out_of_range();
}
if (ptr->ref > 1)
{
size_t size = sizeof(StrNode) + sizeof(char) * ptr->size;
StrNode* newdata = (StrNode*)malloc(size);
if (NULL == newdata) exit(1);
memmove(newdata, ptr, size);
ptr->ref -= 1;
ptr = newdata;
ptr->ref = 1;
}
return ptr->data[index];
}
~String()
{
if (ptr != NULL)
{
if (--ptr->ref == 0)
{
free(ptr);
}
}
ptr = NULL;
}
friend ostream &operator<< (ostream & out, const String & s);
};
ostream& operator<< (ostream& out, const String& s)
{
for (int i = 0; i <s.ptr->len; i++)
{
out << s.ptr->data[i] << " ";
}
return out;
}
int main()
{
String s1("zjhinghello");
String s2(s1);
String s3("hello");
cout << "s1: " << s1 << endl;
cout << "s2: " << s2 << endl;
cout << "s3: " << s3 << endl;
s2[3] = 'z';
cout << "s2: " << s2 << endl;
cout << "s1: " << s1 << endl;
s3 = s2;
cout << "s2: " << s2 << endl;
cout << "s3: " << s3 << endl;
return 0;
}
执行结果:
图解步骤:
解释:共享同一块内存空间的对象,无论其中哪一个对象想改变这块空间的数据,都不能直接进行修改,而是重新开辟一块与原空间等大的空间,把原数据拷贝过去之后,对新空间里的数据进行操作,而重新开辟空间意味着不再引用原空间,从而原空间的引用计数减1。对新开辟的空间引用计数初始化为1,以备后边被引用。