13.拷贝构造函数
1.拷贝构造函数
- 1.拷贝构造函数的作用:本质上拷贝构造函也是一种构造函数
- 2.拷贝构造函数的调用时机:与普通的构造函数不同的是,当用一个对象创建另一个对象时才调用拷贝构造函数
- 3.缺省拷贝构造函数的作用:如果我们自己没有书写拷贝构造函数,默认情况下系统会创建默认拷贝构造函数,默认的拷贝构造函数就是完全将对象1拷贝给对象2,以memcpy的形式
- 4.如何自己创建拷贝构造函数
//前面已有CStudent的构造函数
class CStudent
{
public:
CStudent(){
}//构造函数
//拷贝构造函数
CStudent(CStudent& obj)
{
printf("拷贝构造函数");
}
//CStudent(CStudent& obj) = delete;//禁用默认的拷贝构造函数
//CStudent(CStudnet& obj) = default;//使用默认的拷贝构造函数
};
int main()
{
CStudent stu1;
CStudent stu2 = stu1;//使用stu1这个对象创建stu2时,调用默认拷贝构造函数
}
2.拷贝构造函数与深拷贝和浅拷贝
先看一个例子:
class CStudent
{
public:
//构造函数
CStudent()
{
//在构造函数中为m_szName开辟了堆空间(分配动态内存)
//因为我们这里开辟了堆空间,因此函数成员中有了需要释放的资源
m_szName = (char*)malloc(255);
}
//析构函数
~CStudent(){
}
/*自定义的拷贝构造函数
CStudent(CStudent& obj)
{
}
*/
private:
int m_nStuID;
char* m_szName;
};
int main()
{
CStudent stu1;
//使用一个对象构建另一个对象时,如果没有自定义的拷贝构造函数,系统会调用默认的拷贝构造函数
CStudent stu2(stu1);
reutrn 0;
}
大家有没有发现程序中存在问题
- 在CStudent stu1;时我们调用了构造函数,开辟了堆空间
- 在CStudent stu2(stu1);时,由于我们没有自定义拷贝构造函数,系统调用了默认的拷贝构造函数,默认拷贝构造函数是浅拷贝,即仅仅是将stu2指向了与stu1相同的内存空间,并没有开辟新的内存空间,然后再将stu1的内容copy到stu2中
- 当main函数执行结束,将要退出局部作用域时,此时会调用析构函数,首先进行stu1的析构,释放了之前开辟的堆空间,然后进行stu2的析构,但是这时就会遇到问题,因为我们之前使用的是默认的拷贝构造函数,默认拷贝构造函数并没有开辟新的堆空间用来存放stu2,而是将stu2指向了与stu1相同的内存空间,此时就会造成空间的重复释放,因此会造成错误
- 出现错误的原因是:默认构造函数是浅拷贝,并未开辟新的内存空间,在函数退出时调用析构函数造成了资源空间的重复释放
何时我们应该自己定义拷贝构造函数,何时我们应该采用系统默认的拷贝构造函数呢?
答:当对象中的成员函数存在一种需要分配的资源时,为了避免调用默认拷贝构造函数时的浅拷贝问题,造成析构时对资源空间的重复释放,我们需要重写拷贝构造函数或禁用
如何重写拷贝构造函数?
class CStudent
{
public:
//构造函数
CStudent()
{
//在构造函数中为m_szName开辟了堆空间(分配动态内存)
//因为我们这里开辟了堆空间,因此函数成员中有了需要释放的资源
m_szName = (char*)malloc(255);
}
//析构函数
~CStudent(){
}
//自定义的拷贝构造函数
CStudent(CStudent& obj)
{
//这种是深拷贝的做法:创建堆空间
m_szName = (char*)malloc(255);
if(m_szName == nullptr)
{
return;
}
//然后将内容拷贝到新开辟的堆空间
memcpy(this->m_szNmae,obj.m_szName,255);
//浅拷贝如何做的:
//this->m_szName = obj.m_szName;
}
private:
int m_nStuID;
char* m_szName;
};
int main()
{
CStudent stu1;
//使用一个对象构建另一个对象时,如果没有自定义的拷贝构造函数,系统会调用默认的拷贝构造函数
CStudent stu2(stu1);
reutrn 0;
}