【问题标题】:Wrapping dynamic array into STL/Boost container?将动态数组包装到 STL/Boost 容器中?
【发布时间】:2009-11-11 07:41:23
【问题描述】:

我需要将一个动态分配的数组(例如从 a = new double[100] )包装到 std::vector(最好)而不复制数组。 这个限制是因为我要包装的数组是从文件中映射的,所以只需执行 vector(a, a+size) 就会使内存使用量加倍。

有什么技巧可以做到吗?

【问题讨论】:

  • 无论如何,您都不会获得 std::vector 的全部功能(例如,调整大小,可能不起作用),因此列出您需要从包装器中获得的确切内容可能会更好吗?跨度>

标签: c++ arrays boost vector containers


【解决方案1】:

最好的解决方案之一是 STLSoft's array_proxy<> 模板。不幸的是,doxygen 从源代码生成的文档页面对理解模板没有很大帮助。源代码实际上可能更好一点:

array_proxy&lt;&gt; 模板在Matthew Wilson's book, Imperfect C++ 中有很好的描述。我使用的版本是 STLSoft 网站上的精简版本,所以我不必拉入整个库。我的版本不那么便携,但这使它比 STLSoft 上的要简单得多(它跳过了很多可移植性箍)。

如果你像这样设置一个变量:

int myArray[100];

array_proxy<int> myArrayProx( myArray);

变量myArrayProx 有许多STL 接口——begin()end()size()、迭代器等。

所以在很多方面,array_proxy&lt;&gt; 对象的行为就像一个向量(尽管 push_back() 不存在,因为 array_proxy&lt;&gt; 不能增长 - 它不管理数组的内存,它只是包装它在更接近向量的地方)。

array_proxy&lt;&gt; 的一个非常好的地方是,如果将它们用作函数参数类型,函数可以确定传入数组的大小,而原生数组则不然。并且包装数组的大小不是模板类型的一部分,因此使用起来非常灵活。

【讨论】:

  • +1,似乎是最好的解决方案,特别是因为代理不应该重新分配内存,所以它应该没有很多问题。
【解决方案2】:

boost::iterator_range 提供类似容器的接口:

// Memory map an array of doubles:
size_t number_of_doubles_to_map = 100;
double* from_mmap = mmap_n_doubles(number_of_doubles_to_map);

// Wrap that in an iterator_range
typedef boost::iterator_range<double*> MappedDoubles;
MappedDoubles mapped(from_mmap, from_mmap + number_of_doubles_to_map);

// Use the range
MappedDoubles::iterator b = mapped.begin();
MappedDoubles::iterator e = mapped.end();
mapped[0] = 1.1;
double first = mapped(0);

if (mapped.empty()){
    std::cout << "empty";
}
else{
    std::cout << "We have " << mapped.size() << "elements. Here they are:\n"
       << mapped;
}

【讨论】:

  • 对我来说最好的答案,因为它使用了 boost!
【解决方案3】:

我曾经下定决心要完成完全相同的事情。经过几天的思考和尝试,我认为这不值得。我最终创建了自己的自定义向量,其行为类似于 std::vector,但只有我实际需要的功能,如边界检查、迭代器等。

如果您仍然希望使用 std::vector,那么我当时能想到的唯一方法就是创建一个自定义分配器。我从来没有写过,但看到这是控制 STL 内存管理的唯一方法,也许可以在那里完成一些事情。

【讨论】:

  • 我建议不要编写自己的容器。在大多数情况下,这根本不值得,并且会导致很多互操作性问题。此外,花在调试重新发明的东西上的时间是浪费时间。然而,自定义分配器可能很容易实现并且是一种更好的方法。
  • 通常我会同意,但我真的认为这取决于你打算用它做什么。 std::vector 中的大多数“复杂”操作可能与内存管理有关,而在这种情况下,它们都不是必需的。因此,编写一个带有边界检查和指针作为迭代器的小类应该不会花费太长时间。我无法评论互操作性,因为我不知道作者打算在哪里/如何使用它。但是迭代器支持应该使它与 STL 的算法一起工作。
