最近期末考试,忙着复习,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;后仍然没有调用复制构造函数。应该采用的是第二种方法。
有错误欢迎指出。