1.浅拷贝
浅拷贝字面意思就是浅层次的拷贝,就是简单的把值拷贝过去
下面我们来看一下浅拷贝的代码
class Base
{
public:
Base(const char* str)
{
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
Base(const Base& s)
:_str(s._str)
{
}
~Base()
{
if (_str)
{
delete[] _str;
}
_str = NULL;
}
private:
char* _str;
};
int main()
{
Base s1("深浅拷贝");
Base s2(s1);
return 0;
}
但是这代码编译后就崩溃了,为什么呢?
我们在return 0;的前面加一个getchar(); 这样那个黑窗口就弹出来了,然后你一个回车就又崩溃了,证明return的时候出现了问题,return的时候干什么呢?执行析构函数,那 执行析构函数的时候为什么会出错?我们知道析构函数就是销毁内存的,那我们来看看s1,s2的内存
可以看到这俩个指针指向同一个内存但析构函数会调用俩次,那么在第二次调用的时候那块内存已经被销毁了,所以才会出错。
现在发现了浅拷贝的弊端,要解决这个问题就引入了新的概念——深拷贝
深拷贝的本质就是重新开辟一的空间来存放拷贝过来的数据
下面是深拷贝的代码
class Base
{
public:
Base(const char* str)
{
_str = new char[strlen(str) + 1];
//QuoteCount = new int(1);
strcpy(_str, str);
}
Base(const Base& s)
:_str(NULL)
{
Base tmp(s._str);
swap(_str, tmp._str);
}
~Base()
{
if (_str)
{
delete[] _str;
}
_str = NULL;
}
private:
char* _str;
};
int main()
{
Base s1("深浅拷贝");
Base s2(s1);
return 0;
}
这下就不会出错了,我们再来看一下s1,s2的内存
这下指针所指的内存就不一样了
可是深拷贝有的在使用的时候不断地开空间,使用完了又得销毁,十分费力,我们又在我们需要将拷贝过来的数据进行修改时不得不开辟出新的内存,所以就有了引入计数的写时拷贝,就是我们用数来记录有几个指针来指着这块内存,当想要销毁这块内存时将这个数减1,当这个数是1时才真正的销毁这块内存。
代码如下
class Base
{
public:
Base(const char* str)
{
_str = new char[strlen(str) + 1];
QuoteCount = new int(1);
strcpy(_str, str);
}
Base(const Base& s)
:_str(s._str)
, QuoteCount(s.QuoteCount)
{
++(*QuoteCount);
}
~Base()
{
if (--(*QuoteCount) == 0)
{
delete[] _str;
delete[] QuoteCount;
}
}
void CopyOnWrite();
private:
char* _str;
int* QuoteCount;
};
void Base::CopyOnWrite()
{
if (*QuoteCount > 1)
{
char* newstr = new char[strlen(_str) + 1];
strcpy(newstr, _str);
_str = newstr;
QuoteCount = new int(1);
}
}
这里为什么要用int型的指针呢?是因为这是一个int型的变量的话修改它时就会修改的不彻底,意思就是你在拷贝s2时将这个变量加1;那么只会改变s2的引用计数,s1的引用数据还是1,当我们通过地址改变数据时就不会出现这样的情况
我们看一下
int main()
{
Base s1("");
Base s2(s1);
Base s3("");
return 0;
}
可以看到s1,s2的引用计数是2,s3的引用计数是1.我们需要开辟新的地址是就调用CopyOnWrite()函数,这样就非常方便了。