什么是拷贝构造函数?
通过拷贝对象的方式创建一个新的对象,拷贝构造函数的参数必须是类对象的引用,也就是将一个对象拷贝给另一个新建的对象(用途,在创建对象的时候,使用同一类之前创建的对象来初始化新创建的对象)
Book(Book &b);//必须是引用的原因是,如果是传值方式将实参传递给形参,中间要经历一个对象的拷贝,对象拷贝由必须调用拷贝构造函数,这样就形成一个死循环,无解
拷贝构造函数第一个参数一定是对象的引用,后面如果有其他参数,要给出默认值,否则就缺省不写
Book(Book &b,price = 5.0);
如果没有显示的声明一个拷贝构造函数,系统会自动为类生成一个拷贝构造函数,自动生成的仅仅将对象的所有成员变量复制给当前创建的对象(浅拷贝)
为了一个类中包含动态分配存储空间的指针类型的成员变量时,就必须为这个类设计一个拷贝构造函数,还要为他添加一个赋值操作符重载函数,重载(=)
为了不让对象发生拷贝行为,我们可以显示声明一个拷贝构造函数,并将其设置为private属性
什么是浅拷贝?
数据成员之间的简单复制,系统默认的拷贝构造函数实现的就是浅拷贝
默认的拷贝构造函数实现对数据成员一一赋值,但是如果类中含有指针类型的数据,这种拷贝只是将指针简单的拷贝,并没有分配新的内存
运算符“=”的重载就是实现浅拷贝的原因,由于对象之间含有指针数据类型,a , b恰好指向同一块内存,就是浅拷贝
当两个对象的指针指向同一块内存时,调用第一个的析构函数会释放一次内存,调用第二个又会把这个释放过的内存再次释放一次
对同一个动态内存释放两次以上结果时未定义的,容易导致内存泄露和程序崩溃。此时就需要深拷贝解决问题
注意:浅拷贝带来问题的本质在于析构函数释放多次堆内存,使用std::shared_ptr,可以完美解决这个问题。
什么是深拷贝?
为赋值对象申请了一个新的内存
当拷贝对象中有其他资源(堆)的引用时,对象另开辟一块新的资源,而不再对拷贝对象有其他资源的引用的指针或引用进行单纯的赋值
为了解决浅拷贝的问题,必须显示的定义拷贝构造函数,不但可以复制数据成员,还可以为对象分配各自的内存空间
深拷贝不仅对指针进行拷贝,对指针指向的内容也拷贝,也就是给对象新分配了一块内存,经过深拷贝后的指针是指向不同地址
对于普通成员的赋值,深拷贝浅拷贝都是一样的。
参见C++ primer第5版P448页
/* 深拷贝浅拷贝 */
#include<iostream>
using namespace std;
class A
{
public:
A(int x) : data(x){}//构造函数初始化
A(){}
public:
int data;
};
class B
{
public:
B(int x) : num(x){data = new int[num];}//构造函数初始化
B(){};
B(const B &X) : num(X.num){data = new int[num];}//深拷贝,增加了这一句拷贝构造函数,程序就不会崩溃
~B() {delete [] data;}
public:
int *data;
int num;
};
int main()
{
A a(5);//用5初始化
A b = a;//数据成员之间的赋值
cout<< b.data<< endl;//输出为5
cout<< a.data<< endl;//输出为5
B c(5);//用5初始化
B d = c;//对象拷贝,数据成员之间的赋值
cout<< c.data<< endl;//输出为0x7ff5b9402a40
cout<< d.data<< endl;//输出为0x7ff5b9402a40 指向了同一块内存,调用析构函数的时候造成内存泄露
return 0;
}