拷贝构造
创建对象时使用同类对象来进行初始化,这时所用的构造函数称为拷贝构造函数(Copy Constructor),拷贝构造函数是特殊的构造函数
就是使用一个已经存在的对象去创建一个新的对象。新的对象是旧的对象的一份拷贝。
- 拷贝构造函数其实是一个构造函数的重载。
- 拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
- 若未显示定义,系统会默认缺省的拷贝构造函数。默认生成(浅拷贝:一个字节一个字节拷贝)依次拷贝类成员进行初始化。
//使用默认的拷贝构造(浅拷贝)
#include<iostream>
using namespace std;
class Data
{
public:
//带参的缺省的构造函数
Data(int year=1900,int month=1,int day=1)
{
_year=year;
_month=month;
_day=day;
}
void Display()
{
cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
}
~Data()//析构函数
{
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Data d1(2018,1,1);//调用构造函数
Data d2(d1);//调用拷贝构造函数
//Data d2=d1 和上面是等价的
d1.Display();
d2.Display();
return 0;
}
[zyc@localhost lession_class]$ ./a.out
2018-1-1
2018-1-1
结果分析:
利用d1.我们拷贝构造了一个对象d2。
默认拷贝构造的缺陷
来看下面例子
#include<iostream>
#include <stdlib.h>
using namespace std;
class Array
{
public:
//构造函数
Array()
{
cout<<"Array()"<<endl;
_ptr=(int *)malloc(2*sizeof(int));
}
//这里的析构函数需要完成清理工作(释放空间)
~Array()
{
cout<<"~Array()"<<endl;
free(_ptr);
_ptr=NULL;
}
private:
int *_ptr;
};
int main()
{
Array ptr;
return 0;
}
编译通过,调用一次构造函数,一次析构函数
[zyc@localhost lession_class]$ ./a.out
Array()
~Array()
类在我们没有定义拷贝构造函数的时候,会默认定义默认拷贝构造函数,也就是说可以直接用同类型的类间可以相互赋值、初始化:
int main()
{
Array p1;
Array p2=p1;
return 0;
}
这个时候,编译器机会报错,原因就是因为同一块空间被释放了两次
类的默认拷贝构造函数只会用被拷贝类的成员的 值 为拷贝类简单初始化,也就是说二者的p指针指向的内存空间是一致的。以前面Array可以知道,编译器为我们默认定义的拷贝构造函数为:
Array(const Array& Array)
{
p1 = Array.p1;
pp2 = Array.p1; //两个类的p指针指向的地址一致。
}
main函数将要退出时,拷贝类p2的析构函数先得到执行,它把自身p指向的堆空间释放了;接下来,p1的析构函数得到调用,被拷贝类p1的析构函数得到调用,它同样要去析构自身的p指向指向的堆空间,但是该空间和p2类中p指向的空间一样,造成重复释放,程序运行崩溃。
为了解决这种问题,我们可以使用自定义拷贝构造函数,里面用深度拷贝的方式为拷贝类初始化
深拷贝原理
关于深拷贝后面再介绍。
自定义拷贝构造函数,也可以定义成简单的值拷贝,即浅拷贝。
class Data
{
public:
//带参的缺省的构造函数
Data(int year=1900,int month=1,int day=1)
{
_year=year;
_month=month;
_day=day;
}
Data(const Data& d)//拷贝构造函数,这里必须使用传引用
{
this->_year=d._year;
this->_month=d._month;
this->_day=d._day;
}
void Display()
{
cout<<_year<<"-"<<_month<<"-"<<_day<<endl;
}
~Data()//析构函数
{
}
private:
int _year;
int _month;
int _day;
};
拷贝构造函数 需要注意的问题
拷贝构造函数的参数必须使用引用传参,使用传值方式会引发无穷递归调用。
传引用,否则无限递归
举例:假设是传参时使用传值方式。
class Data
{
public:
//构造函数
Data()
{
cout<<"Data()"<<endl;
}
//拷贝构造函数,以传值方式
Data(const Data d )
{
cout<<"Data(const Data &d)"<<endl;
}
~Data()
{
cout<<"~Data()"<<endl;
}
private:
int a;
};
int main()
{
Data d1;
Data d2=d1;
return 0;
}
运行结果:会提示报错信息。编不过
[zyc@localhost lession_class]$ g++ tmp.cpp -g
tmp.cpp:21: error: invalid constructor; you probably meant ‘Data (const Data&)’
原因:
将传值应用更改成传引用。
Data(const Data &d )
运行结果
[zyc@localhost lession_class]$ ./a.out
Data()
Data(const Data &d)
~Data()
~Data()