在我们实现string类的时候会需要用到字符串的拷贝,会有一下三种方法:浅拷贝、深拷贝、写实拷贝
浅拷贝:
浅拷贝的实现
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
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 test()
{
String s1("hello world");
String s2(s1);
}
int main()
{
test();
return 0;
}
由上图可知两个指针指向了同一块内存,因此在调用析构函数时,会重复调用两次,导致出错。
深拷贝:
由上图可知,深拷贝即是重新开辟一块与原数据块一样大小的内存空间,将s1内容拷贝,这样s1和s2指向各自的空间。
深拷贝的实现:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class String
{
public:
String(const char*str)
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
String(const String&str)
:_str(new char[strlen(str._str)+1])
{
strcpy(_str, str._str);
}
String operator=(const String &str)
{
if (this != &str)
{
delete[]_str;
_str = new char[strlen(str._str) + 1];
strcpy(_str, str._str);
}
return *this;
}
~String()
{
if (_str)
delete[]_str;
}
private:
char*_str;
};
void test()
{
String s1("hello world");
String s2(s1);
}
int main()
{
test();
return 0;
}
此时两个指针便会指向不同的内存,不存在重复析构的问题。
写实拷贝
由上图我们可知,当s1开辟空间的时候,用前四个字节来存放ReferenceCount
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class String
{
public:
String(const char*str)
:_str(new char[strlen(str) + 5])
{
_str = _str + 4;
strcpy(_str, str);
RefCount() = 1;
}
int &RefCount()
{
return *((int*)(_str - 4));
}
String(const String&str)
:_str(str._str)
{
RefCount()++;
}
String operator=(const String &str)
{
if (this != &str)
{
if (RefCount() == 1)
{
delete[](_str-4);
}
_str = str._str;
RefCount()++;
}
return *this;
}
~String()
{
if (RefCount()== 1)
delete[](_str-4);
else
RefCount()--;
}
private:
char*_str;
};
void test()
{
String s1("hello world");
String s2(s1);
}
int main()
{
test();
return 0;
}
当RefCount为1时正式调用析构函数。
当使用写时拷贝时,相比较深拷贝,减少了内存开销,优化了浅拷贝。