使用shared_ptr<字符串>转换为一个无序集合<字符串>

Using a shared_ptr<string> into an unordered_set<string>

本文关键字:lt 字符串 gt 一个 无序 集合 shared 使用 转换 ptr      更新时间:2023-10-16

我正试图通过将字符串放入unordered_set<string>,然后绕过shared_ptr<string>来减少字符串复制(这被认为是我的应用程序中的性能瓶颈)。很难知道何时删除了对集合中字符串的所有引用,所以我希望shared_ptr能帮助我。这是未经测试的代码,说明了我希望如何编写它:

unordered_set<string> string_pool;
:
shared_ptr<string> a = &(*string_pool.emplace("foo").first); // .first is an iterator
:
shared_ptr<string> b = &(*string_pool.emplace("foo").first);

在上面的例子中,字符串"foo"只有一个实例应该在string_pool中;a和b都应该指向它;在a和b都被破坏的时候,"foo"应该从字符串池中删除。

Template()上的文档表明,指针a可以在指针b的分配导致的重新哈希中幸存下来,但我没有明确表示。它似乎也保证了"foo"的第二个位置不会引起任何重新分配,因为它被认为已经存在于集合中。

我在这里走对了吗?我需要防止字符串池无休止地增长,但没有任何一点可以简单地清除(),也没有任何明确的字符串"所有者"。

更新1

这个问题的历史:这是一个"交通警察"应用程序,它从服务器读取数据,将数据打包到其他服务器,接收他们的答案,将这些答案打包到其他人,接收,最后组装并返回摘要答案。它包括一个应用程序协议栈,用于接收TCP消息,将其解析为字符串标量值,然后应用程序将其组装为其他TCP消息,发送、接收等。我最初使用strings、vectors<string>s和字符串引用编写它,并且valgrind报告了"大量"字符串构造函数(甚至使用-O3编译),以及集中在与字符串相关的库例程中的高CPU使用率。我被要求研究减少字符串复制的方法,并设计了一个"memref"类(char*和指向输入缓冲区的长度),它可以被复制来代替字符串本身。然后出现了需要重用输入缓冲区的情况,而其中的memref仍然需要有效,所以我花钱将每个缓冲区子字符串复制到一个中间区域(unordered_set<string>)中,并在那里设置memref点。然后,我发现在这个过程中,很难找到一个可以同时清理拘留区的地方(以防止它在没有束缚的情况下生长),我开始尝试重新设计拘留区,这样当所有被拘留的绳子的记忆都消失时,绳子就会从水池中移走。因此产生了shared_ptr。

正如我在对@Peter R的评论中提到的,我对移动语义、容器和引用的理解甚至不如现在,而且很可能我没有编写简单的基于字符串的解决方案来使用C++11所能提供的所有功能。到现在为止,我似乎已经绕了一个大圈。

unordered_set拥有字符串。当它超出范围时,您的字符串将被释放。我的第一印象是,您的方法听起来不会在可维护性或可测试性方面带来积极的体验。当然这个

shared_ptr<string> a = &(*string_pool.emplace("foo").first);

是错误的。您的无序集合中已经有了字符串的所有者。试图用shared_ptr在上面添加另一个所有权层是行不通的。你可以有一个unordered_set<shared_ptr<string>>,但即使这样,我也不推荐。

如果不了解代码库的其余部分,就很难在这里推荐"解决方案"。移动语义和传递const string&的组合应该在低级别处理大多数需求。如果仍然存在性能问题,那么它们可能是体系结构问题。当然,如果字符串没有自然所有者,并且复制成本很低,那么只使用shared_ptr<string>可能会解决您的寿命问题,只是在这种情况下不要使用unordered_set<string>

你有点任性了。shared_ptr在概念上形成了一组对象的共享所有者。。。第一个shared_ptr应该使用make_shared创建,然后在复制该值时自动创建其他副本(使用"值"语义)。你试图做的是有缺陷的:

  • string_pool本身存储不参与共享所有权的string,当shared_ptr的引用计数达到0 时,也没有任何方式通知或更新string_pool

  • share_ptr彼此之间没有关系(你给它们两个原始指针,而不是复制一个来制作另一个)

对于您的使用,您需要决定是否在某个时间点主动erase来自string_pool的字符串,否则您可能需要在string_pool中放入一个weak_ptr,并在使用它之前检查共享的string是否仍然存在。如果您还不熟悉这个概念,可以在谷歌上搜索weak_ptr。


另外,值得检查的是,您当前观察到的字符串复制是一个性能问题是否是由于编码效率低下造成的。例如:

  • 是您的string变量,在可能的情况下通过引用传递,例如:const std::string&函数参数,只要您不更改它们

  • 您是否使用static const strings而不是从字符串文字/字符数组中连续进行运行时重新创建?

  • 您是否使用合理的优化水平(例如-O2,/O2)进行编译

  • 在某些地方,保留对string的引用和字符串中的偏移量将极大地提高性能并减少内存使用(引用的字符串必须保留,只要它被间接使用)-实现"string_ref"或类似的类是非常常见的,因为这是中大型C++项目