拷贝构造函数被调用的几种情况:

  1. 当用类的一个对象去初始化该类的另一个对象(或引用)时,系统自动调用拷贝构造函数实现拷贝赋值。
  2. 若函数的形参为类对象,调用函数时,实参赋值给形参,系统自动调用拷贝构造函数。(这里有可能被编译器优化)
  3. 当函数的返回值是类对象时,系统自动调用拷贝构造函数。(注意编译器可能会进行优化,而观察不到拷贝的发生)
#include<ctime>
#include<cstdlib>
#include<iterator>
#include<algorithm>
#include<iostream>
#include<numeric>
using namespace std;

class A {
public:
	A():data(0){}
	A(const A& a){
		data = a.data;
		cout << "拷贝构造函数调用\n";
	}
	A& operator=(const A&a){
		data = a.data;
		cout << "调用赋值函数\n";
		return *this;
	}

	int data;
};

void fun1(A a) {
	return ;
}

A fun2() {
	A a;
	return a;
}

int main() {
	A a;
	A b(a);          // 用类的一个对象 a 去初始化另一个对象 b
	A c = a;         // 用类的一个对象 a 去初始化另一个对象 c,注意这里是初始化,不是赋值
	fun1(a);         // 形参为类对象,实参初始化形参,调用拷贝构造函数
	A d = fun2();    // 函数返回一个类对象时,这里可能会被编译器优化,从而可能没有调用拷贝构造
	d = a;           // d 已经初始化过了,这里是赋值,调用赋值函数

	return 0;
}

需要注意,在第三条:当函数的返回值是类对象时,系统自动调用拷贝构造函数。这里默认情况下一般会被编译器优化,减少不必要的拷贝构造,所以,具体的返回值可能会因编译器及编译选项的不同而不同。使用 g++ 编译器,关闭优化 g++ xxx.cpp -fno-elide-constructors 后执行结果如下(-fno-elide-constructors 选项就是用来关闭拷贝优化的):

拷贝构造函数调用
拷贝构造函数调用
拷贝构造函数调用
拷贝构造函数调用
拷贝构造函数调用
调用赋值函数

默认情况下,优化后的结果如下:

拷贝构造函数调用
拷贝构造函数调用
拷贝构造函数调用
调用赋值函数

编译器具体是怎么优化的,一般编译器会先看支不支持拷贝优化,如果不支持,再看有没有定义移动构造函数,如果都没有,就调用拷贝构造函数。更具体的细节可以参考移动语义及拷贝优化的内容。

最后,注意 A c=a;d=a; 的不同,一个是初始化,一个是赋值。