深浅拷贝与写时拷贝

来源:互联网 发布:病态矩阵特征值 编辑:程序博客网 时间:2024/05/24 00:13

在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;}
原创粉丝点击