我最近一直在阅读有关不可变字符串Why can't strings be mutable in Java and .NET?和Why .NET String is immutable?以及有关为什么D选择不可变字符串的一些内容。似乎有很多好处。
更不用说大多数新语言都有不可变的字符串,D2.0,Java,C#,Python等。
C ++会从不可变字符串中受益吗?
是否有可能在c ++(或c ++ 0x)中实现具有所有这些优点的不可变字符串类?
更新
对不可变字符串const_string和fix_str进行了两次尝试。五年内都没有更新。它们甚至被使用过吗?为什么const_string没有进入boost?
答案 0 :(得分:44)
我发现此帖子中的大多数人并不真正理解immutable_string
是什么。它不仅仅是关于常数。 immutable_string
的真正强大之处在于性能(即使在单线程程序中)和内存使用情况。
想象一下,如果所有字符串都是不可变的,并且所有字符串都实现为
class string {
char* _head ;
size_t _len ;
} ;
我们如何实现sub-str操作?我们不需要复制任何字符。我们所要做的就是分配_head
和_len
。然后子字符串与源字符串共享相同的内存段。
当然,我们不能仅使用两个数据成员实现immutable_string。实际实现可能需要引用计数(或飞加权)内存块。喜欢这个
class immutable_string {
boost::fly_weight<std::string> _s ;
char* _head ;
size_t _len ;
} ;
在大多数情况下,内存和性能都会比传统字符串更好,特别是当你知道自己在做什么时。
当然C ++可以从不可变的字符串中受益,并且拥有一个很好。我查看了Cubbi提到的boost::const_string
和fix_str
。这些应该是我所说的。
答案 1 :(得分:23)
作为意见:
真的值得做(作为标准库功能)吗?我会说不。 const的使用为您提供了本地不可变的字符串,系统编程语言的基本特性意味着您确实需要可变的字符串。
答案 2 :(得分:9)
我的结论是C ++不需要不可变模式,因为它具有const语义。
在Java中,如果您有一个Person
类并且使用String name
方法返回该人员的getName()
,则您唯一的保护是不可变模式。如果它不存在,那么你需要整天clone()
你的字符串(因为你必须处理非典型值对象的数据成员,但仍然需要保护)。
在C ++中,您有const std::string& getName() const
。因此,您可以将SomeFunction(person.getName())
写为void SomeFunction(const std::string& subject)
。
答案 3 :(得分:3)
你当然不是唯一一个那样的人。事实上,Maxim Yegorushkin有一个const_string库,它似乎已被写入包含在boost中。这是罗兰·皮宾格(Roland Pibinger)的一个小型图书馆fix_str。我不确定在运行时完整的字符串实习是多么棘手,但大多数优点在必要时都可以实现。
答案 4 :(得分:3)
我认为这里没有明确的答案。它是主观的 - 如果不是因为个人品味,那至少是因为最经常处理的代码类型。 (仍然是一个有价值的问题。)
当内存很便宜时,不可变字符串很棒 - 当开发C ++时,这种情况并非如此,并且在C ++所针对的所有平台上都不是这样。 (OTOH在更有限的平台上C似乎比C ++更常见,所以这个论点很弱。)
您可以在C ++中创建一个不可变的字符串类,并且可以使它与std::string
基本兼容 - 但是在与具有专用优化和语言功能的内置字符串类进行比较时,您仍然会丢失。
std::string
是我们获得的最佳标准字符串,因此我不希望看到任何混乱。但我很少使用它;从我的角度来看,std::string
有太多的缺点 。
答案 5 :(得分:2)
const std::string
你去吧。字符串文字也是不可变的,除非你想进入未定义的行为。
编辑:当然这只是故事的一半。 const字符串变量没有用,因为您无法使其引用新字符串。对const字符串的引用会这样做,除了C ++不允许像其他语言(如Python)一样重新分配引用。最接近的是指向动态分配字符串的智能指针。
答案 6 :(得分:1)
不可变的字符串非常好如果,只要有必要创建一个新字符串,内存管理器将始终能够确定每个字符串引用的位置。在大多数平台上,可以以相对适中的成本提供对这种能力的语言支持,但是在没有内置语言支持的平台上更加困难。
例如,如果想要在x86上设计支持不可变字符串的Pascal实现,则字符串分配器必须能够遍历堆栈以查找所有字符串引用;唯一的执行时间成本是需要一致的函数调用方法[例如不使用尾调用,并且每个非叶函数都保持一个帧指针]。分配有new
的每个内存区域需要有一个位来指示它是否包含任何字符串,而那些包含字符串的内存需要有一个内存布局描述符的索引,但这些成本会非常小。
如果GC没有表来遍历堆栈,那么代码必须使用句柄而不是指针,并且当局部变量进入范围时让代码创建字符串句柄,并在它们被破坏时删除句柄超出范围。更大的开销。
答案 7 :(得分:0)
Qt还使用带有copy-on-write的不可变字符串 关于通过合适的编译器真正为您带来多少性能,存在一些争论。
答案 8 :(得分:0)
常量字符串对于值语义没有多大意义,并且共享不是C ++最大的优势之一......
答案 9 :(得分:-1)
Ruby中的字符串是可变的。
$ irb
>> foo="hello"
=> "hello"
>> bar=foo
=> "hello"
>> foo << "world"
=> "helloworld"
>> print bar
helloworld=> nil
- 简单线程安全
我倾向于忘记安全论点。如果您想要是线程安全的,请将其锁定,或者不要触摸它。 C ++不是一种方便的语言,有自己的约定。
- 更安全
没有。只要你有指针算术和不受保护的地址空间访问,就忘了保持安全。更安全的是编码无辜,是的。
- 在大多数用例中,内存效率更高。
除非你实现了CPU密集型机制,否则我看不到。
- 便宜的子串(标记化和切片)
这将是一个非常好的观点。可以通过引用带有反向引用的字符串来完成,其中对字符串的修改会导致副本。令牌化和切片变得免费,突变变得昂贵。
答案 10 :(得分:-4)
C ++字符串是线程安全的,所有不可变对象都保证是线程安全的,但Java的StringBuffer是可变的,就像C ++字符串一样,并且它们都是线程安全的。为什么要担心速度,使用const关键字定义方法或函数参数,告诉编译器字符串在该范围内是不可变的。此外,如果字符串对象是按需不可变的,等待你绝对需要使用字符串时,换句话说,当你将其他字符串附加到主字符串时,你有一个字符串列表,直到你真正需要整个字符串然后它们被连接在那一点。
不可变和可变对象以我所知的相同速度运行,除了它们的方法是利弊。常量基元和变量基元以不同的速度移动,因为在机器级别,变量被分配给需要一些二进制操作的寄存器或存储器空间,而常量是不需要任何这些的标签,因此更快(或者减少工作量)。仅适用于基元而不适用于对象。