【问题标题】:c++ vector construct with given memory具有给定内存的c ++向量构造
【发布时间】:2011-06-18 08:20:15
【问题描述】:

我想使用 std::vector 来控制给定的内存。首先,我很确定这不是一个好习惯,但好奇心胜过我,无论如何我都想知道如何做到这一点。

我遇到的问题是这样的方法:

vector<float> getRow(unsigned long rowIndex)
{
    float* row = _m->getRow(rowIndex); // row is now a piece of memory (of a known size) that I control
    vector<float> returnValue(row, row+_m->cols()); // construct a new vec from this data
    delete [] row; // delete the original memory 
    return returnValue; // return the new vector 
}

_m 是一个 DLL 接口类,它返回一个浮点数组,调用者负责删除该数组。所以我想把它包装在一个向量中并将其返回给用户......但是这个实现为向量分配新内存,复制它,然后删除返回的内存,然后返回向量。

我想做的是直接告诉新向量它可以完全控制这块内存,所以当它被删除时,内存就会被清理掉。

更新:最初的动机(从 DLL 返回的内存)已被许多响应者相当坚决地压制 :) 但是,无论如何,我很想知道这个问题的答案...有没有办法使用给定的预分配内存 T* 数组块和该内存的大小来构造 std::vector?

【问题讨论】:

  • 如果内存是用new[]malloc 分配的,当您调用delete row 时,发布的代码会导致未定义的行为
  • @Jamie Cook:好的,那么您的 DLL 接口是否具有分配和释放内存的功能? DLL 接口应该具有用于​​内存管理的对称函数,因为您不能保证从getRow() 返回的数组甚至可以被delete[] 删除。您应该将getRow() 重命名为copyRow() 之类的名称,并创建一个名为deleteRow() 之类的函数,然后提供createRowArray() 之类的名称,以便vector 可以增加其容量。
  • 不要让您的 DLL 分配您负责解除分配的内存,这可能会导致难以追踪的内存损坏错误。请参阅blogs.msdn.com/b/oldnewthing/archive/2006/09/15/755966.aspx 以获得更详细的解释(简短回答:不同的 DLL/模块可以加载不同版本的 C/C++ 运行时)。
  • @Jamie Cook:下面是我所说的一个例子:GetWindowTextLength()GetWindowText() 是在 Windows API 中检索窗口标题的两个函数。应用程序调用GetWindowTextLength() 分配一个足够大的缓冲区,然后传递缓冲区指针GetWindowText() 允许函数用文本填充它。你应该这样设计你的 DLL 接口。那么根据这两个函数创建一个方便函数就变得很容易了。
  • @ChrisLutz:_m 仅在全局范围内保留。 不是“所有以下划线开头的名称”都是保留的。我怀疑这个 _m 是一个数据成员。 (如果它不是数据成员,那么如果它在命名空间中,它仍然可能不会被保留。)

标签: c++ memory-management vector


【解决方案1】:

显而易见的答案是使用自定义分配器,但是您可能会发现这确实是您需要的重量级解决方案。如果你想这样做,最简单的方法是获取由实现定义的分配器(作为 vector 的默认 scond 模板参数),复制它并使其按需要工作。

另一种解决方案可能是定义向量的模板特化,根据实际需要定义尽可能多的接口并实现内存自定义。

最后,如何使用符合 STL 接口定义自己的容器,定义随机访问迭代器等。这可能很容易,因为底层数组将很好地映射到 vector,并且指向它的指针将映射到迭代器。

评论 UPDATE:“有没有办法使用给定的预分配内存 T* 数组块和该内存的大小来构造 std::vector?”

当然,这里的简单答案是“不”。如果您希望结果是一个向量,那么它必须支持按需增长,例如通过 reserve() 方法,而这对于给定的固定分配是不可能的。所以真正的问题是:你到底想达到什么目标?像vector这样可以使用的东西,或者在某种意义上确实必须成为矢量的东西,如果是,那是什么意思?

