【问题标题】:Where is the memory leak in this C++?这个 C++ 中的内存泄漏在哪里?
【发布时间】:2010-11-17 06:57:05
【问题描述】:

有人告诉我,以下代码正在泄漏内存,但我们终生无法看到:

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

编辑

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

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),这不再泄漏,但我担心在将 SysFreeString 分配给 CString 之后在 fieldValue 上使用它。它似乎可以运行,但我知道这并不意味着没有内存损坏!

【问题讨论】:

  • 您的帖子让我想到了 BSTR 值。我没有确切地遇到这个问题,但我有以下导致内存泄漏的代码。花了很多时间将其缩小到这行代码。抱歉格式化问题。 void GetValue(COleVariant& oVar) { . . . //oVar.Clear(); -- memory leak because of following line without this call if bstr was allocated for this variant!! oVar.Vt = VT_I4; oVar.lVal = 100; }

标签: c++ mfc memory-leaks


【解决方案1】:

你必须释放分配给 BSTR 的内存。

article

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

strFieldValue = olevar.detach().bstrVal;

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

【讨论】:

  • 虽然 COleVariant 的析构函数会为您执行此操作,但您可能会想到...现在无法真正检查它的作用。
  • 其实是翻看MFC源码。 ~COleVariant 调用“VariantClear”并根据 VariantClear 上的文档

    “如果 vtfield 是 VT_BSTR,则释放字符串”
  • 托管 C++ 中的这是否符合犹太教规?当 olevar 超出引用 (strFieldValue) 的范围时,您正在分配一些将被销毁的东西,在非托管 C++ 中,这通常意味着 strFieldValue 只是巧合正确。
  • strFieldValue 是一个 CString,我假设分配给它的 bstr 会强制隐式创建 CString 包装器。我同意 bstr 然后被销毁(或者至少这是我想要达到的),但 CString 应该仍然可以吗?
  • CString 将使用 bstr 的 0 终止的 wchars 缓冲区。这就是为什么你必须做一个分离。它确保 olevar 析构函数不会在 bstr 缓冲区上调用 SysFreeString。
【解决方案2】:

此代码 sn-p 可能会在异常处理程序中泄漏内存。换句话说,这个函数不是异常安全的。

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

如果在您调用new 之后以及到达delete 行之前引发异常,您永远不会清理olevarfieldName

我建议您使用某种智能指针(std::auto_ptrboost::scoped_ptr)在您使用完指针后自动释放它们。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-22
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多