【浅拷贝】
又称位拷贝,编译器只是直接将指针的值拷贝过来,结果多个对象共用同一块内存,当一个对象将这块对象释放掉之后,其他对象不知道该内存已经还给了系统,以为还有效,所以在这段内存进行操作的时候,发生了错误。
#include<iostream>
using namespace std;
#include<cstring>
class String
{
public:
String(const char *str)//构造函数
:_str(new char(strlen(str)+1))
{
strcpy(_str, str);
}
String& operator=(const String& str)//赋值运算符
{
if (this != &str)//不是自己给自己赋值
{
_str = str._str;
}
return *this;
}
~String()//析构函数
{
if (NULL != _str)
{
delete[] _str;
_str = NULL;
}
}
private:
char *_str;
};
int main()
{
String s1("hello world!");
String s2 = s1;
system("pause");
return 0;
}
结果出错:
原因:
【深拷贝】
#include<iostream>
using namespace std;
#include<string.h>
#include<assert.h>
class String
{
public:
String()
{}
String(const char *str)//构造函数
:_size(strlen(str) + 1)
{
_str = new char[_size];
strcpy(_str, str);
_str[_size] = '\0';
}
String(const String& str)//拷贝构造
:_size(str._size)
{
_str = str._str;
}
String& operator=(const String& str)//赋值运算符
{
if (this != &str)//不是自己给自己赋值
{
_str = str._str;
_size = str._size;
}
return *this;
}
~String()//析构函数
{
if (NULL != _str)
{
delete[] _str;
_str = NULL;
_size = 0;
}
}
void print()//打印
{
if (this != NULL)
{
cout << _str << endl;
}
}
int Size()const//获取字符串长度
{
return _size;
}
bool operator==(const String &s)//比较两个字符串是否相等
{
if (_size != s._size)
{
return false;
}
return strcmp(_str, s._str) ? false:true;
}
bool operator!=(const String &s)//比较两个字符串是否不相等
{
return !(*this == s);
}
bool operator<(const String &s)//比较当前字符串是否小于s
{
int i = 0;
while (_str != '\0' && s._str != '\0')
{
if (_str[i] < s._str[i])
{
return true;
}
else if (_str[i] > s._str[i])
{
return false;
}
i++;
}
if (_str == '\0')
{
return true;
}
return false;
}
bool operator<=(const String &s)
{
return (*this < s) || (*this == s);
}
bool operator>(const String &s)
{
return !(*this <= s);
}
bool operator>=(const String &s)
{
return (*this > s) || (*this == s);
}
void remove(size_t n,size_t len)//删除指定长度的元素
{
for (int i = (int)n; i < _size; i++)
{
_str[i] = _str[i + len];
}
_size -= len;
}
void Insert(int n,const char* s)//在指定位置插入元素
{
assert(n < _size);
int len = strlen(s);
for (int i = _size; i >= (int)n; i--)
{
_str[i + len] = _str[i];
}
while (*s != '\0')
{
_str[n++] = *s++;
}
_size += len;
}
private:
char *_str;//字符串
int _size;//长度
};
int main()
{
char array[] = "hello wrold";
String s1(array);
s1.print();
String s2(s1);
s2.print();
String s3;
s3 = s1;
s3.print();
cout << endl;
String s("hello wrold!");
cout << s.Size() << endl;
cout << endl;
cout << s2.operator==(s1) << endl;
cout << s3.operator!=(s1) << endl;
cout << endl;
String s4("hello");
String s5("hello");
cout << s4.operator<(s5) << endl;
cout << s4.operator<=(s5) << endl;
cout << s4.operator>(s5) << endl;
cout << s4.operator>=(s5) << endl;
cout << endl;
String S1("hi!!!!!!");
S1.print();
S1.remove(1,3);
S1.print();
S1.Insert(4, "hello");
S1.print();
system("pause");
return 0;
}
【写时拷贝】
浅拷贝的两个问题?
1、释放多次——用引用计数解决
2、一个修改了会影响另一个——用写时拷贝解决
String的三种写时拷贝:
方案一:
方案二:
方案三:
第三种方案最合适
#include<iostream>
using namespace std;
class String
{
public:
String(const char* str)//构造函数
:_str(new char[strlen(str) + 5])
{
_str += 4;//跳过计数器的4个字节
strcpy(_str, str);
//*((int*)(_str - 4)) = 1;//给计数器赋值,不会改变_str
GetRefCount() = 1;//给计数器赋值
//(int)(_str - 4) = 1;//不行,左值不可以变
}
//s2(s1)
String(const String& s)//拷贝构造
:_str(s._str)
{
GetRefCount()++;
}
//s3=s1
String& operator=(const String& s)
{
//if (this != &s)只能判断s1=s1,不能判断s1=s2(s1、s2本身就指向同一块空间)
if (_str != s._str)//不是自己给自己赋值
{
if ((--GetRefCount()) == 0)//引用计数为0才释放其空间
{
delete[] (_str - 4);
}
_str = s._str;
GetRefCount()++;//让this来调用,不让s来调用
}
return *this;
}
//写时拷贝
void CopyOnWrite()
{
if (GetRefCount() > 1)
{
--GetRefCount();
char* tmp = new char[strlen(_str) + 5];
tmp += 4;
strcpy(tmp, _str);
GetRefCount() = 1;
}
}
char& operator[](size_t pos)
{
CopyOnWrite();
return _str[pos];
}
const char& operator[](size_t pos)const
{
return _str[pos];
}
int& GetRefCount()//获取计数器内的数字
{
return *((int*)(_str - 4));
}
~String()//析构函数
{
if (--GetRefCount() == 0)//判断计数器是否为0
{
delete[] (_str - 4);//释放计数器占用的空间
}
}
private:
char *_str;
};
int main()
{
String s1("hello");
String s2(s1);
String s3("hello world");
cout << s1.GetRefCount() << endl;
cout << s2.GetRefCount() << endl;
cout << s3.GetRefCount() << endl;
cout << endl;
s1 = s3;
cout << s1.GetRefCount() << endl;
cout << s2.GetRefCount() << endl;
cout << s3.GetRefCount() << endl;
cout << s1[0] << endl;
cout << s1.GetRefCount() << endl;
cout << s2.GetRefCount() << endl;
cout << s3.GetRefCount() << endl;
system("pause");
return 0;
}