解析浅拷贝的缺陷及其解决方案
浅拷贝的缺陷
类的定义:
class Name
{
public:
Name(const char *myp)
{
m_len = strlen(myp);
m_p =(char *) malloc(m_len + 1); //
strcpy(m_p, myp);
}
~Name()
{
if (m_p != NULL)
{
free(m_p);
m_p = NULL;
m_len = 0;
}
}
protected:
private:
char *m_p ;
int m_len;
};
//对象析构的时候 出现coredump
void objplaymain()
{
Name obj1("abcdefg");
Name obj2 = obj1; //C++编译器提供的 默认的copy构造函数 浅拷贝 //解释图为:图1
//Name obj3("obj3");
//obj3 = obj1; // C++编译器提供的 等号操作 也属 浅拷贝 //解释图为:图2
}
void main()
{
objplaymain();
cout<<"hello..."<<endl;
system("pause");
}
在这个程序中,首先abcdefg存储在内存全局区中,obj1初始化调用有参构造函数Name(const char *myp),然后使用malloc()函数动态分配内存(分配在堆区),并把这个内存空间的地址赋给P,strcpy(m_p, myp)则将myp的数据“abcdefg”赋值到堆区中,这时P指向的内存空间的数据就是“abcdefg”,完成了初始化工作。
Name obj2 = obj1; 调用C++编译器提供的 默认的copy构造函数 ,是浅拷贝,则只是将obj1的属性拷贝给了obj2,而并未重新开辟新的内存空间,这时,obj1 obj2属性是一样的,P指向的内存空间也是同一个。
类的析构函数首先会析构掉obj2,这时obj2的P指针指向的内存空间就释放了。然后接着析构obj1的内存空间。这就有问题了,因为obj1和obj2 P指针指向的内存空间是一样,P指向的内存空间已经释放了,obj2析构时,想要再次释放内存空间,就出现同一内存出现多次释放的错误。
因此,浅拷贝存在缺陷,在写程序时,应尽量避免浅拷贝的使用。C++编译器默认提供的拷贝构造函数机制就是浅拷贝,在写类时,特别注意浅拷贝的缺陷。
文字没看懂,请看原理图:
以上是解释了机制,在C++中,C++的默认拷贝构造函数和赋值操作,使用的都是浅拷贝,默认拷贝构造函数为图1,赋值操作的浅拷贝为下图(图2);机制原理相似。不再赘述。本来obj3指向0xbb11,经过赋值操作,obj3指向了0xaa11,这样就造成了0xbb11内存泄漏,并且0xaa11重复释放的错误。
解决方案(C++类)
对于拷贝构造函数造成的浅拷贝,解决方案就是在类中重写拷贝构造函数。例如:
//Name obj2 = obj1;
//解决方案: 手工的编写拷贝构造函数 使用深copy
Name(const Name& obj1)
{
m_len = obj1.m_len;
m_p = (char *)malloc(m_len + 1);
strcpy(m_p, obj1.m_p);
}
对于赋值操作造成的浅拷贝,解决方案同样是在类中重写赋值函数。
文中两图来源于传智播客。