【问题标题】:Why C++ try/catch does not catch the "pure virtual call" exception?为什么 C++ try/catch 没有捕捉到“纯虚拟调用”异常?
【发布时间】:2020-01-08 05:11:24
【问题描述】:

以下函数是在 Microsoft Visual Studio 2017 中编译的。在该函数中,CAttributedObject 是实现IObjectID 接口的 COM 类。该函数必须处理有关事件的通知。参数pCaller 是指向CAttributedObject 类型对象的指针,它是事件的来源。通知通过其消息队列分派到主线程。预计在通知到达的那一刻,pCaller指向的对象可以被销毁;这就是代码中出现 try/catch 块的原因。

然而,有时,对QueryInterface 的调用会引发“纯虚拟调用”异常,该异常未被捕获并导致应用程序崩溃。在许多其他情况下,我们使用 try/catch 测试对可能已删除的内存的访问,并且所有这些都已经工作了多年。我不明白为什么“纯虚拟调用”异常很特别。

HRESULT CDataProvider::OnObjIconChanged(
            BSTR Key, BSTR Path, IUnknown* pCaller,
            UINT Flags, __int64 hIcon, const CLSID& ProviderCLSID)
{
    CAttributedObject* pAttrObj = 0;
    IObjectID* pObjID = 0;
    try {
        try { pCaller->QueryInterface(__uuidof(IObjectID), (void**)&pObjID); }
        catch(...) {}

        if (pObjID != 0)
            pAttrObj = static_cast<CAttributedObject*>(pObjID);

        if (pAttrObj != 0)
        {
            pAttrObj->SetIcon((HICON)hIcon, Flags & SHGFI_SMALLICON);
            pAttrObj->Release();
        }
    } catch(...) {}

    TreeService()->Fire_ObjIconChanged(Key, Path, hIcon);
    return S_OK;
}

【问题讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • " pCaller 指向的对象可以被销毁" 也许你在某处缺少AddRef/Release 对。
  • 第一个错误是称其为“纯虚拟调用异常”。 “纯虚拟通话”也不例外。

标签: c++ winapi exception com


【解决方案1】:

我不明白为什么“纯虚拟调用”异常很特别。

因为它是未定义的行为,根据 C++ 标准。未定义的行为,表明您的代码中的某个地方存在错误。鉴于这是未定义的行为,C++ 实现可能抛出一个可捕获的异常,但它没有义务这样做。大多数 C++ 实现都会记录一条简洁的消息,并abort() 整个程序。

在 C++ 标准的范围内唯一可能进行的纯虚函数调用是来自抽象基类的构造函数或析构函数,而 C++ 标准明确指出这是未定义的行为:

[class.abstract]

成员函数可以从构造函数(或析构函数)调用 一个抽象类;对 pure 进行虚拟调用的效果 直接或间接用于正在创建的对象的虚函数 来自这样的构造函数(或析构函数)的(或销毁的)是未定义的。

没有其他可能的方法来进行纯虚函数调用,因为您无法在 C++ 中直接实例化抽象类(具有纯虚函数的类),这将在同一子句的其他地方进一步讨论。

因此,无论您转向哪种方式,最终都会出现未定义的行为。您的代码中有一个错误,该错误会导致来自构造函数或析构函数的纯虚函数调用,或者内存损坏。解决这个问题的方法不是捕获一些异常,而是定位错误并修复它。

如果在这种情况下需要抛出一个可捕获的异常,那不会是未定义的行为,不是吗?

异常不是未定义的行为。它们的机械工作部分完全在 C++ 标准中定义。

【讨论】:

  • 因为它是未定义的行为,根据 C++ 标准 不,因为 _purecall 的当前 lib 实现调用 __fastfail(),如果 IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE) 并且这个具体的异常可以不能从应用程序处理(仅由调试器)。如果不存在 PF_FASTFAIL_AVAILABLE - 生成的 STATUS_FATAL_APP_EXIT 异常,可以处理。然而,所有这些都是特定于 Windows 的,并且取决于 _purecall 的实现。不是来自通用 c++ 标准
  • @Sam 好的,谢谢。尽管您引用的标准摘录与我的案例没有任何关系,但我现在明白“纯虚拟调用”异常是特殊的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-20
  • 2017-07-25
  • 1970-01-01
  • 1970-01-01
  • 2013-02-05
  • 2021-07-25
  • 2010-09-07
相关资源
最近更新 更多