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

C++ 为什么在这种情况下只调用一个构造函数而调用两个析构函数?,c++,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::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;
}
这让我很困惑,因为只有一个对象被创建为func的参数,但函数结束后调用了两个析构函数。 我开始怀疑,这是因为调用了默认的复制构造函数吗?所以我写了复制构造函数的定义,这让事情变得更奇怪。 在我向类中添加注释掉的代码(如上面所示)后,即复制构造函数的定义,输出如下:

输出:

call Test::constructor
call func
call Test::destructor
call Test::destructor
call Test::constructor
call func
call Test::destructor
现在一切都很顺利。 有人能给我解释一下这种现象吗?非常感谢

您对调用隐式声明的复制构造函数的原始代码的解释是正确的。 根据编译器正在实现的标准版本,它实际上可能使用隐式声明的move构造函数。但这是同样的事情。 在修改后的代码中,您显式地提供了一个复制构造函数,而在修改后的代码中,恰好触发了复制构造函数,编译器只是在开始时所需的位置构造对象。这是标准特别允许优化的少数情况之一,即使它会影响程序的可观察行为,因为您可以知道是否调用了复制构造函数。 修改后的代码不需要复制省略,原始代码也不禁止复制省略;这两个版本恰好不同,它们是否会在当前设置下触发编译器中的优化。 注意:这里的情况在C++17中发生了一些变化,在某些情况下,这种优化是强制性的。有关详细信息,请参阅上面的链接。
编辑为添加:顺便提一下,在使用显式复制构造函数的版本中,构造函数采用非常量引用是不寻常的。这实际上意味着无论如何都不能使用它,因为非常量引用不能绑定到临时Test1。我认为这种奇怪可能与编译器执行复制省略的原因有关。如果像隐式声明的复制构造函数那样,将构造函数更改为接受常量引用,则可能会看到预期的行为,显式复制构造函数将被调用,析构函数将被调用两次。但这只是我的猜测;你得试试看

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

您忘记了复制/移动构造函数。如果不创建复制构造函数,编译器将自己创建一个。有趣的是,在修复复制构造函数以获取常量引用并重命名_tmain之后,在g++下可以观察到完全相同的行为。如果copy-ctor微不足道,没有副作用,没有成员具有copy-ctor,编译器似乎不会执行copy-elision,但可以避免析构函数调用。在实际代码中,这可能是一种极不可能出现的情况-根据三/五规则,具有普通复制构造函数的对象也会具有普通析构函数,这就是为什么编译器在它们不匹配时不需要优化实例的原因。