33

I have a question very similar to

How do I allocate a std::string on the stack using glibc's string implementation?

but I think it's worth asking again.

I want an std::string with local storage that overflows into the free store. std::basic_string provides an allocator as a template parameter, so it seems like the thing to do is to write an allocator with local storage and use it to parameterize the basic_string, like so:

std::basic_string<
char, 
std::char_traits<char>, 
inline_allocator<char, 10> 
> 
x("test");

I tried to write the inline_allocator class that would work the way you'd expect: it reserves 10 bytes for storage, and if the basic_string needs more than 10 bytes, then it calls ::operator new(). I couldn't get it to work. In the course of executing the above line of code, my GCC 4.5 standard string library calls the copy constructor for inline_allocator 4 times. It's not clear to me that there's a sensible way to write the copy constructor for inline_allocator.

In the other StackOverflow thread, Eric Melski provided this link to a class in Chromium:

http://src.chromium.org/svn/trunk/src/base/stack_container.h

which is interesting, but it's not a drop-in replacement for std::string, because it wraps the std::basic_string in a container so that you have to call an overloaded operator->() to get at the std::basic_string.

I can't find any other solutions to this problem. Could it be that there is no good solution? And if that's true, then are the std::basic_string and std::allocator concepts badly flawed? I mean, it seems like this should be a very basic and simple use case for std::basic_string and std::allocator. I suppose the std::allocator concept is designed primarily for pools, but I think it ought to cover this as well.

It seems like the rvalue-reference move semantics in C++0x might make it possible to write inline_allocator, if the string library is re-written so that basic_string uses the move constructor of its allocator instead of the copy constructor. Does anyone know what the prospect is for that outcome?

My application needs to construct a million tiny ASCII strings per second, so I ended up writing my own fixed-length string class based on Boost.Array, which works fine, but this is still bothering me.

4

4 回答 4

16

Andrei Alexandrescu,杰出的 C++ 程序员,撰写了“现代 C++ 设计”,曾经写过一篇关于使用可定制存储系统构建不同字符串实现的精彩文章。他的文章(在此处链接)描述了如何将上述内容作为一个更通用的系统的特例来处理,该系统可以处理各种巧妙的内存分配要求。这并没有过多地谈论std::string并更多地关注完全自定义的字符串类,但您可能需要研究它,因为实现中有一些真正的宝石。

于 2011-03-30T22:52:02.007 回答
10

C++2011 真的会在这里帮助你:)

事实是allocatorC++03 中的概念被削弱了。其中一个要求是类型的分配器A应该能够从类型的任何其他分配器中释放内存A......不幸的是,这个要求也与每个都挂接到自己的池的有状态分配器不一致。

Howard Hinnant(他管理 C++ 委员会的 STL 子组,并且正在为 C++0x 从头开始​​实施新的 STL)在他的网站上探索了基于堆栈的分配器,您可以从中获得灵感。

于 2011-03-31T07:18:48.180 回答
6

这通常是不必要的。它被称为“短字符串优化”,并且大多数实现std::string已经包含它。它可能很难找到,但无论如何它通常都在那里。

例如,这sso_string_base.h是 MinGW 的相关部分:

  enum { _S_local_capacity = 15 };

  union
  {
_CharT           _M_local_data[_S_local_capacity + 1];
size_type        _M_allocated_capacity;
  };

_M_local_data成员是相关的 - 用于存储(最多)15 个字符(加上一个 NUL 终止符)的空间,而不在堆上分配任何空间。

如果没记错的话,VC++ 中包含的 Dinkumware 库会为 20 个字符分配空间,尽管我已经有一段时间没有看了,所以我不能发誓(而且在它们的标题中追踪很多东西往往很痛苦,所以如果可以的话,我宁愿避免看)。

无论如何,我很有可能你一直在从事那种被称为过早优化的非常流行的传递时间。

于 2011-03-30T22:47:20.583 回答
2

我相信来自 Chromium 的代码只是将东西包装到一个漂亮的外壳中。但是您可以在不使用 Chromium 包装容器的情况下获得相同的效果。

因为分配器对象经常被复制,所以它需要保存一个指向内存的引用或指针。所以你需要做的是创建存储缓冲区,创建分配器对象,然后使用分配器调用 std::string 构造函数。

这将比使用包装类更冗长,但应该得到相同的效果。

您可以在我关于堆栈向量的问题中看到详细方法的示例(仍然使用铬的东西)

于 2011-03-30T21:47:51.317 回答