拷贝构造函数和构造函数不能分开说,他们都是初始化对象的一种方法。但是我们这里用构造函数辅助说明拷贝构造函数,主要说说拷贝构造函数的声明,用途和使用注意事项。
众所周知,构造函数是一个初始化类对象的函数,即使不显示调用,编译器也会隐式调用构造函数初始化类对象。同样的,拷贝构造函数是一种特殊的构造函数,目的也是初始化类对象,同样在不声明的情况下也会隐式调用该函数。而隐式调用拷贝构造函数的时候,我们称之为“浅拷贝”。但是,请注意一点,并不是说显示调用就是“深拷贝”,而是如果要深拷贝一定要显示调用。
深浅拷贝后文会说,现在我们着重考虑其用途:
#include <iostream>
using namespace std;
class A{
private:
int a;
public:
A(int b):a(b){
cout<<"构造函数"<<endl;
}
A(const A& c){
a = c.a;
cout<<"拷贝构造函数"<<endl;
}
~A(){
cout<<"析构函数"<<endl;
}
};
int main(){
A a(100); //调用构造函数
A b = a; //调用拷贝构造函数
return 0;
}
看图,当创建了一个对象a时,编译器调用构造函数将a初始化为100。当又创建了一个对象b,将a赋值于b,此时调用拷贝构造函数将b初始化。最后由于调用了一次构造函数一次拷贝构造函数,所以析构函数被调用两次。
那么将主函数部分稍作修改,改成:
int main(){
A a(100);
A b(200);
b = a;
return 0;
}
会怎样呢?
可见,当我们对函数初始化完成,就不会再调用拷贝构造函数了,这时候b=a就是简单的赋值运算操作,和拷贝构造无关。
程序稍作改动,当类对象以参数的形式存在在普通函数中,会怎么样?
#include <iostream>
using namespace std;
class A{
private:
int a;
public:
A(int b):a(b){
cout<<"构造函数"<<endl;
}
A(const A& c){
a = c.a;
cout<<"拷贝构造函数"<<endl;
}
~A(){
cout<<"析构函数"<<endl;
}
void newA(A b){
cout<<"bbbbb"<<endl;
}
};
int main(){
A a(100);
a.newA(a);
return 0;
}
理论上来说,调用函数newA(a)会利用构造函数构造一个新的b对象,实际上呢?
我们看到,它调用的是拷贝构造函数。为什么?
不知道,我们就改动一下程序,直接查看b对象中a的值:
void newA(A b){
cout<<b.a<<endl;
}
奇怪了,我们明明没有对b初始化,它却生成了b=100。是这样么?当然不是。
我们传的参数是a,而a已经被初始化为100。这时候形参是 A b = a 的。是不是看出点东西了。
所以,在调用void newA(A b){}的时候,实际上是创造了一个形参,这个形参被拷贝构造函数初始化为已经被构造函数初始化的a初始化。说的别扭,就上图。
总结一下,什么时候调用拷贝构造函数?就是当使用“=”赋值的时候,需要用到拷贝构造函数。
关于深浅拷贝,请看:深拷贝与浅拷贝