深浅拷贝区别
上一节简单提了编译器会默认给我们提供值拷贝构造函数,结果是新的对象会拥有和传入对象一样的属性,由编译器提供的拷贝构造被称为浅拷贝构造,而由我们自己编写的不同于编译器提供的拷贝构造函数就叫深拷贝构造了,举个典型的例子说明。
代码解释
#include<iostream> using namespace std; //深浅拷贝问题,存在经典的坑,面试考过 class Person { public: Person(int age,int height) { m_age = age; m_Height = new int(height); cout << "Person 的有参构造函数调用" << endl; } //自己写不同于编译器的拷贝构造函数属于深拷贝 Person(const Person& p) { cout << "拷贝构造函数调用" << endl; m_age = p.m_age; //m_Height = p.m_Height; /*编译器默认执行上行代码 新开辟的地址相同,会导致调用析构函数时违法操作,无法访问内存*/ m_Height =new int(*p.m_Height); } ~Person() { //析构代码,将堆区开辟的数据做释放操作 if (m_Height != NULL) { delete m_Height; m_Height = NULL; } cout << "~Person 的析构构造函数调用"<<endl; } int m_age; int* m_Height; }; void test() { Person p1(20,180); Person p2(p1); cout << "p2.age= " << p2.m_age << " p2.height=" << *p2.m_Height << endl; } int main() { test(); system("pause"); }
创建Person类,设置m_age和指针类型*m_Height为私有属性;依次对Person类设置有参构造和拷贝构造函数以及析构函数;前面析构函数一直没有什么作用,其实它是用来清理对象的,析构函数会在程序结束前自动调用,这时候就可以使用delete清理掉;
特别注意
Person(const Person& p) { cout << "拷贝构造函数调用" << endl; m_age = p.m_age; //m_Height = p.m_Height; /*编译器默认执行上行代码 新开辟的地址相同,会导致调用析构函数时违法操作,无法访问内存*/ m_Height =new int(*p.m_Height); } ~Person() { //析构代码,将堆区开辟的数据做释放操作 if (m_Height != NULL) { delete m_Height; m_Height = NULL; } cout << "~Person 的析构构造函数调用"<<endl; }
这里不能使用编译器提供的浅拷贝,如果直接使用m_Height=p.m_Height,毫无疑问这两个属性地址相同,那么在调用析构函数的时候,p1先释放内存,这时候虽然有一个NULL判断,但是此块内存已经被删除,再次访问都会提示错误,这是很危险的,所以我们需要用深拷贝解决重复删除的问题。使用m_Height=new int(*p.m_Height) 语句给身高属性重新开辟空间,这样在调用析构的时候各自清理各自的属性,就解决了这个浅拷贝带来的重复清理问题。
内存图解释
上面是浅拷贝的p1、p2对象的内存示意图,两次析构会重复当问0x00011地址,但是当这个地址被删除后,是不允许再次访问的。
利用我们设置的深拷贝构造后,地址不一样,各自删除各自的地址,解决问题
初始化列表
初始化列表用来给属性初始化
语法
普通构造函数+ : + 类属性(变量或常量)+ {}
具体实现
class Person { public: Person() :m_age(20), m_sex(1), m_height(180){} Person(int a, int b, int c) :m_age(a), m_sex(b), m_height(c){} int m_age; int m_sex; int m_height; }; int main() { Person p1; Person p2(10, 20, 30); cout << "年龄为:" << p1.m_age; cout << "性别为:" << p1.m_sex; cout << "身高为:" << p1.m_height<<endl; cout << "年龄为:" << p2.m_age; cout << "性别为:" << p2.m_sex; cout << "身高为:" << p2.m_height << endl;; }
主函数中p1调用无参构造函数,各属性初始化为属性()括号里面的值;p2调用有参构造函数,将实参10,20,30分别传给a,b,c,然后a的值传给m_age,b的值传给m_sex;c的值传给m_height;直接来看结果: