寻找类似C ++ STL的矢量类,但使用堆栈存储

时间:2008-12-09 22:15:21

标签: c++ data-structures stl vector

在我自己写作之前,我会问你们所有人。

我正在寻找一个几乎完全像STL向量的C ++类,但是将数据存储到堆栈中的数组中。某种类型的STL分配器类也可以工作,但我试图避免任何类型的堆,甚至是静态分配的每线程堆(尽管其中一个是我的第二选择)。堆栈效率更高。

对于使用向量的当前代码,它几乎需要替代。

对于我自己要写的东西,我在考虑这样的事情:

char buffer[4096];
stack_vector<match_item> matches(buffer, sizeof(buffer));

或者类可以在内部分配缓冲区空间。然后它看起来像:

stack_vector<match_item, 256> matches;

我认为如果空间不足,它会抛出std :: bad_alloc,尽管这不应该发生。

更新

使用Chromium的stack_container.h效果很好!

我之所以没想过这样做的原因是我总是忽略了STL集合构造函数的allocator对象参数。我已经使用了几次模板参数来做静态池,但是我从未见过代码或写过任何实际使用过对象参数的代码。我学到了新东西。很酷!

代码有点乱,由于某种原因,GCC强迫我将分配器声明为实际项而不是将其构造为vector的allocator参数。它来自这样的事情:

typedef std::pair< const char *, const char * > comp_list_item;
typedef std::vector< comp_list_item > comp_list_type;

comp_list_type match_list;
match_list.reserve(32);

对此:

static const size_t comp_list_alloc_size = 128;
typedef std::pair< const char *, const char * > comp_list_item;
typedef StackAllocator< comp_list_item, comp_list_alloc_size > comp_list_alloc_type;
typedef std::vector< comp_list_item, comp_list_alloc_type > comp_list_type;

comp_list_alloc_type::Source match_list_buffer;
comp_list_alloc_type match_list_alloc( &match_list_buffer );
comp_list_type match_list( match_list_alloc );
match_list.reserve( comp_list_alloc_size );

每当我宣布一个新的时候我都要重复一遍。但它的工作方式就像我想要的那样。

我注意到stack_container.h定义了StackVector,我尝试使用它。但它不会从vector继承或定义相同的方法,因此它不是替代品。我不想使用向量重写所有代码,所以我放弃了它。

10 个答案:

答案 0 :(得分:41)

您不必编写全新的容器类。您可以坚持使用STL容器,但更改例如std::vector的第二个参数,以便为其提供从堆栈缓冲区分配的自定义分配器。铬作者为此写了一个分配器:

https://chromium.googlesource.com/chromium/chromium/+/master/base/stack_container.h

它的工作原理是分配一个缓冲区,你可以说它有多大。您创建容器并调用container.reserve(buffer_size);。如果溢出该大小,分配器将自动从堆中获取元素(因为它是从std::allocator派生的,在这种情况下它将只使用标准分配器的工具)。我没有尝试过,但看起来它来自谷歌,所以我觉得值得一试。

用法是这样的:

StackVector<int, 128> s;
s->push_back(42); // overloaded operator->
s->push_back(43);

// to get the real std::vector. 
StackVector<int, 128>::ContainerType & v = s.container();
std::cout << v[0] << " " << v[1] << std::endl;

答案 1 :(得分:16)

似乎boost::static_vector正是您所寻找的。来自文档:

  

static_vector是向量和数组之间的混合:与向量一样,它是一个具有连续存储的序列容器,可以改变大小,以及静态分配,低开销和固定容量的数组。 static_vector基于Adam Wulkiewicz和Andrew Hundt的高性能varray类。

     

static_vector中的元素数量可能会动态变化到固定容量,因为元素与数组类似地存储在对象本身中。

答案 2 :(得分:11)

您可能需要查看的一些选项:

Matthew Wilson(Imperfect C ++的作者)的STLSoft有一个auto_buffer模板类,它将一个默认数组放在堆栈上,但是如果它增长到大于堆栈分配将从堆中获取内存。我喜欢这个类 - 如果你知道你的容器大小通常会受到一个相当低的限制,那么你就可以获得一个本地堆栈分配数组的速度。但是,对于需要更多内存的极端情况,它仍能正常工作。

http://www.stlsoft.org/doc-1.9/classstlsoft_1_1auto__buffer.html

请注意,我自己使用的实现不是STLSoft,而是一种从中大量借用的实现。

“Lazy Programmer”为使用alloca()存储的容器实现了一篇文章。我不是这种技术的粉丝,但我会让你自己决定它是否是你想要的:

http://tlzprgmr.wordpress.com/2008/04/02/c-how-to-create-variable-length-arrays-on-the-stack/

然后有boost::array没有前两个的动态大小调整行为,但是提供了更多的vector接口,而不仅仅是使用指针作为内置数组获得的迭代器(即,你得到begin()end()size()等等):

http://www.boost.org/doc/libs/1_37_0/doc/html/boost/array.html

答案 3 :(得分:4)

您可以使用自己的std :: vector分配器,让它分配基于堆栈的存储块,类似于您的示例。 allocator类是模板的第二部分。

编辑:我从未尝试过这个,看着文档进一步让我相信你不能编写自己的分配器。我还在调查它。

答案 4 :(得分:4)

如果速度很重要,我会看到运行时间

  • 4 ns int [10],堆栈上的固定大小
  • 40 ns <vector>
  • 1300 ns <stlsoft/containers/pod_vector.hpp>

下面的一个愚蠢的测试 - 只需2推,v [0] v [1],2 pop, 在一个平台上,mac ppc,仅限gcc-4.2 -O3。 (我不知道Apple是否优化了他们的版本。)

不接受任何你没有伪造的时间。 当然,每种使用模式都不同。 尽管如此,因素> 2让我大吃一惊。

(如果mems,内存访问,是运行时的主要因素, 什么是各种实现中的额外mems?)

#include <stlsoft/containers/pod_vector.hpp>
#include <stdio.h>
using namespace std;

int main( int argc, char* argv[] )
{
        // times for 2 push, v[0] v[1], 2 pop, mac g4 ppc gcc-4.2 -O3 --
    // Vecint10 v;  // stack int[10]: 4 ns
    vector<int> v;  // 40 ns
    // stlsoft::pod_vector<int> v;  // 1300 ns
    // stlsoft::pod_vector<int, std::allocator<int>, 64> v;

    int n = (argv[1] ? atoi( argv[1] ) : 10) * 1000000;
    int sum = 0;

    while( --n >= 0 ){
        v.push_back( n );
        v.push_back( n );
        sum += v[0] + v[1];
        v.pop_back();
        v.pop_back();
    }
    printf( "sum: %d\n", sum );

}

答案 5 :(得分:3)

tr1 :: array与您的描述部分匹配。它缺少诸如push ___ back()之类的东西,但它可能值得一看作为起点。包装它并向“back”添加索引以支持push_back()等应该相当容易。

答案 6 :(得分:2)

为什么要特别将它放在堆栈上?如果你有一个alloca()的实现,你可以使用它而不是malloc()来构建一个类分配器,但你使用静态分配的数组的想法更好:它在大多数体系结构上都快,而你没有风险堆栈腐败你搞砸了。

答案 7 :(得分:2)

您可能正在使用Qt。那么您可能想要QVarLengthArraydocs)。它基本上位于std::vectorstd::array之间,静态分配一定数量,并在必要时回退到堆分配。

如果我使用的话,我更喜欢升级版。

答案 8 :(得分:1)

Boost有这个。它被称为small_vector

  

small_vector是一个类似矢量的容器,针对它的情况进行了优化   包含很少的元素。它包含一些预分配的元素   就地,这允许它避免使用动态存储   当实际元素数低于该数时,分配   预分配阈值。 small_vector的灵感来自LLVM的SmallVector   容器。与static_vector不同,small_vector的容量可以增长   超出最初的预分配能力。

     

small_vector可转换为small_vector_base,这是一种独立于预分配元素的类型   count,允许不需要模板化的客户端代码   N论点。 small_vector继承了所有vector的成员函数   支持所有标准功能,如安置,有状态分配器,   等

答案 9 :(得分:0)

如果您想在堆栈上进行分配但不希望在编译时预先定义最大大小,则可以使用StackVector,这是一个可以像这样使用的小实现:

new_stack_vector(Type, name, size)

其中Type是向量中元素的类型,name是向量的变量名,size是向量中允许的最大元素数。< / p>

size可以是变量,不需要是编译时常量! :d

示例:

new_stack_vector(int, vec, 100); //like vector<int> vec; vec.reserve(100); but on the stack :)
vec.push_back(10); //added "10" as the first item in the vector

......那就是全部!

免责声明:一般情况下,不要在堆栈上使用非常大的数组大小。就像你不应该使用int var[9999999]一样,你应该同样不要使用new_stack_vector(int, vec, 9999999)!负责任地使用。