C++中当对象中含有指针数据成员时,由于使用默认的构造函数(拷贝构造函数,以及默认赋值函数)就可能造成指针悬挂问题。
首先分析一下产生指针悬挂的机理。假设我们自己使用一个String类,包含是一个指向字符串的指针以及字符串的大小。
其数据结构如下图:
编写一个存在指针悬挂隐患的程序:
#include<iostream>
#include<string.h>
using namespace std;
class String
{
private :
char *ptr;
int size;
public :
String(int size = 1,char * str = "\0")
{
ptr = new char[size];
strcpy(ptr,str);
this->size = size;
}
char & operator[](int idx)
{
return ptr[idx];
}
~String()
{
delete [] ptr;
}
};
int main()
{
String str1(10,"gaozhefeng"),str2(10,"Neo");
str2 = str1;//容易造成指针悬挂
return 0;
}
这句代码容易产生指针悬挂问题。代码愿意是str2指向一片属于自己的内存区,但是内存区放的数据和str1一样。但是由于默认的赋值函数只是把str1对象的数据一次拷贝给str2,也就是str2中指针的值是一样的,指向同一块内存。当函数作用结束时,str1和str2分别调用析构函数,错误的将一块内存区析构两次。
下面图解说明一下原因:
刚开始的初始化:
这行代码的本意:
由于没有超载赋值运算符,最后意外的造成了指针悬挂问题:
有效解决指针悬挂问题的策略是将拷贝构造函数,赋值运算符进行超载,即可。编程中一定要避免是真悬挂的问题。会造成意想不到的错误。
附上超载后的程序:
#include<iostream>
#include<string.h>
using namespace std;
class String
{
private :
char *ptr;
int size;
public :
String(int size = 1,char * str = "\0")
{
ptr = new char[size];
strcpy(ptr,str);
this->size = size;
}
String(const String & obj)//避免造成指针悬挂问题
{
size = obj.size;//大小
// delete [] ptr_str;//先释放原来的内存
ptr = new char[size];//生成新的内存
strcpy(ptr,obj.ptr);//拷贝字符串
}
char & operator[](int idx)
{
return ptr[idx];
}
String operator=(const String & obj)//避免造成指针悬挂问题
{
size = obj.size;//大小
delete [] ptr;//先释放原来的内存
ptr = new char[size];//生成新的内存
strcpy(ptr,obj.ptr);//拷贝字符串
return *this;
}
char * getPtr()
{
return ptr;
}
~String()
{
delete [] ptr;
}
};
int main()
{
String str1(10,"gaozhefeng"),str2(10,"Neo"),str3 = str1;
str2 = str1;//容易造成指针悬挂
return 0;
}