为什么C ++ try / catch不捕获“纯虚拟调用”异常?

时间:2019-09-05 10:08:09

标签: c++ winapi exception com

以下函数在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;
}

1 个答案:

答案 0 :(得分:3)

  

我不明白为什么“纯虚拟通话”例外很特殊。

根据C ++标准,这是未定义的行为。未定义的行为表明您的代码中某处存在错误。鉴于这是未定义的行为,因此C ++实现 可能会引发可捕获的异常,但并非必须这样做。大多数C ++实现都会记录一条简短的消息,并abort()记录整个程序。

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

  

[class.abstract]

     

可以从以下对象的构造函数(或析构函数)调用成员函数   抽象类;虚拟呼叫纯音的效果   直接或间接针对所创建对象的虚拟函数   从这样的构造函数(或析构函数)中删除(或销毁)。

没有其他方法可以进行纯虚函数调用,因为您无法直接实例化C ++中的抽象类(具有纯虚函数的类),因此在同一子句中的其他地方对此进行了进一步的散列。

因此,无论您以哪种方式转弯,最终都会出现不确定的行为。您的代码中某处存在一个错误,该错误可能导致从构造函数或析构函数进行纯虚拟函数调用,或者导致内存损坏。解决这个问题的方法不是捕获某些异常,而是定位错误并进行修复。

如果在这种情况下需要引发可捕获的异常,那不是未定义的行为,不是吗?

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