关于String类的写实拷贝

时间:2022-05-08 19:51:18

在自己定义的String类中使用写实拷贝来提高效率。

写实拷贝是在浅拷贝的基础上使用引用计数器来拷贝对象,如果有对象被拷贝构造时只需要将计数器++,不用再开辟新的空间。

可以编写初版的代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
String(const char* str = "")
:_str(new char[strlen(str) + 1])
,_refcount(1)
{
strcpy(_str, str);
//每当有新的对象构造时,将计数器置1,意思是有一个对象引用这块空间
}
~String()
{
if (--_refcount == 0) //如果只有一个对象引用这块空间,则直接释放,否则计数器--
{
delete[]_str;
}
}
//写实拷贝
String(String& s)
:_str(s._str)
{
_refcount++;
}
private:
char* _str;
int _refcount;
};

这段代码看似正确,但是其实有很大的bug,因为计数器是私有的,所以每个对象的计数器都为1,释放空间时系统会奔溃。
所以可以将_refcount设置为static静态,为对象共有。代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
String(const char* str = "")
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
_refcount = 1; //每当有新的对象构造时,将计数器置1,意思是有一个对象引用这块空间
}
~String()
{
cout << "delete[]" << _str << endl;
if (--_refcount == 0) //如果只有一个对象引用这块空间,则直接释放,否则计数器--
{
delete[]_str;
}
}
//写实拷贝
String(String& s)
:_str(s._str)
{
_refcount++;
}
private:
char* _str;
static int _refcount;//static静态成员可以被共用
};

int String::_refcount = 0; //静态变量要在类中声明,类外定义

但是static是全局变量,所以创建不同对象时_refcount会出现问题。
所以可以直接再开辟一个空间用来存储计数器个数 ,使用一个指针指向这块空间,这样拷贝构造时只需要将对象指针指向计数器,将计数器内容++,释放对象空间时,如果计数器个数不为1,则直接计数器内容–,为1证明只有一个对象指向这块空间,则释放计数器空间,释放字符串空间。
代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
class String
{
public:
String(const char* str = "")
:_str(new char[strlen(str) + 1])
, _refptr(new int(1))
{
strcpy(_str, str);
}
~String()
{
if (--(*_refptr) == 0)
{
delete[]_str;
delete _refptr;
}
}
String(String& s)
:_str(s._str)
, _refptr(s._refptr)
{
(*_refptr)++;
}
String& operator=(String& s)
{
//自己给自己赋值,或者两个指向同一块空间
//自己拥有一块空间
//自己和别人共同拥有一块空间
if (_str != s._str)
{
if (--(*_refptr) == 0)
{
delete[]_str;
delete _refptr;
}
_str = s._str;
_refptr = s._refptr;
(*_refptr)++;
}
return *this;
}
private:
char* _str;
int* _refptr;
};

这样就是一个完整的正确的写实拷贝了。