如何分配连接字符串?

时间:2013-08-22 02:23:48

标签: c++ string memory concatenation

我想知道字符串和内存如何协同工作。

据我所知,我知道当创建一个字符串时,它会将一些字符数组+'\ 0'放入内存中。我也知道他们是不变的。那么对于像连接这样的东西,内存中会发生什么,允许你访问相同的字符串?

我不认为你连接的字符串或字符直接放在原始字符串的地址之后,因为这可能会重叠一些所需的内存。

在C#和其他语言中,您可以说:

string s = "Hello" ... s = s + '!'

这会创建一个新字符串吗?一个指向一个新的位置,上面写着“你好!”,原来永远不会被引用?

或者是否存在字符串使用的默认字符串缓冲区,允许连接中的某些空间?

3 个答案:

答案 0 :(得分:4)

您要质疑的表达式的行为是由标准明确定义的,并且是实现要遵循的行为所必需的。该标准的相关部分如下:

C ++11§21.4.8.1-11

template<class charT, class traits, class Allocator> 
    basic_string<charT,traits,Allocator>
operator+(const basic_string<charT,traits,Allocator>& lhs,
          const charT* rhs);
  

返回:lhs + basic_string<charT,traits,Allocator>(rhs)

这导致:

C ++11§21.4.8.1-3

template<class charT, class traits, class Allocator>
    basic_string<charT,traits,Allocator>
operator+(const basic_string<charT,traits,Allocator>& lhs,
          basic_string<charT,traits,Allocator>&& rhs);
  

返回:std::move(rhs.insert(0, lhs))

最后......

C ++11§21.4.2-22

basic_string<charT,traits,Allocator>&
  operator=(basic_string<charT,traits,Allocator>&& str) noexcept;
  

效果:如果* this和str不是同一个对象,则修改* this为   如表71所示。[注意:有效的实现是swap(str)。 - 结束   注意]

换句话说,为+运算符的rhs创建临时值,然后使用rhs.insert(0,lhs)修改rvalue-reference,最后将结果发送到rvalue-reference版本赋值运算符,可以有效地执行移动操作。

有关详细信息,请参阅标准的相关部分。


C ++ 03x笔记

我已经要求为C ++ 03x提供相同的演练。关于标准的最后(官方)版本,我不是肯定的,但作为参考,以下是基于ISO / IEC 14882:2003(E)。请自行决定使用。

类似的演练也是为C ++ 03x定义的墙,如下所述,标准的相关章节已正确注明。

C ++03x§21.3.7.1-5

template<class charT, class traits, class Allocator>
             basic_string<charT,traits,Allocator>
operator+(const basic_string<charT,traits,Allocator>& lhs, const charT* rhs);
  

返回:lhs + basic_string<charT,traits,Allocator>(rhs)

与C ++ 11一样,临时是从表达式的 rhs 构造的。从那里......

C ++03x§21.3.7.1-1

template<class charT, class traits, class Allocator>
             basic_string<charT,traits,Allocator>
operator+(const basic_string<charT,traits,Allocator>& lhs, 
          const basic_string<charT,traits,Allocator>& rhs);
  

返回:basic_string(lhs).append(rhs)

这里我们与C ++ 11不同。我们构造了一个临时的 lhs ,然后使用append()成员函数追加给定的 rhs (第一步中的临时)。为简洁起见,我省略了 lhs 临时的const-reference构造函数。这需要我们......

C ++03x§21.3.5.2-1

basic_string<charT,traits,Allocator>&
  append(const basic_string<charT,traits>& str);
  

返回:append(str, 0, npos)

这会将调用转发给相关成员函数,该函数接受来自要枚举的rhs的起始和停止索引。这需要我们......

C ++03x§21.3.5.2-2..5     basic_string的&安培;       append(const basic_string&amp; str,size_type pos,size_type n);

  

需要:pos&lt; = str.size()

     

抛出:out_of_range如果pos&gt; str.size()。

     

效果:确定要附加的字符串的有效长度rlen,作为n和str.size() - pos中较小的一个。如果size()&gt; = npos - rlen,则函数会抛出length_error。否则,该函数将由* this控制的字符串替换为长度为size()+ rlen的字符串,其第一个size()元素是由* this控制的原始字符串的副本,其余元素是初始元素的副本由str控制的字符串从位置pos开始。

     

返回:* this。

基本上,这会对位置参数进行一些健全性检查,然后使用连接内容执行替换。最后,现在已完成赋值的完成后,我们可以对整个惨败的目标执行赋值操作,这将带我们去...

C ++03x§21.3.1-16

basic_string<charT,traits,Allocator>&
  operator=(const basic_string<charT,traits,Allocator>& str);
  

效果:如果* this和str不是同一个对象,则修改* this,如表-43所示

     

返回:* this

表-43表示以下所需效果。

  

data() - 指向数组的已分配副本的第一个元素,其第一个元素由str.data()

指向      

size() - str.size()

     

capacity() - 至少与size()

一样大

我对此的评估是实现可以做到它想要实现的效果(在表43中,仍然需要这里显示的实现路径)。

我太累了,无法进入C ++ 98。我希望这已经足够了。

答案 1 :(得分:2)

正如评论中指出的那样,std::string不是一成不变的。

将+运算符与字符串一起使用时,如s + '!'中所示,将创建一个包含结果的新临时字符串。 s = s + '!'将此临时字符串复制回原始s,替换原始文本。这就是不可变字符串在其他语言中的工作方式。

使用+ =运算符或追加函数时,将修改字符串,并将额外字符添加到同一字符串对象中。但是,如果旧的内存缓冲区不够大,可以在内部分配新的内存缓冲区。重新分配时,通常会要求一些额外的空间,以便在不重新分配的情况下允许小的未来追加(更高效)。您可以选择使用保留功能增加内部缓冲区的最小大小。如果您知道要追加多少数据,这会更有效。

答案 2 :(得分:0)

直到C ++ 11,它依赖于实现。但是,使用+=时,图书馆使用+的优化次数(并且仍然有)要比使用s+= "!" ; 时更好。最大的区别是C ++ 11现在指定(并强制要求)这些优化。

一般规则(包括语言的过去,现在和未来规范,甚至类似语言)是:总是喜欢

string

代替您使用的示例代码。

原因是s = s + "!" ; s不是语言原语。它们只是另一种“用户”类型(恰好与编译器一起提供,但这是另一个故事)。当你写

+

调用类string的{​​{1}}方法。但是,它被迫创建一个新对象(可能与s共享一些存储空间),因为您可以在其他上下文中使用它:

t = s + "!" ;

相反,+=方法可以确保您想要附加到当前字符串,从而优化一点(例如:使用内部缓冲区中的可用空间)。