我是否必须在CComPtr对象上调用Release()方法?

bod*_*ydo 2 windows com atl smart-pointers sapi

我正在使用SAPI5 API来处理文本到语音.如果我简化我的代码看起来像这样(我删除了错误检查,以尽可能简化它):

int main() {
    CoInitialize(NULL);
    CComPtr<ISpVoice> spVoice;
    spVoice.CoCreateInstance(CLSID_SpVoice);
    ...
    CoUninitialize();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

出于某种奇怪的原因,如果我不调用spVoice.Release(),我的代码会崩溃.所以上面的代码崩溃了,但是这段代码很好用:

int main() {
    CoInitialize(NULL);
    CComPtr<ISpVoice> spVoice;
    spVoice.CoCreateInstance(CLSID_SpVoice);
    ...
    spVoice.Release();
    CoUninitialize();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

CComPtr它超出范围时不会自动释放底层对象吗?

我查看了CComPtr它的实现,它确实调用Release了析构函数本身.

所以我想知道可能出现什么问题,为什么如果我打电话给Release自己,我的代码不会崩溃.但如果我不打电话,Release它会崩溃.

Mic*_*ael 9

CComPtr的析构函数将调用Release.但是,当对象超出范围时,它会这样做.在上面的代码中,这是在主要返回之前,这是调用CoUninitialize之后.

以下代码更正确,并保证析构函数在CoUninitialize之前运行.

int main() {
    CoInitialize(NULL);
    { // Begin scope
        CComPtr<ISpVoice> spVoice;
        spVoice.CoCreateInstance(CLSID_SpVoice);
        ...
    } / End scope, spVoice's destructor runs.
    CoUninitialize();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

另一种方法是在CoInitialize/CoUninitialize周围创建一个RAII包装器.如果在spVoice之前声明了这个新对象,它的析构函数将在spVoice的析构函数之后运行,从而保证正确的顺序.