C++拷贝构造函数
1. 概念
在现实生活中,可能存在一个与你一样的自己,我们称其为双胞胎。
那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?除了定义类的对象如何初始化之外,类还需要控制拷贝。
拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
class A{
public:
A(); //默认构造函数
A(const A& a); // 拷贝构造函数
// ...
};
2. 特征
-
拷贝构造函数是构造函数的一个重载形式。
-
拷贝构造函数的第一个参数必须是一个引用类型。
-
若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
3. 拷贝构造函数典型调用场景
我们先定义一个简单的类,为了方便学习,我们将拷贝构造函数的实现写为打印字符串"A(const A& a)"
以便我们更直观的了解拷贝构造函数的调用。
#include <iostream>
class A
{
public:
A()
{}
A(const A& a)
{
std::cout << "A(const A& a)" << endl;
}
~A()
{}
private:
int _a;
};
-
使用已存在对象创建新对象
int main() { A a1; //这两条语句都会调用拷贝构造函数 A a2(a1); // 初始化对象 d2 A a3 = a1; // 初始化对象 d3 return 0; }
注意:下面写法调用的是赋值运算符重载,不会调用拷贝构造函数。
A a1; A a2; a2 = a1; //这里不会调用拷贝构造
-
函数参数类型为类类型对象
void fun(A a) //形参是类的对象 {} int main() { A aa; fun(aa); //调用拷贝构造函数 return 0; }
-
函数返回值类型为类类型对象
A fun(A a) { return a; } int main() { A aa; fun(aa);//调用拷贝 return 0; }
4. 例题
以下代码共调用多少次拷贝构造函数:
class B{
public:
B()
{}
B(const B&)
{
std::cout << "B(const B&)" << std::endl;
}
~B()
{}
private:
int _b;
};
B f(B b){
B v(b);
B w = v;
return w;
}
int main(){
B b;
B y = f(f(b));
}
我们知道,拷贝构造在上述三种情况下会被调用,分析一下题目,第一层f(b)函数调用时一共有4次拷贝构造,第二层f(f(b))一共有4次拷贝构造,最后B y = f(f(b));算一次拷贝构造,一共9次。
但是不同的编译器会有不同的优化,在vs2019中,第4次和第5次连续的构造和拷贝构造优化为1次,同理第8次和第9次连续的构造和拷贝构造优化为1次。这样经过编译器优化后就只会进行7次拷贝构造了。
在vs2022中,编译器甚至会将第3次和第4次的拷贝构造优化为1次,第7次和第8次的拷贝构造优化为1次,只剩下5次拷贝构造。