一、浅拷贝
对于普通变量来说,它们之间的互相赋值很简单:
但是对类对象来说存在深浅拷贝问题: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; }