【解决方案4】:

不,使用std::vector 是不可能的。

但如果可能的话,您可以创建具有此大小的向量,并可能将文件映射到该大小。

std::vector<double> v(100);
mmapfile_double(&v[0], 100);

【讨论】:

    【解决方案5】:

    指向映射区域元素的指针向量怎么样(减少内存消耗,因为 sizeof(double*)

    有一些缺点(主要是您需要特殊的谓词进行排序)但也有一些好处,例如,您可以在不更改实际映射内容的情况下删除元素(或者有偶数个具有不同元素顺序的此类数组而没有任何更改为实际值)。

    在映射文件上使用 std::vector 的所有解决方案都有一个共同问题:将矢量内容“钉”到映射区域。这无法跟踪,您只能自己注意不要使用可能导致矢量内容重新分配的东西。所以在任何情况下都要小心。

    【讨论】:

      【解决方案6】:

      您可以使用 array_proxy,或查看 Boost.Array 。它为您提供 size()、front()、back()、at()、operator[] 等。就我个人而言,我更喜欢 Boost.Array,因为 Boost 更普遍。

      【讨论】:

        【解决方案7】:

        好吧,向量模板允许提供您自己的内存分配器。我自己从来没有做过,但我想让它指向你的数组并不难,也许用placement new 运算符......只是一个猜测,如果我尝试并成功,我会写更多。

        【讨论】:

          【解决方案8】:

          这是您问题的解决方案。在我想出一个可行的解决方案之前,我已经断断续续地尝试了很长时间。需要注意的是,您必须在使用后将指针清零,以避免双重释放内存。

          #include <vector>
          #include <iostream>
          
          template <class T>
          void wrapArrayInVector( T *sourceArray, size_t arraySize, std::vector<T, std::allocator<T> > &targetVector ) {
            typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr =
              (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector);
            vectorPtr->_M_start = sourceArray;
            vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = vectorPtr->_M_start + arraySize;
          }
          
          template <class T>
          void releaseVectorWrapper( std::vector<T, std::allocator<T> > &targetVector ) {
            typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *vectorPtr =
                  (typename std::_Vector_base<T, std::allocator<T> >::_Vector_impl *)((void *) &targetVector);
            vectorPtr->_M_start = vectorPtr->_M_finish = vectorPtr->_M_end_of_storage = NULL;
          }
          
          int main() {
          
            int tests[6] = { 1, 2, 3, 6, 5, 4 };
            std::vector<int> targetVector;
            wrapArrayInVector( tests, 6, targetVector);
          
            std::cout << std::hex << &tests[0] << ": " << std::dec
                      << tests[1] << " " << tests[3] << " " << tests[5] << std::endl;
          
            std::cout << std::hex << &targetVector[0] << ": " << std::dec
                      << targetVector[1] << " " << targetVector[3] << " " << targetVector[5] << std::endl;
          
            releaseVectorWrapper( targetVector );
          }
          

          或者,您可以只创建一个从 vector 继承的类,并在销毁时将指针清空:

          template <class T>
          class vectorWrapper : public std::vector<T>
          {   
          public:
            vectorWrapper() {
              this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL;
            }   
          
            vectorWrapper(T* sourceArray, int arraySize)
            {   
              this->_M_impl _M_start = sourceArray;
              this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize;
            }   
          
            ~vectorWrapper() {
              this->_M_impl _M_start = this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = NULL;
            }   
          
            void wrapArray(T* sourceArray, int arraySize)
            {   
              this->_M_impl _M_start = sourceArray;
              this->_M_impl _M_finish = this->_M_impl _M_end_of_storage = sourceArray + arraySize;
            }   
          };  
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2010-09-09
            • 1970-01-01
            • 1970-01-01
            • 2012-01-27
            • 2013-09-23
            • 2013-08-12
            • 2012-03-17
            相关资源
            最近更新 更多