C++中函数返回值为自定义类时不调用复制构造函数的问题

最近期末考试,忙着复习,ACM暂时鸽了。
复习的时候手敲了一个程序想看看编译器是怎么调析构函数的,结果出问题了。
问题是这样的:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
class Complex
{
public:
    int r, i;
public:
    Complex(int a, int b): r(a), i(b) { cout << "Constructor called." << endl; }
    ~Complex() { cout << "Destructor called." << endl; }
    Complex(const Complex &a) { r = a.r, i = a.i; cout << "Copy constructor called." << endl; }
    Complex operator +(Complex x1);
};
Complex Complex::operator +(Complex x1)
{
    return Complex(r + x1.r, i + x1.i);
}
int main()
{
    Complex a(1, 2);
    a = a + a;
    return 0;
}

运行完结果是:

Constructor called.
Copy constructor called.
Constructor called.
Destructor called.
Destructor called.
Destructor called.

按照书上讲的来说,这个程序首先调用一次构造函数创建对象a,之后在主程序第二行调用复制构造函数把a传给形参x1,之后在函数中调用一次构造函数生成加法的结果,之后再调用一次复制构造函数把这个结果存到一个临时对象里,然后析构结果和x1,之后把临时对象赋值给a,最后析构临时对象。所以理想结果应该是:

Constructor called.
Copy constructor called.
Constructor called.
Copy constructor called.
Destructor called.
Destructor called.
Destructor called.
Destructor called.

但是很明显程序运行结果少了一次复制构造函数和析构函数的调用。百思不得其解,想了一个下午,最后通过询问神奇海螺发现C++对于返回值有个叫做返回值优化的东西。
这篇文章讲得很详细:C++中临时对象及返回值优化

具体来说返回值优化有两种实现方式。第一种是对于已经存在的对象a,在调用函数对该对象赋值是编译器会偷偷往函数里多塞一个形参,把a的地址传进去,之后直接在函数里对a赋值。第二种不再赘述,上面这篇博客写得比较详细。

把程序的

a = a + a;

改成

a + a;

后仍然没有调用复制构造函数。应该采用的是第二种方法。

有错误欢迎指出。

猜你喜欢

转载自blog.csdn.net/qq_36000896/article/details/107050275