【问题标题】:std::vector is cleared automatically before returning it [closed]std::vector 在返回之前自动清除[关闭]
【发布时间】:2013-01-18 20:27:11
【问题描述】:

我有这个功能,

vector<QDC::AdapterUserInfo> QDC::QueryInterface::RetrieveAdapterList()
{
    vector<QDC::AdapterUserInfo> retVal;
    InnerQueryInterface::IISTATE::Transition trans = _IQI->AdapterList(retVal);
    if (trans._OldState == trans._NewState)
    {
        if ( trans._NewState != InnerQueryInterface::IISTATE(trans._OldState).SuccessfulTransition(trans._Transition) )
        {
            throw Exceptions::FunctionFailed();
        }
    }
    return retVal;
}

QDC 是一个命名空间。 QueryInterface 是一个使用__declspec(dllexport) 导出的类。 (生成带有 lib 的 DLL) 在其他一些项目中,我按如下方式使用此功能,

vector<QDC::AdapterUserInfo> aui = Qui.RetrieveAdapterList();

但向量在 Release 版本中总是返回大小为 0。但返回大小为 1(这是元素的实际数量)。我在release模式下调试,发现RetrieveAdapterList函数里面的retVal填充的是实际数据,然后在函数的最后一行,也就是return retVal;这一行,显示retVal向量是cleared 变为空。所以最后返回的值是一个空向量。

为什么会这样?怎么了?为什么它在调试模式下工作?

--编辑--

似乎向量aui 的析构函数在继续执行到其范围结束后失败了。

-- 调用者的代码--

void DXE::Screen::Initialize( const HWND in_Window, const bool in_Windowed )
{
    QDC::UsableParameters UP;
    QDC::QueryInterface Qui;
    vector<QDC::AdapterUserInfo> aui = Qui.RetrieveAdapterList();
    if( aui.size() <= 0 )
    {
        throw Exceptions::UnknownException();
    }
    Qui.SelectAdapter(0);
    Qui.SelectDisplayMode(in_Windowed);
    UP = Qui.RetrieveParameters();

    _AdapterOrdinal = UP._AdapterOrdinal;

    _D3DPresentParams.BackBufferWidth = UP._Width;
_D3DPresentParams.BackBufferHeight = UP._Height;
_D3DPresentParams.BackBufferFormat = UP._BackBufferFormat;
    //..... fills the _D3DPresentParams..
}

-- 编辑:结论--

经过多次尝试后,出现一条错误消息,指出可能发生了堆崩溃,这就是 Peter Ruderman 提出的原因。所以然后我从使用动态库更改为静态库,这样在这种情况下可能不会发生这种堆的事情(至少我是这么想的)。然后程序似乎运行良好,没有任何问题。所以结论应该是彼得的理论是正确的。谢谢大家帮忙。

【问题讨论】:

  • 你能构造一个minimal test-case吗?
  • 当时我试图在一个简单的数据结构中复制错误。如果我能够复制这个错误,我会发布它。
  • 我能看到的唯一可以填充 retVal 的地方是_IQI-&gt;AdapterList(retVal)。这个函数是引用还是指针?
  • "我在发行版中对其进行了调试" 这几乎不是一个好主意。您所展示的有效:retVal 在 return 语句中具有的任何值都是 aui 将具有的。如果aui 为空,则表示retVal 为空。在调查retVal 是如何被清空的(不,编译器不会自动破坏您的程序)之前,请确保您确定aui 实际上是空的。如果retVal 为空,则意味着_IQI-&gt;AdapterList(retVal); 没有对其进行任何处理,或者它以某种方式在循环中被清除。调试一个调试版本并从那里检查出来。
  • 问题在于,在 Debug 构建中,它运行良好。但在发布版本中,aui 为空,retVal 在函数的底部被清空。我在关闭优化和调试信息的情况下仔细检查了发布版本。

标签: c++ visual-studio-2010 stl vector return-value


【解决方案1】:

另一种理论:

"QDC 是一个命名空间。QueryInterface 是一个使用 __declspec(dllexport) 导出的类。(生成带有 lib 的 DLL)"

如果我理解正确,您是说函数 Screen::Initialize 驻留在可执行模块中,但函数 QueryInterface::RetrieveAdapters 列表驻留在 DLL 中。在这种情况下,您的 DLL 正在分配内存,然后将其移动到调用站点。当 aui 超出范围时,您的 exe 会删除内存。如果 DLL 和 exe 使用不同的堆,这可能会导致严重错误(堆损坏)。

【讨论】:

  • 是的,他们来自不同的住所。哇,我从来没想过。我看看是不是这个原因。
  • DLL 有自己的堆吗?原谅我,我不是很有经验。 DLL 是用最简单的方式编写的。它只是一堆带有大量 std::set 和 std::vector 元素的少量函数。
  • 这取决于。如果 DLL 和应用程序都动态链接到完全相同版本的 CRT,那么它们将共享一个堆。如果它们链接到不同的版本或静态链接,那么它们将各有自己的。
  • 那么我该如何避免这种情况呢?通过作为参考传递?还是其他方式?
【解决方案2】:

这不是您的错误的来源。在 VS2010 使用的 C++11 的早期版本中,向量是可移动构造的。在您的 return 语句中,局部变量 retval 成为一个右值,因此编译器将其内容移动到调用站点的“aui”变量中。 (实际上,它只是交换了 aui 和 retval 的内容。)如果您也跟踪调试版本,您应该会看到这种行为。

【讨论】:

  • 是的,听起来可能是 OP 正在查看调试器并检查本地 retVal after 返回,这最终会成为垃圾或归零内存退出函数。
  • 我想也许你的仪式。但问题是aui 没有显示任何元素。 :(
  • @Deamonpog,你用的是哪个版本的VS,是VS2010吗?我已经看到一个带有矢量移动语义的错误,也许这是一种表现,或者它可能是一个不同的错误。
  • 是的,它的 VS2010。那是什么错误?似乎VS2008更好:P
  • @Deamonpog,如果您阅读我的问题的 cmets,您会发现 Microsoft 错误报告的链接:stackoverflow.com/questions/10218223/…
猜你喜欢
  • 2017-07-03
  • 1970-01-01
  • 2017-10-19
  • 2017-11-13
  • 2012-06-30
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多