这个C ++中的内存泄漏在哪里?

时间:2009-08-06 09:06:59

标签: c++ mfc memory-leaks

我被告知有几个工具,下面的代码是泄漏内存,但我们不能为我们的生活看到哪里:

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中分配的内存。如果这不是原因,那是什么?

修改

我阅读了雷建议的文章以及与之相关的评论,然后尝试了:

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的主要区别现在已明确创建和销毁。

这大约减少了泄漏量,但这里仍有一些泄漏。

解决方案吗

看看Ray关于使用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),这不再泄漏,但我担心在将字符串分配给CString后在fieldValue上使用SysFreeString。它似乎运行,但我知道这并不表示存在内存损坏!

2 个答案:

答案 0 :(得分:6)

您必须释放为BSTR分配的内存。

请参阅article

哦,你必须在将VARIANT的bstr值赋给CString之前进行分离

strFieldValue = olevar.detach().bstrVal;

然后确保你的CString对象及时被正确销毁。

答案 1 :(得分:2)

此代码段可能会泄漏异常处理程序中的内存。换句话说,此功能不是例外安全。

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

如果在到达{{1}之前致电olevar之后发生异常,则永远不会清除fieldNamenew行。

我建议您使用某种智能指针(deletestd::auto_ptr)在完成使用后自动释放指针。

boost::scoped_ptr