【问题标题】:std::string with no free store memory allocation没有空闲存储内存分配的 std::string
【发布时间】:2011-07-26 10:56:09
【问题描述】:

我有一个非常相似的问题

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

但我认为值得再问一次。

我想要一个带有本地存储的std::string,它会溢出到免费存储中。 std::basic_string 提供了一个分配器作为模板参数,所以看起来要做的事情就是写一个带有本地存储的分配器并使用它来参数化basic_string,如下所示:

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

我尝试编写inline_allocator 类,它可以按照您期望的方式工作:它保留10 个字节用于存储,如果basic_string 需要超过10 个字节,那么它调用::operator new()。我无法让它工作。在执行上面这行代码的过程中,我的 GCC 4.5 标准字符串库调用了 inline_allocator 的复制构造函数 4 次。我不清楚是否有一种明智的方法可以为inline_allocator 编写复制构造函数。

在另一个 StackOverflow 线程中,Eric Melski 提供了这个指向 Chromium 中的类的链接:

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

这很有趣,但它不是std::string 的直接替代品,因为它将std::basic_string 包装在一个容器中,因此您必须调用重载的operator-&gt;() 才能访问std::basic_string

我找不到任何其他解决此问题的方法。莫非没有好的解决办法?如果这是真的,那么std::basic_stringstd::allocator 的概念是否存在严重缺陷?我的意思是,对于std::basic_stringstd::allocator,这似乎应该是一个非常基本和简单的用例。我想std::allocator 的概念主要是为池设计的,但我认为它也应该涵盖这一点。

如果重写字符串库以便basic_string 使用其分配器的移动构造函数而不是复制构造函数。有谁知道这个结果的前景是什么?

我的应用程序需要每秒构造一百万个微小的 ASCII 字符串,所以我最终编写了自己的基于 Boost.Array 的固定长度字符串类,效果很好,但这仍然困扰着我。

【问题讨论】:

  • 作为过去与自定义分配器搏斗的人,我很想听听权威人士就这个话题发表讲话。
  • +1 表示“免费商店”。哦,因为这是一个很好的问题。
  • Generic: A Policy-Based basic_string Implementation 的链接是关于 basic_string 存储的精彩讨论。不过,我仍然想知道是否可以在我的问题中编写 inline_allocator 类。简要浏览一下 Alexandresu 的文章并不能告诉我答案,我得花点时间了解它。
  • Chromium 的 stack_container.h 的链接已失效。这是一个更新的:src.chromium.org/viewvc/chrome/trunk/src/base/containers/…

标签: c++ c++11 memory-management stdstring


【解决方案1】:

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

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

Howard Hinnant(他管理 C++ 委员会的 STL 小组,正在从头开始为 C++0x 实施新的 STL)探索了 stack-based allocators on his website,您可以从中获得灵感。

【讨论】:

  • 这很有趣。我查看了 Hinnant 的 stack_alloc 课程,它对 std::vector 非常有效,但很可惜,不适用于 std::basic_string。我相信,问题又出在std::basic_string 使用stack_alloc 复制构造函数的方式上。
  • 而且我认为由于您描述的要求,允许以这种方式使用复制构造函数。
  • @James:是的,不幸的是,由于标准几乎暗示分配器是无状态的(或者至少相同类型的所有分配器或多或少共享相同的状态,不知何故),复制完全允许。
【解决方案2】:

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

【讨论】:

  • 这是 Alexandrescu 的一篇很棒的文章,谢谢。对于如何为 basic_string 自定义存储的问题,这是一个很好的答案。
【解决方案3】:

这通常是不必要的。它被称为“短字符串优化”,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 个字符分配空间,尽管我已经有一段时间没有看过了,所以我不能发誓(并且在它们的标题中追踪很多东西往往是痛苦,所以我宁愿避免看)。

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

【讨论】:

  • VC++ 为 SSO 分配了 16 个字节,在我看来这太小而不能普遍使用,因为 std::wstring 在 Windows 代码中比 std::string 更常用,并且它只允许 8 个字符一个std::wstring(如果经常使用.c_str(),则为7)。
  • 标头不属于 MinGW 库扩展(不是 std::string 的一部分)吗?
  • @UncleBens:我不这么认为,但我可能弄错了。还有一些替代方案(例如,STLPort、libc++)在std::string 的实现中肯定会使用 SSO。
  • @ildjarn:再看一遍,你说得很对,我同意这有点小尺寸。我仍然认为修改它并重新编译标准库比尝试破解它更容易。
  • @James Brock:当然——我从不怀疑。至于控制阈值,在大多数情况下,只需在标头中找到enum,根据需要对其进行修改,并(可能)重新编译标准库以匹配(但由于它是模板,因此很有可能您只需要修改标题)。
【解决方案4】:

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

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

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

You can see an example of the verbose method (still using the chromium stuff) in my question about stack vectors.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-24
    • 2011-07-13
    • 2013-08-17
    • 2018-10-09
    • 1970-01-01
    • 2015-03-21
    • 1970-01-01
    • 2021-11-06
    相关资源
    最近更新 更多