class A
{
A();
A(A a);
A(A& a);
A(const A& a);
}
上面类A中4个构造函数可以说是重载,理论上没有错误,但是一般编译器会提示A(A a)有错“A类型的复制构造函数不能带有A类型的参数”,假如去掉编译器的这个错误提示,语法上来说是没问题的。如果只有A(A a)这个构造函数时,在构造过程中会产生死循环,直到内存资源耗尽,原因:值传递过程中必然产生临时对象,恰恰这个临时对象又是A类型且带A类型的参数,必然一直调用复制构造函数一直循环下去,如果参数是引用,那就不会产生死循环了,因为参数为引用,那么参数在接受传值的过程中不会产生临时对象,如有疑问,可以看这里对引用、临时对象、值传递的举例说明http://www.cnblogs.com/bigshow/archive/2008/11/10/1330514.html。
当然如果非要实现自己复制构造函数为值传递,可以多加一个参数(表面上看似不会产生死循环,实际骨子里临时对象调用的A(A& a)且A(A& a)显示定义的情况下,而名义上两个参数的这个所谓的传值复制构造函数根本无意义,或者可以说这已经不是复制构造函数了,除非很特殊的时候也许会用到,但不知道什么时候会用到这样的构造函数),其实这已经钻牛角尖了,就不讨论了。
而传引用的两个复制构造函数不同的是一个有const一个没,在编译的时候会警告两个复制构造,但不影响代码生成(至少VS2010上试验是这样),需要调用复制构造函数的时候调用者会调用谁呢?如果传的实参是const A&类型的,那肯定调用带const参数的那个复制构造,否则会调用顺序在前面的那个引用。当然实际自己定义的时候没有特殊需求最好不要出现这样的两个复制构造,通常我们用到复制构造函数都是为了引用源地址里的变量值或者复制源地址里成员的东西,而不去改变源地址里的东西,所以定义都是带const引用的那个,还有编译器默认的复制构造函数也是它。
疑问?如果我们有A c;A b(c);你会不会问 A b(c);这不就是调用的A(A a)么,看清楚,正确或者通常情况下我们定义或者使用默认的都是A(const A& a)这个复制构造,当然调用的是A(const A& a)了,内部代码(a = c看似是值传递,别忘了引用也是这个样子传递的),何来A(A a)的调用,这下应该清楚了吧。
为什么很多时候复制构造函数最好我们自己定义而不去用默认的复制构造?默认的复制构造函数就是把源地址里成员变量的值复制一份到当前对象的对应成员变量里,貌似叫做浅拷贝,行吧。如果我们的类成员变量是指针,如果源地址对象里的指针成员已经指向了动态分配的内存,而拷贝到当前对象里不过是指针的值,那就是说当前对象里的成员变量指针指向了源地址里动态分配的内存,那么我们如果在当前对象里对成员指针操作不慎就会修改到源地址里分配的空间里的数据。。。。如果我们在不做深拷贝的情况下,这已经非常危险了,要避免也可以 ,自己定义一个私有的空拷贝构造函数(什么都不做),这样就不会出现浅拷贝的情况了。另外如果我们希望是拷贝一份指针所指的数据,那么我们得重新自己动态分配内存,然后把源地址里面动态内存里的数据复制一份过来(貌似这叫深拷贝),这肯定得我们自己设计拷贝构造函数了。故我们很多时候自己显示定义并完善我们自己的复制构造xx(cons xx &){}来达到我们的预期.
总结:要用到复制构造,我们最好自己手动定义自己的xx::xx(cons xx &){}