定义
拷贝构造函数又被叫做复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。
背景
在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”):
- 一个对象作为函数参数,以值传递的方式传入函数体;
- 一个对象作为函数返回值,以值传递的方式从函数返回;
- 一个对象用于给另外一个对象进行初始化(常称为赋值初始化);
如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是拷贝构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的。
特性
- 拷贝构造函数是构造函数的一个重载形式。
- 拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。
#include<iostream> using namespace std; class Date{ public: Date(int year = 1900, int month = 1, int day = 1){ _year = year; _month = month; _day = day; } Date(const Date& d){ _year = d._year; _month = d._month; _day = d._day; } private: int _year; int _month; int _day; }; int main() { Date d1; Date d2(d1); return 0; }
-
3.若未显示定义拷贝构造函数,系统会生成默认的拷贝构造函数。
浅拷贝和深拷贝:
所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。
大多情况下“浅拷贝”已经能很好地工作了,但是一旦对象存在了动态成员,那么浅拷贝就会出问题了,如下:
class Date{
public:
Date(int year = 1900, int month = 1, int day = 1){
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main() {
Date d1(2001,29,1);
// 这里d2调用的默认拷贝构造完成拷贝,d2和d1的值也是一样的。
//此时调用浅拷贝完全可以解决拷贝问题,但当加入动态成员变量时,浅拷贝就不能很好的解决问题
Date d2(d1);
return 0;
}
-
using namespace std; class Rect{ public: Rect(){ p = new int(50); } ~Rect(){ assert(p!=NULL); delete p; } private: int width; int height; int *p; }; int main(){ Rect rect1; Rect rect2(rect1); return 0; }
第二段代码在运行定义rect1时,由于构造函数有一个动态的分配语句,在使用rect2拷贝rect1时,由于进行的是浅拷贝,只是将值赋值给了rect2,两个指针指向的就是同一块内存,在调用析构函数的时候,两个对象调用两次析构函数对同一块地址空间进行销毁,这就是出错的原因。
深拷贝:
每个对象共同拥有自己的资源,必须显式提供拷贝构造函数和赋值运算符。
class Rect{
public:
Rect(){
p = new int(50);
}
Rect(const Rect& r){
width = r.width;
height = r.height;
p = new int(50);
*p = *(r.p);
}
~Rect(){
assert(p!=NULL);
delete p;
}
private:
int width;
int height;
int *p;
};
int main(){
Rect rect1;
Rect rect2(rect1);
return 0;
}
此时的rect1和rect2各自指向一块内存空间,不会发生上述的情况