han*_*rik 29 c++ string stdstring c++11 c++03
以前这是std::string::c_str()
工作,但是从C ++ 11开始,data()
它也提供了工作,为什么还要c_str()
添加null终止字符std::string::data()
?在我看来,这似乎是在浪费CPU周期,如果null终止字符根本不相关而仅data()
使用,C ++ 03编译器就不必关心终止符,也不必每次调整字符串大小时,都必须向终止符写入0,但是由于data()
-null-guarantee,C ++ 11编译器不得不浪费每次写入字符串大小时都要写入0的周期,因此,这可能会使代码变慢,我想他们有一定理由要添加该保证,那是什么?
Aco*_*orn 28
这里有两点要讨论:
理论上,C ++ 03的实现可能已经避免了终止分配空间和/或可能需要执行拷贝(如不共享)。
但是,所有理智的实现都为null终止符分配了空间以便支持c_str()
开始,因为否则,如果这不是一个琐碎的调用,则实际上将无法使用。
确实,某些非常(1999),非常老的实现(2001)编写了\0
每一个c_str()
调用。
但是,为了避免在C ++ 11发布之前采用这种方式,主要的实现方式发生了变化(2004)或已经很像那样(2010),因此,当新标准出现时,对于许多用户而言,它们没有任何变化。
现在,无论C ++ 03实现是否应该这样做:
在我看来,这似乎浪费了CPU周期
并不是的。如果调用c_str()
不止一次,则已经多次写入已在浪费周期。不仅如此,您还使高速缓存层次结构混乱,在多线程系统中考虑这一点很重要。回想一下,多核/ SMT CPU在2001年至2006年之间开始出现,这解释了向现代非CoW实施的转换(即使在此之前的几十年中就已经有了多CPU系统)。
唯一会保存任何内容的情况是,如果您从未致电过c_str()
。但是,请注意,在重新调整字符串大小时,无论如何都在重写所有内容。额外的字节将几乎无法测量。
换句话说,通过不将终止符写成调整大小,您会使自己暴露于更差的性能/延迟中。通过同时写入一次,您必须执行字符串的副本,性能行为将更加可预测,并且,如果最终使用c_str()
,尤其在多线程系统上,可以避免性能下降。
eer*_*ika 25
变更的优点:
当data
也保证了空终止,程序员不需要知道之间的差异模糊的细节c_str
,并data
因此将避免传递字符串不空终止的保证成空要求终止函数未定义行为。此类函数在C接口中无处不在,并且C接口在C ++中经常使用。
下标运算符也已更改为允许对进行读取访问str[str.size()]
。不允许访问str.data() + str.size()
将是不一致的。
虽然在调整大小等操作时未初始化空终止符可能会使该操作更快,但会强制进行初始化,c_str
从而使该功能变慢¹。删除的优化案例并不是普遍更好的选择。考虑到第2点中提到的更改,缓慢性也会影响下标运算符,这肯定对性能不可接受。这样,无论如何,空终止符都会存在,因此保证它不会有不利影响。
好奇的细节:str.at(str.size())
仍然引发异常。
PS还有一个更改,就是要确保字符串具有连续的存储(这就是为什么data
首先提供字符串的原因)。在C ++ 11之前,实现可能使用绳索字符串,并在调用时重新分配c_str
。(据我所知)没有任何主要的实现方案选择利用这种自由。
PPS例如,GCC的libstdc ++的旧版本显然仅将空终止符设置为c_str
3.4版。有关详细信息,请参见相关的提交。
¹导致此问题的一个因素是C ++ 11中的语言标准引入了并发性。并发的非原子性修改是数据争用未定义的行为,这就是为什么允许C ++编译器进行积极的优化并将其保存在寄存器中的原因。因此,用普通C ++编写的库实现将具有UB用于并发调用.c_str()
在实践中(请参阅注释),因为有多个线程编写相同的内容,所以不会引起正确性问题,因为实际CPU的asm没有UB。C ++ UB规则意味着,多个线程实际上在不同步的情况下修改了一个std::string
对象(而不是调用c_str()
),这是编译器+库可以假定不会发生的事情。
但是它会弄脏缓存并阻止其他线程读取它,因此仍然是一个糟糕的选择,尤其是对于可能具有并发读取器的字符串。.c_str()
由于商店的副作用,它也不会从根本上优化。
Dav*_*aim 13
问题的前提是有问题的。
字符串类必须做很多事情,例如分配动态内存,将字节从一个缓冲区复制到另一个缓冲区,释放基础内存等等。
一件糟糕的mov
组装说明让您烦恼吗?相信我,这不会对您的表现造成0.5%的影响。
在编写编程语言运行时时,您不必沉迷于每条小的汇编指令。您必须明智地选择优化方案,而优化不引人注意的空终止并不是其中之一。
在这种特定情况下,与C兼容比空终止更为重要。