std :: shared_ptr <std :: string const =“”>可以作为引用计数的不可变字符串的有效实现吗?

时间:2017-06-29 11:17:31

标签: c++ string immutability c++-standard-library

理想情况下,不可变的字符串类只需要为每个字符串分配一个内存。甚至引用计数也可以存储在保存字符串本身的同一块内存中。

stringshared_ptr的简单实现会为shared_ptr<string const>分配三个不同的内存:

  • 字符串缓冲区的内存
  • 字符串对象的内存
  • 参考计数的内存

现在,我知道在使用std::make_shared()时,智能实现可以将最后两个组合成一个分配。但这仍然会留下两个分配。

当你知道字符串是不可变的时,字符串缓冲区将不会被重新分配,因此应该可以将它与字符串对象集成,只留下一个分配。

我知道一些字符串实现已经对短字符串使用了这样的优化,但我正在执行一个实现,无论字符串长度如何。

我的问题是:我的推理是否合理?实现是否允许并且能够执行此操作?我可以合理地期望从高质量的标准库中实现这种优化吗?你知道当代图书馆的实施吗?

或者这是我必须自己实施的东西吗?

2 个答案:

答案 0 :(得分:1)

我认为唯一的方法是make_shared接受运行时变量大小的数组。标准的does not,即使是c ++ 17(它为数组增加了对shared_ptr的支持)。

另一方面,

Boost具有boost::make_shared,它也可以采用数组大小​​参数。一旦你拥有了,你就会变得金黄;你得到的shared_ptr<char[]>几乎可以满足您的需求(除了实际上是std::string

如果你不想使用提升,你可以自己动手。它可能不会那么难。

要考虑的其他事情是,如果你只创建O(1)字符串,那么永远不会删除它们并传递原始指针(或std::string_view s)会快得多。这可以避免任何复制或摆弄引用计数。 (引用计数实际上非常慢,因为它们使用原子操作。)

您还可以使用std::unordered_set<std::string>等实习机制。

答案 1 :(得分:0)

您可能需要为所有分配使用自定义分配器。

class ImmutableStringAllocator;

template<typename CharT>
using immutable_string = std::basic_string<CharT, std::char_traits<CharT>, ImmutableStringAllocator>

template<size_t N>
immutable_string<char> make_immutable_string(char (&data)[N])
{
    ImmutableStringAllocator alloc(N);
    // going for basic_string::basic_string(charT *, size_t, Allocator)
    return allocate_shared<immutable_string<char>>(alloc, data, N, alloc);
}

class ImmutableStringAllocator {
    size_t len;
    size_t offset;
    char * buf;
    std::reference_wrapper<char *> ref;
public:
    // Normal Allocator stuff here
    ImmutableStringAllocator(size_t N) : len(N), buf(nullptr), offset(0), ref(buf) {}

    ImmutableStringAllocator(const ImmutableStringAllocator & other) : len(other.len), buf(nullptr), offset(other.offset), ref(other.buf) {}

    ImmutableStringAllocator operator=(const ImmutableStringAllocator & other) 
    { 
         assert(buf == nullptr); 
         temp(other); 
         swap(*this, temp); 
         return *this; 
    }

    pointer allocate(size_type n, const_void_pointer hint)
    {
         if (!ref.get()) { buf = ::new(n + len); offset = n; return buf; }
         return ref.get() + offset;
    }
}