Using STL Allocator with STL Vectors
这是基本问题。我依赖一个API,它的方法使用以下语法:
1
| void foo_api (std::vector<type>& ref_to_my_populated_vector); |
所讨论的代码区域是相当高的性能密集型的,我希望避免使用堆来分配内存。因此,我创建了一个自定义分配器,它分配堆栈上向量所需的内存。现在我可以定义一个向量为:
1 2 3 4 5
| // Create the stack allocator, with room for 100 elements
my_stack_allocator<type, 100> my_allocator;
// Create the vector, specifying our stack allocator to use
std::vector<type, my_stack_allocator> my_vec(my_allocator); |
一切都好。与标准向量相比,使用堆栈分配向量进行的性能测试显示,性能大约快了4倍。问题是,我不能打电话给foo-api!所以…
1 2
| foo_api(my_vec); // Results in an error due to incompatible types.
// Can't convert std::vector<type> to std::vector<type, allocator> |
有解决办法吗?
- 一般来说,在堆栈上分配数据没有安全和明智的方法。别那样做,它迟早会坏的。那么简单地调用reserve()以确保一次性分配内存呢?或者,如果需要堆栈上的所有内容,则使用固定大小的数组
- std::vector应该是std::vector是吗?typedef在这里会有所帮助。
- 我猜你不能更改foo_api()的签名和实现吗?
- 是的,GMAN——对不起:-/这很安全。事实上,它已经完成了:src.chromium.org/viewvc/chrome/trunk/src/base/stack_containe&zwnj;&8203;r.h
- 没错,约翰。
- @安德鲁:太糟糕了。因为如果您可以使它成为template void foo_api(Iter begin, Iter end);,它将是很好的和通用的,可以与任何容器一起使用。
- 或者可能是template void foo_api(Sequence &ref);,这样就不必对foo-api的代码进行过多的修改。它取决于它使用的向量的成员函数。foo-api可以添加/删除任意元素,在这种情况下,使用迭代器编写api的唯一方法是复制和修改。
您必须按照函数的预期使用缺省分配器。你有两种不同的类型,这是不可能的。
在对向量进行操作之前,只需调用reserve就可以避免内存分配。
想想可能发生的坏事。这个函数可以得到你的向量并开始添加更多的元素。很快,您就可以将分配的堆栈空间溢出;哎呀!
如果您真的关心性能,一个更好的方法是用自定义内存管理器替换operator new和kin。我已经这样做了,分配可以大大提高。对我来说,分配大小为512或更小的大约是4个操作(四处移动几个指针);我使用了池分配器。
- 分配程序的编写方式是这样的:如果请求的内存多于堆栈上保留的内存,那么它将求助于堆。在我看来,这种情况不能由STL来处理真的有点可悲。似乎如果分配器由几个函数指针和一段用户定义的数据组成,这类事情是可能的。
- 在我看来,这并不可悲。具有不同分配器的向量只是不同的类型。它应该如何在两者之间转换?推送时它会调用哪个分配器?去除?问题是你的接收代码。作为一个模板,它应该简单地将容器作为一个整体。
- 我没有选择使用STL。我自己更像一个C人。在C语言中,这可以通过几个函数指针来完成。使用分配器的对象不需要知道关于分配器本身的任何信息。它只需要一个接口。foo-api关心使用哪个分配器吗?
- foo_api()并不真正关心分配器。无论如何,不是直接的。但向量是这样的。您所遇到的问题并不是STL中存在缺陷的证据(尽管它确实有缺陷可以确定),而是foo_api()接口中的缺陷。
- @安德鲁:混合界面是毁灭性的!分配程序是类型的一部分,这是件好事。当填充向量时使用一个接口,然后函数使用单独的接口释放向量时会发生什么?未定义的行为。C++方法是把容器与它的算法分开,使用迭代器。
- 哦,我同意…这就是为什么接口应该是cotainer的一部分…我分配的向量应该有指向我指定的任何分配器接口的函数指针。
- 为什么要让世界上的每一个向量都成为一个更大、可能更慢的函数指针,这样糟糕的接口(如foo_API)就可以支持像定制分配器这样的异常用例?它是foo-api,它坚持输入必须是使用默认分配器的向量。如果foo-api想要支持其他向量,它可以通过作为模板并将分配器作为模板参数来实现。或者,它可以用C++方式来执行,而采用迭代器参数,而不是任何类型的向量。它不希望你把你想要传递的对象传递给它——这不是STL中的一个缺陷。
- 不是真的。如果API的设计者做了您所描述的,那么一切都将是一个模板!我也不能传递迭代器,因为类型错误。函数指针更大?更慢的?对于像向量这样微不足道的东西,最好使用new/delete。
- @安德鲁:"如果API的设计者做了你所描述的,一切都将是一个模板!"呃,猜猜标准库和Boost主要是什么…模板!通用编程是实现通用算法的最佳方法之一。您传递迭代器的方式与所有基于迭代器的算法的工作方式相同。它将使用模板化类型作为迭代器,不可能使类型不匹配。是的,函数指针不能是内联的,因此速度较慢。显然,现在我们不同意"更好"这个词。动态内存来自堆;那就是new和delete。
- 你基本上是选错了事情来怪你。故障是图书馆制造者的。不是用C++的方式编程,而是使用模板(它们是通用的、安全的,而且通常快得多),它们硬编码并做出明显不正确的假设。
- 再加上一句:"你说一切都是模板",这是件坏事。:)