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



第一次,代码如下所示:

#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::constructorn";
}
Test::~Test(){
    cout << "call Test::destructorn";
}
/*Test::Test(Test&temp){
    varInt = temp.varInt;
    cout << "call Test::copy constructorn";
}*/
void func(Test temp){
    cout << "call funcn";
}
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

事情变得刚刚好。有人能给我解释一下这个现象吗?非常感谢。

    你对原始代码的解释(隐式声明的复制构造函数正在被调用)是正确的。
    • 根据您的编译器正在实现的标准的版本,它实际上可能会使用隐式声明的move构造函数。但这其实是一回事。
  • 修改后的代码(其中显式地提供了复制构造函数)恰好触发了复制省略优化,其中编译器仅在开始所需的位置构造对象。这是标准明确允许优化的少数情况之一,即使它会影响程序的可观察行为(因为您可以判断是否调用了复制构造函数)。
  • 修改后的代码不需要复制省略,原始代码不禁止复制省略;这两个版本的不同之处在于它们是否在当前设置下触发编译器中的优化。
    • 注意:这里的情况在c++ 17中有所改变,在某些情况下,这种优化确实是强制性的。查看我上面的链接了解详细信息。
顺便说一下,在带有显式复制构造函数的版本中,构造函数在接受非常量引用时是不寻常的。这实际上意味着它无论如何都不能使用,因为非常量引用不能绑定到临时的Test(1)。我认为这种奇怪的现象可能与编译器执行拷贝省略的原因有关。如果将构造函数更改为接受常量引用,就像隐式声明的复制构造函数那样,您可能会看到期望的行为,显式复制构造函数被调用,析构函数被调用两次。(但这只是我个人的猜测;你必须尝试一下,看看!)

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

最新更新