You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实参抛出异常时,C++函数形参yy的析构函数为何未调用?

问题:C++函数形参析构未调用的原因分析

示例代码

#include <iostream>
using namespace std;

class X {};

class Y {
public:
    Y() { cout << "Y ctor\n"; }
    ~Y() { cout << "Y dtor\n"; }
};

int gun() {
    throw X{};
    return 42;
}

void fun(Y yy, int i) {}

int main()
{
    Y a;
    cout << "--------\n";
    try
    {
        fun(a, gun());
    }
    catch (const X&)
    {
        cout << "catched\n";
    }
    cout << "--------\n";
}

运行输出

Y ctor
--------
catched
--------
Y dtor

疑问

为何由实参a拷贝初始化的形参yy的析构函数未被调用?即使调换函数参数顺序(如定义void fun(int i, Y yy)并调用fun(gun(), a)),输出结果仍一致,因此并非函数实参求值顺序的问题?


解答

核心原因:形参yy未被构造

你的代码中看不到yy的析构输出,本质是**yy根本没有被构造**——你的编译器始终选择了先执行会抛出异常的gun(),导致yy的初始化步骤从未启动。

基于C++标准的详细解释

根据C++标准对函数调用的规则:

  1. 函数名的求值会先于所有实参表达式的计算。
  2. 不同实参的求值、对应形参的初始化之间是不确定顺序的(编译器可自由选择执行顺序,这是标准允许的优化空间)。
  3. 若在实参求值过程中抛出异常,只有已经完成初始化的形参会被销毁(调用析构);未开始初始化的形参,自然不会触发析构逻辑。

在你的测试场景中:

  • 无论函数参数顺序如何,编译器都优先选择了先求值gun()。gun()执行时直接抛出异常,此时yy对应的实参求值和形参初始化还未开始,因此yy从未被构造,也就不会有析构函数的输出。

验证方法

如果给Y类添加一个带输出的拷贝构造函数:

Y(const Y&) { cout << "Y copy ctor\n"; }

重新编译运行后,你可能会看到两种结果:

  • 多数情况下仍输出原结果(编译器优先执行gun(),无拷贝构造);
  • 少数编译器可能选择先初始化yy再执行gun(),此时会依次输出Y copy ctor、Y dtor,之后才是catched。

这说明问题的核心确实是编译器对实参求值顺序的选择——你的测试环境中编译器通过优先执行抛出异常的逻辑,避免了不必要的拷贝操作。


内容的提问来源于stack exchange,提问作者CPPL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 21:20:32