用string剖析浅拷贝、深拷贝、写时拷贝

来源:互联网 发布:淘宝库存会影响排名吗 编辑:程序博客网 时间:2024/06/05 20:56

一、浅拷贝

对于普通变量来说,它们之间的互相赋值很简单:

int  a=1;

int b=2;

a=b;

但是对类对象来说存在深浅拷贝问题:

浅拷贝就是值拷贝!

示例:

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

s2=s1中,String的成员变量_str是个指针,其本质是个地址,将s1的_str赋值给s2的_str,使s1._str和s2._str指向同一块空间,这就是浅拷贝。


浅拷贝的弊端:

因为两个对象指向同一块空间,所以析构时同一块空间析构两次,导致程序崩溃!

总结:

当类的对象里有指针时,进行简单的赋值拷贝,两个对象指向同一块内存,存在崩溃问题!所以这里我们要进行深拷贝!

二、深拷贝

 拷贝时开一个新的的空间,空间内的内容和拷贝的内容相同。


代码如下

#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<string>using namespace std;class String{public://构造String():_pstr(NULL){}String(const char* pstr):_pstr(NULL){if (pstr == NULL){_pstr = new char[1];*_pstr = '\0';}else{_pstr = new char[strlen(pstr) + 1];strcpy(_pstr, pstr);}}//拷贝构造////传统写法//String(const String & s)//:_pstr(new char[strlen(s._pstr) + 1])//{//strcpy(_pstr, s._pstr);//}//现代写法String(const String & s):_pstr(NULL){_pstr = new char[1];String strtmp(s._pstr);std::swap(_pstr, strtmp._pstr);}//析构~String(){if (_pstr){delete[]_pstr;_pstr = NULL;}}//赋值运算符重载////传统写法//String & operator = (const String & s)//{//if (this != &s)//{//char* ptmp = new char[strlen(s._pstr) + 1];//strcpy(ptmp, s._pstr);//_pstr = ptmp;//}//return *this;//}//现代写法String & operator = (const String & s){if (this != &s){String strtmp(s);std::swap(_pstr, strtmp._pstr);}return *this;}private:char* _pstr;};
深拷贝弊端:

每次拷贝,赋值都要新开一个内存,导致占用大量内存且很多内存片段内的内容重复,产生浪费。

三、写时拷贝

写时拷贝就是在需要给空间内写入内容时再去开辟空间,简单的赋值时不去开空间;但是这样就产生了上述的浅拷贝的问题,为了解决这个问题,引入了引用计数,当一块空间的引用计数为0时才去释放这块空间,这样就解决了同一块空间被释放多次的问题。

有两中方案来实现:

方案一:

#define _CRT_SECURE_NO_WARNINGS#include<iostream>using namespace std;class String{public:String(const char* str):_str(new char[strlen(str)+1]),_refcount(new int(1)){strcpy(_str, str);}String(String &s){_str = s._str;_refcount = s._refcount;++(*_refcount);}~String(){if (--(*_refcount) == 0){delete[] _str;delete _refcount;cout << "delete" << endl;}}//s2=s1        (s2 == s1?    s2 != s1?)String& operator =(const String &s){if (this != &s){if (--(*_refcount) == 0){delete[] _str;delete _refcount;}--(*_refcount);_str = s._str;_refcount = s._refcount;++(*_refcount);}return *this;}//写时拷贝函数:当发生写入时,引用计数减一void Copyonwirte()//{if (*_refcount > 1){char* tmp = new char[strlen(_str) + 1];strcpy(tmp, _str);--(*_refcount);_str = tmp;_refcount = new int(1);}}char& operator [](size_t pos){Copyonwirte();//有可能写入return _str[pos];}private:char* _str;int* _refcount;};void test(){String s1("abcdef");String s2 = s1;//s2[1] = 'a';char a = s1[2];}int main(){test();return 0;}


方案二:


class String{public:String(const char* str):_str(new char[strlen(str) + 5]){_str += 4;*(int*)(_str - 4) = 1;strcpy(_str, str);}String(String &s){_str = s._str;++(*(int*)(_str - 4));}~String(){if (--(*(int*)(_str - 4)) == 0){delete[](_str - 4);cout << "delete" << endl;}}//s2=s1        (s2 == s1?    s2 != s1?)String& operator =(const String &s){if (this != &s){if (--(*(int*)(_str - 4)) == 0){delete[] (_str-4);}else{--(*(int*)(_str - 4));}_str = s._str;++(*(int*)(_str - 4));}return *this;}//写时拷贝函数:当发生写入时,引用计数减一void Copyonwirte()//{if (*(int*)(_str - 4) > 1){char* tmp = new char[strlen(_str) + 5];strcpy(tmp, _str-4);--(*(int*)(_str - 4));_str = tmp+4;*(int*)(_str - 4) = 1;}}char& operator [](size_t pos){Copyonwirte();//有可能写入return _str[pos];}private:char* _str;};void test(){String s1("abcdef");String s2 = s1;//s2[1] = 'a';char a = s1[2];}int main(){test();return 0;}



阅读全文
0 0
原创粉丝点击