2017-06-29 34 views
4

理想情况下,一个不可变的字符串类只需要为每个字符串分配一次内存。即使引用计数可以存储在保存字符串本身的同一块内存中。可以std :: shared_ptr <std :: string const>作为引用计数的不可变字符串的有效实现吗?

甲琐碎实施stringshared_ptr将分配三个不同的存储器件为shared_ptr<string const>

  • 内存为字符串缓冲区
  • 存储器的字符串对象
  • 存储器的引用计数

现在,我知道当使用std::make_shared()时,智能impl将最后两个组合成一个单独的分配。但是这仍然会留下两个分配。

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

我知道某些字符串实现已经对短字符串使用了这样的优化,但是我在执行这个操作之后不管字符串长度如何。

我的问题是:我的推理的声音?实际上是否允许并能够执行此操作?我可以从一个高质量的标准库中合理地期望实现这种优化吗?你知道这样做的当代库实现吗?

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

+0

GCC 4.x的已引用计数'的std :: string':https://stackoverflow.com/questions/12520192/is-stdstring-refcounted-in-gcc-4-x-c11。如果使用'-D_GLIBCXX_USE_CXX11_ABI = 0'进行编译,后续版本的GCC仍然有它。 –

+0

看看[allocate_shared](http://en.cppreference。com/w/cpp/memory/shared_ptr/allocate_shared) – Caleth

+0

@Caleth这没有什么帮助,它和'make_shared'非常类似,只是它使用了明确的分配器。 –

回答

1

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

另一方面,Boost具有boost::make_shared,它也可以采用数组大小​​参数。一旦你有了,你就是金;你也几乎你想要的什么shared_ptr<char[]>(除了实际上是一个std::string

如果你不想使用升压你可以只推出自己的。它可能不会那么难。

还有一点需要考虑的是,如果你只会创建O(1)字符串,那么永远不要删除它们,并且要传递原始指针(或std::string_view),这样可以避免任何复制或摆弄(引用计数实际上很慢,因为它们使用原子操作。)

您也可以使用像这样的内部机制。

+0

如果使用C++ 17,你可以将'boost :: shared_ptr '包装在一个简单的类中,这样你就可以轻松访问'std :: string_view'。 – aschepler

+0

感谢您指出这一点。然而,没有std :: string的接口是一个明显的缺点。我想过使用std :: string_view,但我怎样才能真正获得数组的大小? –

+0

@ sh-嗯,看起来你实际上不行。我想如果你这样做了,你将不得不使用以null结尾的字符串。 – Dan

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; 
    } 
} 
+1

是不是由shared_ptr使用的分配器分配的字符串对象不同于字符串用于分配其存储的字符串对象? – Dan

+0

如果你让它们一样 – Caleth

+0

根据cppreference,'allocate_shared'使用分配器的一个副本。 – Dan

相关问题