为什么在这种情况下只调用一个构造函数但调用了两个析构函数?

时间:2016-09-13 04:53:32

标签: c++

第一次,代码如下所示:

#include "stdafx.h"
#include<iostream>
using namespace std;

class Test{
public:
    explicit Test(int);
    ~Test();
    //Test(Test&);
    int varInt;
};
Test::Test(int temp){
    varInt = temp;
    cout << "call Test::constructor\n";
}
Test::~Test(){
    cout << "call Test::destructor\n";
}
/*Test::Test(Test&temp){
    varInt = temp.varInt;
    cout << "call Test::copy constructor\n";
}*/
void func(Test temp){
    cout << "call func\n";
}
int _tmain(int argc, _TCHAR* argv[])
{
    func(Test(1));
    return 0;
}

输出:

call Test::constructor
call func
call Test::destructor
call Test::destructor

这让我感到困惑,因为只创建了一个对象(作为func的参数),但是在函数结束后调用了两个析构函数。 我开始怀疑,这是因为默认的复制构造函数被调用了吗?所以我写了复制构造函数的定义,这让事情变得更加奇怪。 在我将上面提到的注释掉的代码(即复制构造函数的定义)添加到类中后,输出变为如下所示:

输出:

call Test::constructor
call func
call Test::destructor

事情变得恰到好处。 有人可以向我解释这个现象吗?非常感谢你。

2 个答案:

答案 0 :(得分:3)

  • 您对原始代码(正在调用隐式声明的复制构造函数)的解释是正确的。
    • 根据编译器实现的标准版本,它实际上可能正在使用隐式声明的移动构造函数。但这也是一回事。
  • 您修改过的代码(您明确提供了复制构造函数)恰好触发the copy elision optimization,编译器只是在所需的位置构建对象。这是标准特别允许优化的少数情况之一,即使它影响程序的可观察行为(因为您可以判断是否调用了复制构造函数)。
  • 您修改的代码的任何内容都不需要复制省略,而您的原始代码禁止;两个版本恰好在它们是否在您当前设置下触发编译器中的优化时会有所不同。
    • 注意:此处的情况在C ++ 17中有所改变,在某些情况下,此优化确实成为必需。有关详细信息,请参阅我的上述链接。

编辑添加:顺便提一句,在您的带有显式复制构造函数的版本中,构造函数在获取非常量引用时不常见。这实际上意味着它无论如何都无法使用,因为非常量引用无法绑定到临时Test(1)。我认为这种奇怪可能与编译器执行复制省略的原因有关。如果更改构造函数以获取常量引用,则作为隐式声明的复制构造函数,您可能会看到您期望的行为,并调用显式复制构造函数并且两次调用析构函数。 (但这只是我的猜测;你必须尝试看看!)

答案 1 :(得分:1)

你有两个Test类对象。由于您按值传递参数,因此在main函数中显式构造一个参数,另一个使用默认复制构造函数构造,因为您的复制构造函数已被注释掉。两个对象都被破坏了。一个在func()的出口处,另一个在main()的出口处。因此有两个析构函数调用。