深浅拷贝与写时拷贝

在c++中,基本所有的类都要考虑深拷贝,浅拷贝与写时拷贝,根据不同的定义,选择适合自己的拷贝方式。时间类就可以用浅拷贝,而二叉树,string类就需要深拷贝。
string类在vs编译器下使用的深拷贝,在Linux下使用的浅拷贝。
为什么会存在深浅拷贝的问题呢?
string的浅拷贝是让两个不同的指针指向同一块空间,而这在析构的时候会出现将一块空间释放两次,程序会崩溃,因此我们才需要进行深拷贝,即第二个指针开辟和第一个指针一样大小空间,然后将内容复制过去。
深拷贝:

ass string
{
public:
    string(char* pstr)    //构造函数
        :str(new char[strlen(pstr) + 1])   //开辟新的空间
    {
        if (pstr == NULL)//
        {
            str = new char[1];    //开辟一个 存放‘\0’
            *str = '\0';
        }
        else
        {
            str = new char[strlen(pstr) + 1];
            for (size_t i = 0; i < strlen(pstr); i++)  //深复制
            {
                str[i] = pstr[i];
            }
            //strcpy(str,pstr);
            //memcpy(str,pstr,strlen(pstr)+1);浅复制
        }
    }
    //拷贝构造函数
    string(const string& pstr)
        :str(new char[strlen(pstr.str)+1])
    {
        for (size_t i = 0; i < strlen(pstr.str); i++)
        {
            str[i] = pstr.str[i];
        }
        //strcpy(str,pstr.str);
        //memcpy(str,pstr.str,strlen(pstr.str)+1);
    }
    //赋值运算符重载
    string& operator = (const string & pstr)
    {
        if (&str == &pstr.str)     //检查是否是自己给自己赋值
            return *this;
        delete[] str;             //释放临时空间
        str = new char[strlen(pstr.str) + 1];
        for (size_t i = 0; i < strlen(pstr.str); i++)
        {
            str[i] = pstr.str[i];
        }
        //strcpy(str,pstr.str);
        //memcpy(str,pstr.str,strlen(pstr.str)+1);
        return *this;
    }
    /*现代写法: 根据拷贝构造函数让系统自己开辟空间 
    拷贝构造函数
    string(const string& pstr)
         :str=null;    必须置为空,要不然访问地址非法化
         {    
             string tmp (pstr);
             swap(tmp.str,str);
             return *this;
         }
    赋值运算符重载
    string& operator = (const string& pstr )
    {
        string tmp(pstr);
        swap(tmp.str, str);
        return *this;
     }*/
    //析构函数
    ~string()
    {
        delete[] str;
        str = NULL;
    }

private:
    char* str;

};
int main()
{
    string a ("12345");
    string b(a);
    cout << b << endl;
}

浅拷贝,当我们需要改变新的空间的内容的时候,才会重新开辟空间呢?
1)判断空间使用的次数来选择析构,增加一个类成员 count,但是这样造成的后果是每一个成员都有一个不同的count 在析构的时候就很混乱还是会出错
2)然后呢我们会想到使用静态成员的办法,因为此时 static int count 是放在静态区,它是所有对象共享的,不会为每个对象单独生成一个count,可是当我们有多个不同的成员共同管理一块空间,而此时我们又用构造函数创建一个对象时候,count又会变为1,所以这种方法还是不行 。
3)使用引用计数,把引用计数放在字符串的前四个字节里,每个创建出来的对象都有不同的引用计数

class string
{
public:
    string(char* pstr)    //构造函数
        :str(new char[strlen(pstr) + 1+4])//多开辟的4个字节存放引用计数
    {
        if (pstr == NULL)
        {
            str = new char[1];
            *str = '\0';
        }
        else
        {
            str = new char[strlen(pstr) + 1+4];
            int* count = (int* )str;
            *count = 1;
            count ++;  //后移拷贝数据
            str = (char*)count;
            for (size_t i = 0; i < strlen(pstr); i++)  //深复制
            {
                str[i] = pstr[i];
            }
            //strcpy(str,pstr);
            //memcpy(str,pstr,strlen(pstr)+1);浅复制
        }
    }
    //拷贝构造函数
    string(const string& pstr)
    {
        str = pstr.str;
        int * count = (int *)(str-4) ;
        ++*count;

    }
    //赋值运算符重载
    string& operator = (const string & pstr)
    {
        if (&str == &pstr.str)
            return *this;
        destroy();//判断是否释放空间
        str = pstr.str;
        int * count = (int *)(str-4) ;
        ++*count;
    }
void destroy()
{
    int * count = (int *)(str-4); //获取引用计数
    if(*count == 1)
    {
       delete[](str-4);
       str = NULL;
    }
    else
      --*count;
}
//析构函数
~string()
{
   destroy();
}

private:
    char* str;
    int * count;
};

写时拷贝:
在浅拷贝中,假设有多个名称引用了同一个字符串,当其中一个需要修改字符串时,这就会导致引用的值发生改变,这明显不是我们所期望的,所以出现了写时拷贝,(可能会修改字符串的时候使用深拷贝将其与大部队分离,这样就保护了其他引用成员)
则我们只需要重载[] 就可以实现

const string& operator[]const 
{
        string tmp(pstr);
        swap(tmp.str, str);
        return *this;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值