一道很坑的题_(:з」∠)_:
以下代码共调用多少次拷贝构造函数?
// 《深度探索C++对象模型》
Weight f(Weight u)
{
Weight v(u);
Weight w = v;
return w;
}
int main()
{
Weight x;
Weight ret = f(f(x));
return 0;
}
文章目录
- 1 匿名对象
- 2.匿名对象的用处
- 3.题解
- 3.1 铺垫
- 3.2 解题
为了让大家更好地理解上面那道题,我们先来介绍一下什么是匿名对象?
匿名对象可以理解为是一个临时对象,一般系统自动生成的,如你的函数返回一个对象,这个对象在返回时会生成一个临时对象。
语法:类名();
注意:匿名对象,生命周期只在它定义的那一行。
我借助以下代码调试向大家说明:
#includeusing std::cout; using std::cin; using std::endl; class Weight { public: Weight() { cout << "Weight()" << endl; } Weight(const Weight& w) { cout << "Weight(const Weight& w)" << endl; } ~Weight() { cout << "~Weight()" << endl; } Weight& operator=(const Weight& w) { cout << "Weight& operator=(const Weight& w)" << endl; return *this; } void print() { cout << "2022-5-25" << endl; } }; // 《深度探索C++对象模型》 Weight f(Weight u) { Weight v(u); Weight w = v; return w; } int main() { Weight x; Weight(); f(x); return 0; }
我们发现44行这个匿名对象调了构造函数之后,马上又调了析构函数。
我们平时调用类的成员函数,是不是先定义一个对象,通过对象去调用。
匿名对象还可以这样调用:
还有,如果你只是单纯的想要传参,也可以用匿名对象:
但是我们发现左边传参的方式调用了4次拷贝构造,而右边传参的方式调用了3次拷贝构造。
为什么?因为编译器优化了,编译器会在一个步骤里面,比如说一个表达式里面,如果出现连续的构造+拷贝构造或者连续的拷贝构造,编译器会把它们优化。
左边没的说,因为是分开的,先构造了一个对象,然后把这个对象传参又调用了拷贝构造。
右边是构造了匿名对象之后立马作为参数传参,这个时候编译器就会把它们合二为一,直接构造传参的对象。
还记得我们以前说的拷贝构造的两种方式吗?
class A
{
public:
A(int x)
{}
};
int main()
{
A a1(1);
A a2 = 2; // A(2) -> A a2(A(2)) 优化:直接构造
return 0;
}
关于 A a2 = 2;这行代码,它会先拿2构造一个匿名对象(也叫临时对象),然后才会拿这个对象去拷贝构造一个a2。这个时候胆大的编译器也会优化,直接构造a2.
注意:C++标准并没有规定是否优化,完全取决于编译器,不过新一点的编译器一般都会做这个优化。
3.题解 3.1 铺垫如果是以下的代码,我们的程序会调用几次拷贝构造?
ok,我们先自己来算一下。
x传参拷贝构造1次,33、34行各拷贝构造1次,传值返回拷贝构造1次,41返回的结果又拷贝构造给ret,又是1次。是不是应该总共5次呀!
ok,我们看运行结果:
怎么回事?
本来是不是应该这样啊,但是大家注意,41行是不是连续的拷贝构造啊,编译器会把它合二为一,相当于直接用w去拷贝构造ret了。
如果大家还是不能确信,请看下图:
这个返回来的w的临时对象就是赋值给ret,因为ret对象已经存在。阻断了编译器的优化。
所以这道题总共一个调用了7次拷贝构造函数,我们运行一下看是不是:
哈哈,你答对了吗?(:з」∠)



