拷贝构造函数,由 编译器 调用来完成一些基于同一类的其他对象的构建及初始化。
1.构造函数调用规则
构造函数调用规则如下:
默认情况下,c++编译器至少给一个类添加3个函数
- 默认构造参数(无参,函数体为空)
- 默认析构参数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
- 如果用户定义有参构造函数,c++不再提供默认无参构造,但是回提供默认拷贝构造
- 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
(自己写的高级构造函数会屏蔽编译器提供的低级构造函数)
2.浅拷贝与深拷贝
- 浅拷贝:简单的赋值操作
- 深拷贝:在堆区中重新申请空间,进行拷贝操作
案例:发生在默认拷贝构造函数中的浅拷贝
#include<iostream>
using namespace std;
class person
{
public:
person()
{
m_age = 0;
cout << "person的默认构造函数"<<endl;
}
person(int age,int height)
{
m_age = age;
cout << "person的有参构造函数调用" <<m_age<< endl;
m_height = new int(height);//为形参height在堆区中new出一个地址,用指针接收
}
~person()
{
cout << "person的析构函数调用" << endl;
}
int m_age;
int* m_height = NULL;
};
void test01()
{
person p1(18,160);
cout << "p1的年龄为: " << p1.m_age <<"p1的身高为: "<<p1.m_height<< endl;
person p2(p1);//没有自定义的拷贝构造函数,则为默认拷贝函数
cout << "p2的年龄为: " << p2.m_age << "p2的身高为: " << p2.m_height << endl;
//输出 p1 和 p2 身高的地址
}
int main() {
test01();
system("pause");
return 0;
}
运行结果如下图:
注意 :我们自己只声明了构造函数和析构函数,并没有声明拷贝函数,则使用了默认的拷贝函数
然后这里发现p1和p2的身高的地址是相同的,说明两个指针 p1.m_height 和 p2.m_height 指向了同一处地方的数据。
接下来,修改完善一下析构函数(添加 释放new出的在堆区中的内存 的操作)
~person()
{
//析构代码,将堆区开辟的数据做释放操作
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "person的析构函数调用" << endl;
}
然后运行结果如下图:
此时程序已经崩掉了,按道理应该还会打印一个 "person的析构函数调用"
原因:1.首先我们知道两个指针 p1.m_height 和 p2.m_height 指向了同一处地方的数据(height),
2.当我们调用 test01函数 时,先构造了 类p1,然后拷贝构造 p2,因为 test01函数 是在栈区(后进先出)上运行的,当执行完操作后,程序销毁时就会先调用p2的析构函数,将在堆区中存放m_height的内存释放掉,
3.接着,再执行p1的析构函数,重复将在堆区中存放m_height的内存释放掉,问题就出在这里
这就是浅拷贝引发的问题 ,在拷贝构造时,它只是单纯地将new int(height)(即p1.m_height)复制给p2.m_height,最后导致重复delete内存。
解决办法: (利用 深拷贝 解决)
自己实现拷贝构造函数(在类person中添加下面函数) , 解决 浅拷贝 带来的问题
person(const person& p) //传进的p就是p1
{
cout << "person 拷贝构造函数调用" << endl;
m_age = p.m_age;
//m_height=p.m_height ; 编译器默认实现这行代码(浅拷贝)
//深拷贝操作:在堆区中新new一块内存存放*p.m_height
m_height = new int(*p.m_height);
}
运行结果如下图:
可以发现,程序正常运行,且两个地址p1.m_height 和 p2.m_height 不同,其中存放的数据都是160