使用STL分配器与STL向量

3

以下是基本问题。我依赖于一个API,其中有一个使用以下语法的方法:

void foo_api (std::vector<type>& ref_to_my_populated_vector);

所涉及的代码区域相当性能密集,我想避免使用堆来分配内存。因此,我创建了一个自定义分配器,在堆栈上分配向量所需的内存。因此,现在可以定义一个向量:

// 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!所以...

foo_api(my_vec); // Results in an error due to incompatible types.
// Can't convert std::vector<type> to std::vector<type, allocator>

这个问题有解决方案吗?

一般而言,在堆栈上分配数据的方式是不安全且不合理的。不要这样做,迟早会出现严重问题。如果想要确保内存一次性分配,可以考虑直接调用reserve()函数吗?或者如果你希望把所有东西都放在堆栈上,可以使用固定大小的数组。 - jalf
std::vector<type, my_stack_allocator> 应该改为 std::vector<type, my_stack_allocator<type, 100>>,是吗?在这里使用 typedef 会有帮助。 - GManNickG
1
没错,GMan -- 抱歉 :-/它是完全安全的。事实上,已经完成了:http://src.chromium.org/viewvc/chrome/trunk/src/base/stack_container.h - Andrew
1
@Andrew:太糟糕了。因为如果你能让它成为template<typename Iter> void foo_api(Iter begin, Iter end);,那就很好和通用,可以与任何容器一起使用。 - John Dibling
1
或者使用 template <typename Sequence> void foo_api(Sequence &ref);,这样就不需要太多修改 foo_api 的代码。这取决于它使用 vector 的哪些成员函数。如果 foo_api 可能添加/删除任意元素,则使用迭代器编写 API 的唯一方法是进行复制和修改。 - Steve Jessop
显示剩余2条评论
1个回答

4

您必须像函数预期的那样使用默认分配器。您有两种不同的类型,没有其他方法。

在操作向量之前,只需调用reserve以使内存分配顺利进行。

考虑可能发生的糟糕情况。该函数可能会取走您的向量并开始添加更多元素。很快,您可能会超出已分配的堆栈空间;哎呀!

如果您真的关心性能,一个更好的方法是使用自定义内存管理器替换operator new。我已经这样做了,分配可以大大改进。对于我来说,分配大小为512或更小的大小约为4个操作(移动一些指针);我使用了一个池分配器)


2
foo_api() 并不真正关心分配器。至少不是直接关心。但是 vector 是关心的。你遇到的问题并不是 STL 的缺陷(尽管它确实存在这样的缺陷),而是 foo_api() 接口的缺陷。 - John Dibling
3
@Andrew: 混合使用接口非常糟糕!能够让分配器成为类型的一部分是一件事情。如果在填充向量时使用了一个接口,然后函数使用另一个接口释放它会发生什么?这将导致未定义行为。C++的做法是将容器与其算法分离;使用迭代器。 - GManNickG
3
为什么要让世界上的每一个向量都变成一个函数指针更大,可能会变得更慢,只是为了支持像您的自定义分配器这样的不寻常用例这样糟糕的接口呢?而 foo_api 却坚持输入必须使用默认分配器的向量。如果 foo_api 想要支持其他向量,它可以通过成为一个模板并将分配器作为模板参数来实现。或者,它可以采用 C++ 的方式,并采用迭代器参数,而不是任何类型的向量。它不想让你传递你想要传递的对象 - 这不是 STL 的缺陷。 - Steve Jessop
1
@Andrew: "如果API的设计者按照你所描述的做法,那么一切都将成为模板!" 哎呀,你知道吗,标准库和boost大部分都是...模板!泛型编程是实现通用算法的最佳方式之一。你可以以相同的方式传递迭代器,就像所有基于迭代器的算法一样工作。它将接受一个模板类型作为迭代器,不可能出现类型不匹配的情况。而且是的,函数指针无法内联,因此它们速度较慢。显然,我们现在对“更好”的定义有不同意见。动态内存来自堆;也就是newdelete - GManNickG
1
你基本上选择了错误的责备对象。问题在于库制造商。他们没有按照C++的方式编程,使用模板(这些是通用、安全且通常更快的方法),而是硬编码并做出明显不正确的假设。 - GManNickG
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接