【讨论】:

    【解决方案2】:

    Vector 的默认分配器不提供对其内部的这种类型的访问。您可以使用自己的分配器(向量的第二个模板参数)来执行此操作,但这会改变向量的类型。

    如果你可以直接写入向量会容易得多:

    vector<float> getRow(unsigned long rowIndex) {
      vector<float> row (_m->cols());
      _m->getRow(rowIndex, &row[0]);  // writes _m->cols() values into &row[0]
      return row;
    }
    

    注意 &row[0] 是一个浮点数*,它保证向量连续存储项目。

    【讨论】:

    • 理想情况下我想在不改变类型的情况下这样做。重点是为 getRow() 的调用者提供一个标准向量(但没有额外的内存分配)
    • +1 如果 OP 可以访问 DLL 源,那么这是最好的方法。
    • 在这种情况下,我可以更改 DLL 源(并且很可能会这样做)但是我真的很想知道是否可以在不更改的情况下进行。
    • @Jamie Cook:我强烈建议您更改 DLL 接口,使其像这样工作。这就是通常设计 DLL 接口来处理这种情况的方式;看看我的 cmets 你的问题。
    • @Insilico,DLL 接口已更改.... 仍然想知道原始问题的答案。即使我从不使用它,我仍然想知道它是否可以做到。
    【解决方案3】:

    这里最重要的是要知道不同的 DLL/模块有不同的堆。这意味着从 DLL 分配的任何内存都需要从该 DLL 中删除(这不仅仅是编译器版本或delete vs delete[] 或其他问题)。不要跨 DLL 边界传递内存管理责任。这包括在 dll 中创建 std::vector 并返回它。但它还包括将std::vector 传递给DLL 以由DLL 填充;这样的操作是不安全的,因为您不确定std::vector 在填充值时不会尝试调整大小。

    有两种选择:

    • std::vector 类定义您自己的allocator,该类使用保证驻留在创建向量的DLL/模块中的分配函数。这可以通过动态绑定轻松完成(也就是说,让allocator 类调用一些虚函数)。由于动态绑定会在函数调用的 vtable 中查找,因此可以保证它会落在最初创建它的 DLL/模块的代码中。

    • 不要将矢量对象传入或传出 DLL。例如,您可以使用函数 getRowBegin()getRowEnd() 在行数组中返回迭代器(即指针)(如果它是连续的),并让用户 std::copy 进入它自己的本地 std::vector目的。你也可以反过来做,将迭代器 begin() 和 end() 传递给像 fillRowInto(begin, end) 这样的函数。

    这个问题是很现实的,虽然很多人在不知不觉中忽略了它。不要低估它。我个人遇到过与此问题相关的无声错误,而且它并不漂亮!我花了几个月的时间才解决它。

    我已经检查了源代码,boost::shared_ptrboost::shared_array 使用动态绑定(上面的第一个选项)来处理这个问题。但是,不能保证它们是二进制兼容的。不过,这可能是一个更好的选择(通常二进制兼容性比跨模块的内存管理问题要小得多)。

    【讨论】:

    • “不同的 DLL/Module 有不同的堆”——只有在你改变默认的构建设置(或者使用多个编译器构建,这更加复杂)时才会出现这种情况。使用默认设置 (/MD) 只有一个堆。
    【解决方案4】:

    您最好的选择可能是std::vector&lt;shared_ptr&lt;MatrixCelType&gt;&gt;

    this thread 中有更多详细信息。

    【讨论】:

    • 上帝不......请不要到处乱扔shared_ptr:/
    【解决方案5】:

    如果您尝试更改向量分配/重新分配/解除分配内存的位置/方式,那么您正在寻找向量类的分配器模板参数。

    如果您只是想避免构造、复制构造、赋值和销毁的开销,则允许用户实例化向量,然后通过引用将其传递给您的函数。然后用户负责构建和销毁。

    听起来您正在寻找的是一种智能指针。一种在它被破坏时删除它所指向的东西。在这种情况下,请查看 Boost 库或自行开发。

    【讨论】:

      【解决方案6】:

      Boost.SmartPtr 库包含大量有趣的类,其中一些专门用于处理数组。

      例如,看scoped_array

      int main(int argc, char* argv[])
      {
        boost::scoped_array<float> array(_m->getRow(atoi(argv[1])));
        return 0;
      }
      

      当然,问题是scoped_array 不能被复制,所以如果你真的想要std::vector&lt;float&gt;,@Fred Nurk's 可能是你能得到的最好的。

      在理想情况下,您需要与 unique_ptr 等效的数组形式,但我认为它不是标准的一部分。

      【讨论】:

        猜你喜欢
        • 2012-03-14
        • 1970-01-01
        • 2011-08-22
        • 2016-04-20
        • 2011-10-20
        • 1970-01-01
        • 2018-12-21
        • 2011-03-27
        • 2012-01-12
        相关资源
        最近更新 更多