我们知道,对一个已知对象进行拷贝,编译系统会自动调用拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数。
我们先来看一个测试案例:
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student() {
cout << "无参构造函数" << endl;
}
Student(int age, int id) {
m_Age = age;
m_Id = new int(id);
cout << "有参构造函数" << endl;
}
~Student() {
cout << "析构函数调用" << endl;
}
int m_Age;
int *m_Id;
};
void test01()
{
Student s1(15, 01);
cout << "s1的年龄为" << s1.m_Age << " s1的学号为" << *s1.m_Id << endl;
Student s2(s1);
cout << "s2的年龄为" << s2.m_Age << " s2的学号为" << *s2.m_Id << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
现在,我们运行出来的结果和想象当中一样,看似没有什么问题。但事实上,是这当中是存在问题的,id是我们在堆区new出来内存,这需要我们手动释放,但是我们上面并没有将其释放。那接下我们将代码稍微改一下,在析构函数当中添加代码,将其释放
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student() {
cout << "无参构造函数" << endl;
}
Student(int age, int id) {
m_Age = age;
m_Id = new int(id);
cout << "有参构造函数" << endl;
}
~Student() {
//析构函数的作用就是将堆区开辟出来的内存释放
if (m_Id != NULL)
{
delete m_Id;
m_Id = NULL; //将指针置空,防止野指针出现
}
cout << "析构函数调用" << endl;
}
int m_Age;
int *m_Id;
};
void test01()
{
Student s1(15, 01);
cout << "s1的年龄为" << s1.m_Age << " s1的学号为" << *s1.m_Id << endl;
Student s2(s1);
cout << "s2的年龄为" << s2.m_Age << " s2的学号为" << *s2.m_Id << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
这样看起来这代码似乎就比较规范了,但是我们一运行,会发现程序中断,直接崩溃。
百因必有果,这个错误的原因就是:两个对象的Id指针成员所指向的内存相同,Id指针被分配一次内存,但是程序结束时该内存被释放了两次!
这是由于编译器在我们没有自己定义拷贝构造函数时,会在拷贝对象时调用默认拷贝构造函数,进行的是浅拷贝!
什么是浅拷贝,浅拷贝就是对指针Id拷贝后会出现两个指针指向同一个内存空间。
所以,成员有在堆区开辟的,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了悬空指针问题和内存重复释放问题的发生。
所以我们应该将代码改成这个样子:
#include <iostream>
#include <string>
using namespace std;
class Student
{
public:
Student()
{
cout << "无参构造函数" << endl;
}
Student(int age, int id)
{
m_Age = age;
m_Id = new int(id);
cout << "有参构造函数" << endl;
}
Student(const Student &s)
{
cout << "拷贝构造函数!" << endl;
//如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
m_Age = s.m_Age;
m_Id = new int(*s.m_Id);
}
~Student()
{
//析构函数的作用就是将堆区开辟出来的内存释放
if (m_Id != NULL)
{
delete m_Id;
m_Id = NULL; //将指针置空,防止野指针出现
}
cout << "析构函数调用" << endl;
}
int m_Age;
int *m_Id;
};
void test01()
{
Student s1(15, 01);
cout << "s1的年龄为:" << s1.m_Age << " s1的学号为:" << *s1.m_Id << endl;
Student s2(s1);
cout << "s2的年龄为:" << s2.m_Age << " s2的学号为:" << *s2.m_Id << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
执行结果
总结一下:浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝对指针和内容都进行拷贝,经深拷贝后的指针是指向两个不同地址的指针
再说一句:如果成员有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题