最近有人来问复制构造函数和重载值赋值运算符分别在什么时候会被调用。这里理了一下思路。
复制构造和重载赋值运算符
直接上一段代码:
class Dot {
private:
int x;
int y;
public:
Dot();
~Dot();
// Copy Constructor
Dot(const Dot& other) {
this->x = other.x;
this->y = other.y
}
// Override Copy
Dot &operator= (Dot const &other) {
this->x = other.x;
this->y = other.y;
}
}
可以看到在Dot
类中定义了两个函数,前一个是复制构造函数,后一个是重载赋值运算符。那么他们对应在什么时候会被调用呢?
Dot a; // 1
Dot b(a); // 2
Dot c = a; // 3
Dot d = Dot(a); // 4
b = a; // 5
b = Dot(a); // 6
对应上述代码,有:
- 调用默认构造函数(Default Constructor),实例化对象
a
; - 调用复制构造函数,用
a
去初始化b
; - 调用复制构造函数,用
a
去初始化c
; - 调用复制构造函数,用
a
去初始化d
; - 调用重载赋值运算符函数,用
a
赋值给b
; - 调用复制构造函数,创建临时对象(这里记作
tmp
),用a
去初始化tmp
,再调用重载赋值运算符函数,用tmp
赋值b
,之后tmp
析构。
因此不难看出,对象实例化必然调用复制构造函数(2~4),之后的赋值调用重载赋值运算符函数(5~6)。
但值得一提的是,3和4是很容易引起误解的。一些师傅们会觉得是先调用了默认构造函数,然后通过赋值运算进行值传递。然鹅并不是嗷,他们全都会被编译成复制构造。