Должен ли я вызывать метод Release() для объектов CComPtr?

Я работаю с API SAPI5 для обработки текста в речь. Если я упрощаю, мой код выглядит так (я удалил проверку ошибок, чтобы максимально упростить):

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

По какой-то странной причине мой код падает, если я не вызываю spVoice.Release(). Таким образом, приведенный выше код дает сбой, но этот код работает хорошо:

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

Разве CComPtr не освобождает базовый объект автоматически, когда он выходит за рамки?

Я посмотрел на реализацию CComPtr, и она вызывает Release в самом деструкторе.

Поэтому мне интересно, что могло пойти не так и почему, если я сам вызову Release, мой код не сработает. Но если я не позвоню Release, он вылетит.


person bodacydo    schedule 16.04.2012    source источник
comment
Обычно невыпущенный указатель не дает сбой, а просто создает утечку памяти. Действует ли созданный вами фрагмент так же, как и ваша полная программа?   -  person Mark Ransom    schedule 16.04.2012
comment
@MarkRansom Я сузил сбой до вызова spVoice.Release(). Я могу попробовать создать рабочий фрагмент и продолжать добавлять что-то, пока он не выйдет из строя.   -  person bodacydo    schedule 16.04.2012


Ответы (2)


Деструктор CComPtr вызовет Release. Однако это происходит, когда объект выходит за рамки. В приведенном выше коде это происходит непосредственно перед возвратом функции main, то есть после вызова CoUninitialize.

Следующий код является более правильным и гарантирует, что деструктор запустится до CoUninitialize.

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

Альтернативой является создание оболочки RAII вокруг CoInitialize/CoUninitialize. Если этот новый объект объявлен до spVoice, его деструктор будет запущен после деструктора spVoice, гарантируя правильный порядок.

person Michael    schedule 16.04.2012
comment
Я использовал оболочку RAII для CoInitialize/CoUninitialize, и она больше не дает сбоев. Спасибо всем, кто мне помог! - person bodacydo; 16.04.2012
comment
почему Если вы вызываете Release() после ::CoUninitialize(), это вызывает сбой? - person idanshmu; 09.01.2017

Вы правы, вы НЕ должны вызывать Release. Это делается автоматически в деструкторе. Одной из причин сбоя может быть побочный эффект, возникающий из-за того, что вы не инициализируете COM-приложение в начале своей программы.

person mox    schedule 16.04.2012
comment
Объект освобождается в деструкторе, но вам необходимо знать, когда запускаются ваши деструкторы. - person Raymond Chen; 16.04.2012
comment
@mox, извините, я действительно инициализирую COM. Я забыл упомянуть об этом во фрагменте кода, так как хотел, чтобы он был как можно короче. - person bodacydo; 16.04.2012
comment
@RaymondChen Спасибо за ссылку на статью. Я сейчас прочитаю. - person bodacydo; 16.04.2012
comment
@RaymondChen Это должно быть так! У меня точно такая же структура, как в вашем примере CoUninitialize. Сейчас я рефакторинг кода, чтобы убедиться, что spVoice будет уничтожен до вызова CoUninitialize. (PS. Я также обновил свой пример кода в вопросе.) - person bodacydo; 16.04.2012