Есть ли утечка памяти при вызове CComPtr и QueryInterface?

Подскажите, этот код имеет право на существование или нет?

HRESULT CMyClass::Assign(IUnknown * src) {
    if (!src)
        return E_INVALIDARG;
    HRESULT res;
    ATL::CComPtr<IAddress> addr;
    res = src->QueryInterface<IAddress>(&addr);
    if (SUCCEEDED(res))
        return addr->getStreet(*this->street);
    else
        return res;
}

Что меня смущает. В конструкторе класса CBasePtr, который является родительским классом для класса CComPtr, я вижу вызов AddRef(). Но QueryInterface, по идее уже сам вызывает AddRef(), т.е. получится утечка памяти. Или т.к. в QueryInterface передается указатель, то дополнительный AddRef() вызываться не будет?


Ответы (1 шт):

Автор решения: MSDN.WhiteKnight

Базовым классом для CComPtr у меня в VS2017 является CComPtrBase. Вот его конструкторы (из atlcomcli.h):

CComPtrBase() throw()
{
    p = NULL;
}
CComPtrBase(_Inout_opt_ T* lp) throw()
{
    p = lp;
    if (p != NULL)
        p->AddRef();
}

Как видно, здесь есть вызов AddRef, но только в конструкторе с параметром. Этот конструктор вызывался бы в коде вида ATL::CComPtr<IAddress> addr(somePointer);. В вашем случае же вызывается конструктор без параметра, а затем в пустой экземпляр CComPtr записывается адрес из QueryInterface. AddRef будет вызван только один раз (в QueryInterface).

Сказать, есть ли утечка памяти, по приведенному неполному коду нельзя. Если в метод передается правильная реализация IUnknown, то ее не должно быть, в том смысле, что в этом коде нечему вызвать утечку памяти. Есть ли утечка памяти в вашем реальном коде можно узнать с помощью отладчика, поставив точку останова на метод Release в COM-объекте. Если все нормально, при последнем вызове Release счетчик ссылок должен быть равен нулю.

→ Ссылка