Где утечка памяти в этом C ++? - PullRequest
4 голосов
/ 06 августа 2009

Мне сказали пару инструментов, что в следующем коде происходит утечка памяти, но мы не можем за всю жизнь увидеть, где:

HRESULT CDatabaseValues::GetCStringField(ADODB::_RecordsetPtr& aRecordset, CString& strFieldValue,
                                         const char* strFieldName, const bool& bNullAllowed)
{
    HRESULT hr = E_FAIL;

    try
    {
        COleVariant olevar;
        olevar = aRecordset->Fields->GetItem(_bstr_t(strFieldName))->Value;
        if (olevar.vt == VT_BSTR && olevar.vt != VT_EMPTY)
        {
            strFieldValue = olevar.bstrVal;
            hr = true;
        }
        else if ((olevar.vt == VT_NULL || olevar.vt == VT_EMPTY) && bNullAllowed)
        {
            //ok, but still did not retrieve a field
            hr = S_OK;
            strFieldValue = "";
        }
    }
    catch(Exception^ error)
    {
        hr = E_FAIL;
        MLogger::Write(error);
    }
    return hr;
}

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

Я пробовал olevar.detach () и olevar.clear (), оба не дали эффекта, поэтому, если это причина, как освободить память, которая предположительно выделена в GetItem. И если это не причина, то что?

EDIT

Я прочитал статью, предложенную Рэем, а также комментарии, связанные с ней, а затем попытался:

HRESULT CDatabaseValues::GetCStringField(ADODB::_RecordsetPtr& aRecordset, CString& strFieldValue,
                                         const char* strFieldName, const bool& bNullAllowed)
{
    HRESULT hr = E_FAIL;

    try
    {
        COleVariant* olevar = new COleVariant();
        _bstr_t* fieldName = new _bstr_t(strFieldName);
        *olevar = aRecordset->Fields->GetItem(*fieldName)->Value;
        if (olevar->vt == VT_BSTR && olevar->vt != VT_EMPTY)
        {
            strFieldValue = olevar->bstrVal;
            hr = true;
        }
        else if ((olevar->vt == VT_NULL || olevar->vt == VT_EMPTY) && bNullAllowed)
        {
            //ok, but still did not retrieve a field
            hr = S_OK;
            strFieldValue = "";
        }
        delete olevar;
        delete fieldName;
    }
    catch(Exception^ error)
    {
        hr = E_FAIL;
        MLogger::Write(error);
    }
    return hr;
}

Основные различия: olevariant и bstr теперь явно создаются и уничтожаются.

Это примерно вдвое сократило объем утечки, но здесь есть что-то, что протекает.

Решение

Глядя на совет Рэя по использованию Detach, я придумал следующее:

HRESULT CDatabaseValues::GetCStringField(ADODB::_RecordsetPtr& aRecordset, CString& strFieldValue,
                                         const char* strFieldName, const bool& bNullAllowed)
{
    HRESULT hr = E_FAIL;

    try
    {
        COleVariant olevar;
        _bstr_t fieldName = strFieldName;
        olevar = aRecordset->Fields->GetItem(fieldName)->Value;

        if (olevar.vt == VT_BSTR && olevar.vt != VT_EMPTY)
        {
            BSTR fieldValue = olevar.Detach().bstrVal;
            strFieldValue = fieldValue;
            ::SysFreeString(fieldValue);
            hr = true;
        }
        else if ((olevar.vt == VT_NULL || olevar.vt == VT_EMPTY) && bNullAllowed)
        {
            //ok, but still did not retrieve a field
            hr = S_OK;
            strFieldValue = "";
        }
        ::SysFreeString(fieldName);
    }
    catch(Exception^ error)
    {
        hr = E_FAIL;
        MLogger::Write(error);
    }
    return hr;
}

Согласно инструменту (GlowCode), это больше не утечка, но я беспокоюсь об использовании SysFreeString для fieldValue после того, как он был назначен CString. Кажется, он работает, но я знаю, что это не свидетельствует о том, что память не повреждена!

Ответы [ 2 ]

6 голосов
/ 06 августа 2009

Вы должны освободить память, выделенную для BSTR.

См. артикул

О, и вы должны отсоединиться, прежде чем присваивать bstr значение VARIANT для CString

strFieldValue = olevar.detach().bstrVal;

и затем убедитесь, что ваш объект CString вовремя уничтожен.

2 голосов
/ 06 августа 2009

Этот фрагмент кода может привести к утечке памяти в обработчике исключений. Другими словами, эта функция не является безопасной для исключения.

catch(Exception^ error)
{
    hr = E_FAIL;
    MLogger::Write(error);
}

Вы никогда не убираете olevar или fieldName в том случае, если после вызовов new и до выдается исключение *1000*.

Я рекомендую использовать какой-нибудь интеллектуальный указатель (std::auto_ptr, boost::scoped_ptr), чтобы автоматически освобождать указатели, когда вы закончите их использовать.

std::auto_ptr<COleVariant> olevar(new COleVariant);
...