模拟实现string时,要是没有显示定义拷贝构造函数,在拷贝构造一个新的对象时,编译器会调用默认的构造函数默认的拷贝构造函数是怎么样的呢
String(const char* pStr = " ")
:_pStr(new char[strlen(pStr)+1]) {
strcpy(_pStr,pStr);
}
调用这个默认的构造函数会出现一个问题,那就是若是拷贝构造1个对象,比如
String s1("hello world");
String s2(s1);
使用s1拷贝构造s2,注意,现在
s1,s2指向的是同一片内存空间
在销毁对象调用析构函数时
~String() {
if( _pStr ) {
delete[] _pStr;
}
}
先销毁s2,s2指向的空间释放,s2释放成功,但是s1与s2指向的是同一片内存空间,空间被释放,s1变为野指针,导致程序崩溃
那么如何解决呢
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情
况都是按照深拷贝方式提供。
主要思路就是给每个对象都分配空间
加深拷贝
String(const String& s)
: _str(new char[strlen(s._str)+1]) {
strcpy(_str, s._str);
}
注意,_str(new char[strlen(s._str)+1])
,这里是利用被拷贝函数的指针来开辟新的空间给拷贝的函数,这样子,两个对象就是不同的空间了,释放的时候就不会出现浅拷贝的问题
当然,对于赋值运算符重载也可以使用相同的方法
String& operator=(const String& s) {
if (this != &s) {
delete _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
return *this;
}
还有个现代版的写法
String(const String& s)
: _str(nullptr)
{
String strTemp(s._str);
swap(_str, strTemp._str);
}
这里开辟一个strTemp临时空间,使用被拷贝对象的空间(包括数据)开辟临时空间,然后交换拷贝对象与临时空间的空间和数据,这样也可以避免使用相同空间,析构的时候就不会出现浅拷贝现象了
还有一个解决方法,就是写时拷贝技术
这在浅拷贝的基础上加了计数器,构造了对象,资源加一,析构资源减一个,如果计数为1,说明该
对象是资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。
class String {
public:
String(const char* str = "")
{
if (nullptr == str) {
str = "";
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
_count = 1;
}
String( String& s)
:_str(s._str)
{
_count++;
}
String& operator=(String s) {
return *this;
}
~String() {
if (_str && 0 == --_count) {
delete[]_str;
}
}
private:
char* _str;
static int _count;
};
int String::_count = 0;
void TestString() {
String s1("hello");
String s2(s1);
}
int main() {
return 0;
}
代码扒的,就是这样