拷贝构造函数就是用 同一类型的对象复制成员值来初始化对象(当出现类的 “=” 赋值时,就会调用拷贝构造函数)
简单来说,拷贝构造函数就是来复制对象的
classname(const classname& obj){
//拷贝构造函数主体
//可访问obj的私有成员
}
例如
classname a;
classname b = a; //调用了拷贝构造函数
这里调用的是b的拷贝构造函数,而参数obj是a
所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。
class Box {
private:
int var;
public:
Box(int temp) {
var = temp;
}
int getValue() {
return var;
}
};
int main()
{
Box a(100);
Box b = a; //调用了拷贝构造函数
cout << a.getValue() << endl;
cout << b.getValue() << endl;
return 0;
}
输出:
100
100
此时,调用的是默认的拷贝构造函数
class Box {
private:
int var;
public:
Box(int temp) {
var = temp;
}
Box(const Box& obj) {
cout<<"I'm a Copy Constructor"<<endl;
}
int getValue() {
return var;
}
};
int main()
{
Box a(100);
Box b = a;//调用了拷贝构造函数
cout << a.getValue() << endl;
cout << b.getValue() << endl;
return 0;
}
而当我们加入自己定义的拷贝构造函数时,输出如下。
I'm a Copy Constructor
100
-858993460
此时,就不再调用默认拷贝构造函数,因此b中的var值并没有初始化
以上情况类成员都是简单类型,如果类带有指针变量,并有动态内存分配,则它必须有一个拷贝构造函数
class Box {
private:
int* ptr;
public:
Box(int height) { //简单的构造函数
cout << "调用构造函数" << endl;
ptr = new int; //分配内存
*ptr = height;
}
Box(const Box& obj) { //拷贝构造函数
cout << "调用拷贝构造函数" << endl;
ptr = new int;
*ptr = *obj.ptr;
}
~Box(){ //析构函数
cout << "释放内存" << endl;
delete ptr;
}
void show() {
cout << "Height: " << *ptr << endl;
}
};
int main()
{
Box a(10); //调用构造函数
Box b = a; //调用拷贝构造函数
a.show();
b.show();
return 0;
}
输出:
调用构造函数
调用拷贝构造函数
Height: 10
Height: 10
释放内存
释放内存
若仅使用默认拷贝构造函数,则系统会报错
class Box {
private:
int* ptr;
public:
Box(int height) { //简单的构造函数
cout << "调用构造函数" << endl;
ptr = new int; //分配内存
*ptr = height;
}
~Box(){ //析构函数
cout << "释放内存" << endl;
delete ptr;
}
void show() {
cout << "Height: " << *ptr << endl;
}
};
int main()
{
Box a(10); //调用构造函数
Box b = a; //调用拷贝构造函数
a.show();
b.show();
return 0;
}
因为浅拷贝仅将成员的值进行复制,即b.ptr=a.ptr
,也就是两个指针指向同一个空间。在销毁对象时,两个对象的析构函数对同一个内存空间释放两次,所以会出现错误。我们需要的不是两个p有相同的值,而是两个p指向的空间有相同的值
Box a;
Box b=a
,会调用拷贝构造函数需要注意是否需要进行深拷贝Box& b = a
,此时不会调用拷贝构造函数void func(Box b)
,调用复制构造函数,按参数的值构造一个临时对象,这个临时对象仅仅在函数执行是存在,函数执行结束之后调用析构函数。void func(Box& b)
不会调用任何构造函数。Box func()
,调用复制构造函数,返回时按参数的值构造一个临时对象,这个临时对象在函数返回后调用析构函数。当临时对象完成复制构造后,就不需要它了,会析构这个对象。最好能将临时对象的资源直接转移给构造的对象,可以使用移动构造函数。见[ C++ ] — 右值引用和移动构造函数class Box {
public:
Box();
Box(const Box& obj) {
cout << "I'm a Copy Constructor" << endl;
}
};
Box func() {
Box temp;
return temp;
}
int main()
{
func();
return 0;
}
输出
I'm a Copy Constructor
Box& func()
不调用构造函数。Box func() {
Box temp;
return temp;
}
int main()
{
func();
return 0;
}
无输出
然而
int main()
{
Box a;
a=func();
return 0;
}
此时输出
I'm a Copy Constructor
复制构造发生在a=func();
如果不想用户
则可以声明一个私有拷贝构造函数,甚至不用定义它,当用户试图这么做时会报错。
class Box {
private:
Box(const Box& obj);
...
};
void f1(Box box);
int main(){
Box a;
Box b=a; //报错
Myfunction(a); //报错
}
1、 为什么拷贝构造函数必须用引用传递,而不是值传递?
防止无限递归。classA(const classname& obj){ }
因为当使用值传递时,首先要生成obj的一个副本。而生成副本的过程还需要调用拷贝构造函数,因此这个过程会无限递归下去。
2、参数传递过程?
与内置数据类型的不同:
值传递
引用传递
3、 一个类中可以有多个拷贝构造函数吗?
可以
class Box{
public:
Box(const Box& ); //const的拷贝构造函数
Box(Box&); //非const的拷贝构造函数
};
注意,如果一个类中只存在一个参数为 X& 的拷贝构造函数,那么就不能使用const X或volatile X的对象实行拷贝初始化.
4、下列哪些是拷贝构造函数
Box(Box& other); // Avoid if possible--allows modification of other.
Box(const Box& other);
Box(volatile Box& other);
Box(volatile const Box& other);
// Additional parameters OK if they have default values
Box(Box& other, int i = 42, string label = "Box");
Box(other);
除了最后一个其他全是。参数中带该类类型的引用参数的构造函数就是。
拷贝构造函数可以有其它的参数,只要其它参数都有默认值
参考:
彻底讲明白浅拷贝与深拷贝
c++拷贝构造函数详解
菜鸟教程——C++拷贝构造函数
微软C++文档