浅拷贝
1、概念:所谓的浅拷贝就是当在进行对象的复制时,只是进行对象的数据成员的拷贝,其中默认的拷贝构造函数也是浅拷贝。大多数情况下,使用浅拷贝是没有问题的,但是当出现动态成员,就会出现问题。
2、当浅拷贝时,出现了动态成员,当调用析构函数时,一块内存就有可能被析构多次。
写时拷贝
1、概念:因为浅拷贝的缺陷,所以在这个时候我们就引入了引用计数的拷贝。
【说明】:引用计数的拷贝是用来解决浅拷贝存在的问题的,所以它也是一种浅拷贝
2、如何实现:我们为每个内存的字符数组添加一个引用计数refCount,即就是在构造函数申请空间的时候多申请出来4个字节。表示有多少个对象使用这块内存,有多少个对象使用,就让refCount值加1,当对象被析构的时候,让refCount值减1,当pcount值为0的时候,将这块内存释放掉。当然refCount也要实现内存共享,所以它也是一个堆中的数据,每个对象都有一个指向它的指针。
3、那么我们这样想:如果一个对象第一次开辟空间存放字符串再开辟一块新的空间存放新的额引用计数,当它拷贝构造其它对象时让其它对象的引用计数都指向存放引用计数的同一块空间,refCount设置成int*就可以啦,但是这种方式有缺陷。
缺陷一:每次new两块空间,创建多个对象的时候效率比较低
缺陷二:它多次分配小块空间,容易造成内存碎片化,导致分配不出来大块内存
所以 我们进行了优化:
开辟一块空间,但是它的头几个字节用于计数
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
String(char *ptr = "")
{
if(ptr == NULL)
{
_ptr = new char[strlen(ptr)+5];
_ptr = new char[5];
*(_ptr+4) = '\0';
}
else
{
_ptr = new char[strlen(ptr)+5];
*((int*)_ptr) = 1;
_ptr += 4;
strcpy(_ptr,ptr);
}
}
String(const String& s)
:_ptr(s._ptr)
{
(*((int*)(_ptr-4)))++;
}
String& operator=(const String& s)
{
if(this != &s)
{
if(--(*((int*)(_ptr-4))) == 0)
{
delete[]_ptr;
}
else
{
_ptr = s._ptr;
++(*(int*)(_ptr-4));
}
}
return *this;
}
~String()
{
if((_ptr != NULL) && ((--(*((int*)(_ptr-4)))) == 0))
{
delete[]_ptr;
_ptr = NULL;
}
}
private:
char* _ptr;
};
void Funtest()
{
String s1("hello");
String s2(s1);
String s3(s2);
s3 = s2;
}
int main()
{
Funtest();
return 0;
}
【注意】:因为不止一个对象使用这一块内存,当修改自己的时,也等于修改了他人的。在向这块存储单元写之前,应该确信没有其他人使用它。如果引用计数大于1,在写之前必须拷贝这块存储单元,这样就不会影响他人了。