当一个类创建后,编译器就我们默认提供了三个函数
前提是自己没写这三个函数哈,比如用户定义了有参构造函数,编译器就不会在默认提供无参构造,若定义了拷贝构造函数,编译器将不在提供任何函数
构造函数 | 无参,函数体为空 |
析构函数 | 无参,函数体为空 |
拷贝构造函数 | 对属性进行值拷贝 |
浅拷贝会带来堆区内存重复释放
举个例子:
#include<iostream>
using namespace std;
class Person {
public:
int m_age;
int *m_height; //我们定义一个指针,为它分配内存(让它在堆区存储)
//构造函数,因为我们设置了两个成员,所以我们自己重新定义下构造函数,有可控性
Person(int age,int height) {
m_height = new int(height);
m_age = age;
cout << "person的有参构造函数" << endl;
}
Person(const Person &p) { //拷贝函数
//编译器默认提供的拷贝函数就是简单的赋值操作,即浅拷贝就是编译器提供的默认构造函数)
m_age = p.m_age;
m_height = p.m_height;
cout << "Person的拷贝函数" << endl;
}
~Person() {
//堆区开辟的内存在执行结束后,需要手动释放
if (m_height != NULL) {
delete m_height;
m_height = NULL;
}
cout << "Person的析构函数" << endl;
}
};
void test01() {
Person p1(18,160);
cout <<"年龄 "<< p1.m_age<<"身高 " <<*p1.m_height<< endl;
Person p2(p1);
cout << "年龄 " << p2.m_age << "身高 " << *p2.m_height << endl;
}
int main() {
test01();
}
上面代码存在问题:拷贝函数(浅拷贝)就是单纯的赋值操作,在赋值p1.m_height指针时其时是地址拷贝给了p2.height,后p2执行析构函数时,把地址释放了,最后p1再来执行析构函数,但它的地址还是原来的地址(已经被p2被释放了),所以浅拷贝导致堆区内存重复释放
有些人可能会问:在析构函数那里不是判断了一下,为空不是不执行吗,会啥还会导致内存重复释放嘞?
可以这样理解:p2把指针指向的内存释放了,但p1里面的指针还是指向原来的地址,但是地址引进被释放了,所以就崩了;或者理解成p1与p2里的m_height其实是不一样的
此时引出深拷贝,在堆区重新开辟一段内存,让p1和p2两个指针指向的地址不一样,但值是一样的,结束后各种释放自己的内存,互不冲突
#include<iostream>
using namespace std;
class Person {
public:
int m_age;
int *m_height; //我们定义一个指针,为它分配内存(让它在堆区存储)
//构造函数,因为我们设置了两个成员,所以我们自己重新定义下构造函数,有可控性
Person(int age,int height) {
m_height = new int(height);
m_age = age;
cout << "person的有参构造函数" << endl;
}
Person(const Person &p) { //拷贝函数
//编译器默认提供的拷贝函数就是简单的赋值操作,即浅拷贝就是编译器提供的默认构造函数)
m_age = p.m_age;
//m_height = p.m_height; 这是默认拷贝函数(浅拷贝)
m_height = new int(*p.m_height); //深拷贝,就是重新在堆区申请内存
cout << "Person的拷贝函数" << endl;
}
~Person() {
//堆区开辟的内存在执行结束后,需要手动释放
if (m_height != NULL) {
delete m_height;
m_height = NULL;
}
cout << "Person的析构函数" << endl;
}
};
void test01() {
Person p1(18,160);
cout <<"年龄 "<< p1.m_age<<"身高 " <<*p1.m_height<< endl;
Person p2(p1);
cout << "年龄 " << p2.m_age << "身高 " << *p2.m_height << endl;
}
int main() {
test01();
}