c++中字符串的拷贝
1. 在c语言中,
·char string[] = "abcdef";//一般时候是以数组的形式进行字符串的定义
·strcpy(arr, array);//调用库函数进行拷贝
2. 而在c++中,利用类和对象可以进行字符串的定义与拷贝
·先来看一个例子,判断它的封装性
class string
{
public:
string(const char *pstr = "")
:_pstr(new char[strlen(pstr) + 1])
{
strcpy(_pstr, pstr);
}
//在拷贝构造函数中,s1的指针既指向s1字符串,也指向s2的指针
~string()
{
if (_pstr)
delete[]_pstr;//在主函数结束后,需要调用析构函数将s1和s2销毁掉,
//s2销毁掉之后,s1的指针就成为了野指针,会发生内存泄漏
}
private:
char *_pstr;
};
int main()
{
string s1("hello");
string s2(s1);//s2需要调用string类的拷贝构造函数来创建,但是没有显式定义,
//只能使用系统默认拷贝构造函数,而此时s1与s2 共用一块空间
return 0;
}
·防止内存泄漏的方法
~string()
{
if (_pstr)
delete[]_pstr;
_pstr = NULL;//最后应该让指针指向空,才会避免内存访问违规的问题
}
·以上的拷贝为浅拷贝,编译器只是将对象中的值拷贝过来,如果对象中管理资源,最后就会导致多个对象共享一份资源,当一个对象被销毁时,将它所指向的资源释放掉,那么另一个对象不知道该资源已经被释放,所以就会发生访问违规,程序将出现崩溃。
·在浅拷贝中,由于多个对象共用同一块内存空间,所以在释放时,可能会释放多次,会引发很多问题,那么能否保证:当多个对象共用同一块空间时,该空间最终只能释放一次呢?
有这个问题就引出了引用计数,它的原理就是当多个对象共用同一块资源时,要保证资源只能释放一次,则最简单的方法就是记录多少个对象在使用该资源,每减少(增加)一个就记录一次。当最后一个不再使用时,该对象负责将资源释放掉。
string &operator=(const string &s)
{
if (this!=s)
if (0 == --(*_pcount))//若引用计数为0,说明对象已经到最后一个,要将对象释放完,
{
delete[]_pstr;
delete[]_pcount;
}
_pstr = s._pstr;//每增加一个对象,引用计数就加1
_pcount = s._pcount;
++(*_pcount);
return *this;
}
若两个对象s1,s2共用同一块资源时,那么引用计数为2, 在使用完之后,先销毁s2,那么s2不再使用资源,pcount--;然后再销毁s1,pcount--;这样就不会引起销毁完s2之后,s1的指针成为野指针。
3. 深拷贝
·深拷贝与浅拷贝的区别就在于,浅拷贝只是拷贝了值,但是深拷贝利用拷贝构造函数进行拷贝(不是系统默认的拷贝构造函数),给每个对象独立分配资源。
·普通版
string(const char *pstr = "")
:_pstr(new char[strlen(pstr) + 1])
{
strcpy(_pstr, pstr);
}
string &operator=(const string &s);
string(const string &s)
:_pstr(new char[strlen(pstr) + 1])
{
strcpy(_pstr, s._pstr);//最明显的区别就在于两个指针指向两块空间,不必共用一块空间
}
·简洁版
赋值运算符的重载,例:s3=s2
string &operator=(string &s)//相当于引用了s2,s2会自动生成一个临时变量,指向s2的指针指向的字符串
{
Swap(_pstr,s._pstr);//s3和s2指针的交换,实际是两个指针执行指向的内容交换了,s2指向了空,s3指向了字符串
Return *this;//最后析构函数清理的是s2 所指向的空间。
}