我们都知道C++有浅拷贝和深拷贝的概念。默认情况下,用户编写一个类,而没有编写拷贝构造函数的时候,C++编译器会生成一个默认的拷贝构造函数,这个默认的拷贝构造函数只进行浅拷贝,如果类中包含指针,文件句柄,socket等资源的时候,这些资源就只有一个副本。拷贝构造函数就是用来给用户提供一个初始化接口,实现对象资源的深拷贝。还有一个拷贝赋值的概念:一个对象给另外一个对象赋值的时候,默认赋值运算操作只是进行浅拷贝,用户可以实现自己的拷贝赋值运算符,以便进行对象赋值的深拷贝。
拷贝构造函数的定义如下:
T(const T& other) {...}
拷贝赋值运算符的定义如下:
T& operator=(const T& other) {...}
什么时候使用拷贝构造函数,什么时候使用拷贝赋值运算?这个是根据拷贝操作时,目的对象是初始构造还是已经存在的了。如果是初始构造,则调用拷贝构造函数;如果是已经存在的了,则调用拷贝赋值操作符:
(1)T a1; T a2(a1); //使用a1对象来初始化a2对象,a2处于初始构造状态,因此调用拷贝构造函数;
(2)T a1; T a2; a2 = a1; //a1赋值给a2的时候,a2已经构造完成了,因此调用拷贝赋值运算符;
(3)当对象作为参数传递的时候,由于赋值时目的对象刚刚开始构造,因此调用拷贝构造函数:
void func(T t) {...}
(4)特别注意:当对象作为函数返回值的时候,需要视目的对象的存在状态而定,如果目的对象处于构造状态,则不调用任何构造函数;如果目的对象已经被构造,则调用拷贝赋值运算符函数。传统上认为函数返回一个对象的时候会调用拷贝构造函数生成一个匿名对象,这点我持保留意见。从实际的测试来看,编译器并没有构造这个匿名对象(可能跟c++的编译器/版本有关,我使用的是g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609,并且设置优化选项为-O0,有待验证):
#include <iostream>
using std::cout;
using std::endl;
class A {
public:
A() {cout << "Default constructor" << endl;}
A(const A& other) {cout << "Copy constructor" << endl;}
//~A() {cout << "Destructor" << endl;}
A& operator=(const A& other) {cout << "Copy Assignment operator" << endl;}
int count;
};
// Parameter pass
int func1(A other) {
return 0;
}
// return value
A func2() {
A a;
return a;
}
int main(void) {
A a; // Default constructor
A b; // Default constructor
A c(a); // Copy constructor
b = c; // Copy Assignment
// Parameter pass
func1(a); // Copy constructor
// return value
A d = func2(); // Default constructor
a = func2(); // Default constructor & Copy assignment
return 0;
}
运行结果如下:
Default constructor
Default constructor
Copy constructor
Copy Assignment operator
Copy constructor
Default constructor
Default constructor
Copy Assignment operator