C++String深浅拷贝、写时拷贝

C++String深浅拷贝、写时拷贝

在C++中我们要拷贝一个字符串的时,有俩种方法:

1.浅拷贝

2.深拷贝

① 浅拷贝:就是让当前的指针指向已存在的区域,和其他指针共同管理同一块空间

下面举一个String类中字符串str的浅拷贝

代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string.h>
using namespace std;
class String
{
public :
    String(const char* str)
        :_str(new char[strlen(str)+1])
    {
        strcpy(_str,str);
    }
    String(const String& s)
        :_str(s._str)
    {
    }
    String& operator=(const String& s)
    {
        if(this!=&s)
        {
            _str=s._str;
        }
        return *this;
    }
    ~String()
    {
        if(_str)
        {
            delete[] _str;
        }
    }

private:
    char* _str;
};
void test()
{
    String s("hello word!");
    String s1(s);
}
int main()
{
    test();
    return 0;
}

在这的s._str和s1._str共同指向了同一块空间

这里写图片描述
这里写图片描述

因为他俩同时指向同一块空间,但析构的时候只会去找这块空间,会导致析构俩次而出错,这就是简单赋值浅拷贝的弊端。

②深拷贝

深拷贝:就是开辟一块和被拷贝对象同样大小的空间,并复制其内容

如图所示:

Alt text

深拷贝代码如下:

class String
{
public:
    String(const char* str)
        :_str(new char[strlen(str)+1])
    {
        strcpy(_str,str);
    }
    String(const String& s)
        :_str(new char[strlen(s._str)+1])
    {
        strcpy(_str,s._str);
    }
    String& operator=(const String& s)
    {
        if(this!=&s)
        {
            delete[] _str;
            _str=new char[strlen(s._str)+1];
            strcpy(_str,s._str);
        }
        return *this;
    }
    ~String()
    {
        delete [] _str;
    }
private:
    char* _str;
};
void test()
{
    String s("hello word!");
    String s1(s);
    String s2("hello!");
    s2=s1;
}
int main()
{
    test();
    return 0;
}

写时拷贝(Copy—On—Write):

写时拷贝方法1:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<string.h>
using namespace std;
class String
{
public :
    String(const char* str)
        :_str(new char[strlen(str)+1])
        ,_refcount(new int)
    {
        *_refcount=1;
        strcpy(_str,str);
    }
    String(const String& s)
        :_str(s._str)
    {
        _refcount=s._refcount;
        (*_refcount)++;
    }
    String& operator=(const String& s)
    {
        if(_str!=s._str)//判断是否是自己给自己赋值
        {
            if(*refcount!=1)//判断是否有多个对象和它共用同一块空间,如是则进行计数指针的--操作
            {
                (*_refcount)--;//将计数指针的值减一下
            }
            _refcount=s._refcount;//将指针指向s
            (*_refcount)++;//并对计数指针的值加加
        }
        return *this;
    }
    ~String()
    {
        if(*_refcount==1)//当计数指针等于1时真正释放
        {
            delete[] _str;
        }
        else
            (*_refcount)--;
    }
private:
    char* _str;
    int* _refcount;
};
int main()
{
    test();
    return 0;
}
测试结果及分析:

这里写图片描述
这里写图片描述

当代码运行到String s2(“hello!”)时,可以清楚的看到s、s1指向同一块空间,*_refcount=2;s2是单独开辟了一块空间,*_refcount=1;

这里写图片描述
这里写图片描述

当代吗走到s2=s时,s、s1、s2指向同一块空间,*_refcount=3,只析构一次。
这种方法是采用一个计数指针来记下同一块空间被多少个对象共用,析构时只需将计数器减一次,当计数器等于1时,就真正要将这块空间释放了;同时当重载时只需对计数指针进行操作。

这里写图片描述

写时拷贝方法2:
这种方法和方法1其实是差不多的,只不多就是将计数器放在_str中。

这里写图片描述

class String
{
public:
    String(char* str="")
        :_str(new char[strlen(str)+5])
    {
        _str=_str+4;
        strcpy(_str,str);
        *((int*)_str-4)=1;
    }
    String(const String& s)
    {
        _str=s._str;
        (*((int*)_str-4))++;
    }
    String& operator=(const String& s)
    {
        if(this!=&s)
        {
            if((*((int*)_str-4))==1)
            {
                delete[](_str-4);
            }
            _str=s._str;
            (*((int*)_str-4))++;
            cout<<"operator="<<endl;
        }
        return *this;
    }
    ~String()
    {
        if((*((int*)_str-4))==1)
        {
            delete[] (_str-4);
            cout<<"~String()"<<endl;
        }
        else
            (*((int*)_str-4))--;

    }
private:
    char* _str;
};
void test()
{
    String s("hello word!");
    String s1(s);
    String s2("hello!");
    s2=s1;
}
int main()
{
    test();
    return 0;
}
测试结果:

Alt text

分析:s、s1指向同一块空间,而s2单独开辟一块空间,而后来赋值运算符重载,s、s1、s2、指向同一块空间,此时在operator=中对s2释放了一次空间,s、s1 、s2最终析构同一块空间一次。

猜你喜欢

转载自blog.csdn.net/fangxiaxin/article/details/76038212