类中的数据在有指针的情况下,浅拷贝只是增加了一个指针,指向已经存在的内存,而深拷贝就是新增加一个指针并指向新申请的一块内存,使这个新增加的指针指向新开辟的内存,深拷贝可以有效避免,浅拷贝重复释放同一内存的错误,现举例说明如下:
#include <iostream>
using namespace std;
class Student
{
private:
int num;
char *name;
public:
Student();
~Student();
};
Student::Student()
{
name = new char(10);
cout << "Student" << endl;
}
Student::~Student()
{
cout << "~Student " << endl;
delete name;
name = NULL;
}
int main()
{
Student s1;
Student s2(s1);//用对象s1初始化s2
return 0;
}
执行之后:
Student
~Student
~student
press any key to continue
可以看出,调用了一次构造函数,执行了两个析构函数(两个对象的指针成员指向的内存相同,可以添加打印地址验证),这就造成了name指针被分配了一次内存,却被释放了两次,这样会造成严重的内存泄露问题,究其原因是因为,当类中函数指针数据成员时候,用一个对象初始另外一个对象时候会自动调用默认的拷贝构造函数,即此时进行的时浅拷贝,而由于含有指针成员,所有拷贝后,新生成的指针也指向test1中所分配的内存,这就造成的同一块内存有两个指针指向它,对其进行了两次释放,早过就造成了严重的内存泄露的问题。
所以,对于这种情况,显然是应该使用深拷贝,即自己实现拷贝函数,而不使用默认的,代码如下:
Student::Student(const Student &s)
{
name = new char(10);
memcpy(name, s.name, strlen(s.name));
}
总结:浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针(自己实现)。
当对象中存在指针成员时,除了在复制对象时需要考虑自定义拷贝构造函数,还应该考虑以下两种情形:
1.当函数的参数为对象时,实参传递给形参的实际上是实参的一个拷贝对象,系统自动通过拷贝构造函数实现;
2.当函数的返回值为一个对象时,该对象实际上是函数内对象的一个拷贝,用于返回函数调用处。
如何避免两次delete同一块内存
正确的做法:
int* p=new int(1);
delete p;
p = NULL;
以下结论可以有助于你理解
(1)delete 一次以后,p成了野指针,它作为地址的值还是有效地没还可以访问它以前指向的内存,不过那片内存被重新格式化了(不是真正的格式化,意思是可以重新被分配);
(2)p不等于NULL,用 if(p) 语句不能判断它指向的内存是否有效(此时它指向的内存无效,p本身有效);
(3)delete 一次以后,不能再次delete,否则会报错;
(4)此时如果误用p指针,仍然可以修改内存的值和从该处取出数值,但此时数据不受保护,该内存空间可能被重新被分配给别的变量;
(5)如果p指向的空间再次被new函数分配,即使是分配给别的指针,即使分配大小与原来不一样,p又恢复了效力,可以改变内存的值,甚至可以重新被delete,p的作用与新分配的指针一样;
(6)删除为空(NULL)的指针是不会有任何问题的吗?确实是如此。