浅拷贝会造成指针悬挂的问题。
举个例子:两个对象是s1和s2的指针_str都指向new开辟的同一块空间,如下图,主程序结束时,对象逐个撤销,先撤销对象s2,会调用析构函数释放动态分配的内存;再撤销对象s1时,s1._str所指向的内存空间已经是无法访问了,而s2._str原先指向的那块内存却无法释放,出现了所谓的指针悬挂! 两个对象企图释放同一块内存,从而导致一块内存被释放两次这也是不行的,运行会出错。
#include <iostream>
using namespace std;
class String
{
public:
String(char *str)
:_str(new char [strlen(str )+1])
{
strcpy(_str, str);
}
String(const String & s)
{
_str = s._str;
}
String& operator=(const String & s )
{
if (this !=&s)
{
_str = s._str;
}
return *this ;
}
~String()
{
delete[] _str;
}
private:
char* _str;
};
void Test()
{
String s1("Lynn" );
String s2=s1;
}
int main()
{
Test();
system("pause" );
return 0;
}
深拷贝 深拷贝解决了指针悬挂的问题,当调用拷贝构造或赋值运算符的重载函数时,程序会生成一份该内存的拷贝,这样每个指针都会指向一块相对独立的空间,撤销对象时调用析构函数,分别释放他们自己的动态分配的内存,相互之间不影响。如下图:
深拷贝
///////////////////////////////////////////////////////////////////////////////////////
// 写String类的构造函数时一定要注意参数问题
// 首先要考虑到构造的对象分有参数和无参数两种情况
// 构造对象的时候不能直接赋值,否则一块内存两次释放的话程序会出错
// 无参的构造函数不能将_str指针赋值为NULL,因为不能strlen(NULL)
// 赋值运算符的重载要考虑到有可能分配内存失败的问题
// 当然,记得要给'\0'分配空间哦
// By:Lynn-Zhang
//////////////////////////*****************////////////////////////////////////////////
#include<iostream>
using namespace std;
class String
{
public:
String(char * str="") //不能strlen(NULL)
:_str(new char [strlen(str ) + 1])
{
strcpy(_str, str);
}
String(const String &s)
:_str(new char [strlen(s ._str) + 1])
{
strcpy(_str, s._str);
}
//赋值运算符的重载
String& operator=(const String& s)
{
if (this != &s )
{
/* //有可能开辟空间失败,但是却破坏了_str的内容
delete[] _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str); */
char* tmp = new char [strlen(s ._str) + 1];
strcpy(tmp, s._str);
delete[] _str;
swap(_str, tmp);
}
return *this ;
}
char* CStr()
{
return _str;
}
~String()
{
delete[] _str;
}
private:
char* _str;
};
//函数测试
void Test()
{
String s1("aaaaa" );
cout << s1.CStr() << endl;
String s2(s1);
cout << s2.CStr() << endl;
String s3 = s1;
s3= s2;
cout << s3.CStr() << endl;
String s4;
// s4 = s1;
cout << s4.CStr() << endl;
}
int main()
{
Test();
system("pause" );
return 0;
}