字符串 += s1 和字符串 = 字符串 + s1 之间的区别
Difference between string += s1 and string = string + s1
当我使用fans = fans + s[i]
时,我的一个程序超过了时间限制,而当我使用fans += s[i]
时,它被接受......为什么会这样? 为了解释更多,fans是一个字符串,s也是一个字符串,所以在迭代字符串s时,我只想要s的一些字符,所以我正在创建一个新的字符串风扇。现在有两种方法可以为我的新弦乐迷添加角色。下面提到了这个问题
fans = fans + s[i]; // gives Time limit exceeded
fans += s[i]; // runs successfully
> 对于内置类型,a += b
与a = a + b
完全相同(除了a
只计算一次),但对于类,这些运算符被重载并调用不同的函数。
在您的示例中,fans = fans + s[i]
创建一个临时字符串,并将其分配给(移动)fans
,但fans += s[i]
不会创建该临时字符串,因此它可能更快。
std::string
有成员operator +
和operator +=
。前者通常通过中间临时的方式与后者一起实现。实际上看起来像这样(如果您想知道您的实现源代码是做什么的),请检查您的实现源代码):
/// note reference return type
std::string& operator +=(char c)
{
this->append(c);
return *this;
}
// note value return type
std::string operator +(char c) const
{
std::string tmp = *this;
tmp += c; // or just tmp.append(c) directly
return tmp;
}
tmp
的设置很昂贵。通过对调用方端最终目的地的移动赋值语义,整体功能可以(并且通常)变得更好,但临时的费用仍然存在。做几次,你不会注意到差异。做几千次,或者几百万次,等等,这可能意味着一个不同的世界。
如果使用fans=fans+s[i]
,则字符串将在每次循环传递中复制。新元素将被添加到字符串的副本中,结果将被重新分配给变量fans
。在此之后,必须删除旧字符串,因为它不再被引用。这需要很多时间。
如果使用增强赋值fans+=s[i]
则不会在每次循环传递中复制字符串,并且无需删除引用变量,因为此处没有引用变量。这样可以节省大量时间。
希望现在你能明白!!
对于基本类型,a = a + b
和a += b
的意思是一样的。
对于任意类类型,a = a + b
和a += b
是不相关的;它们查找不同的运算符,这些运算符可以执行任意操作。 它们实际上是不相关的是代码气味,这是设计问题的标志。
a = a + b
变得粗略operator=( a, operator+( a, b ) )
;实际的查找规则有点复杂(涉及成员运算符和非成员运算符,以及=
没有非成员运算符的事实等),但这是它的核心。
a += b
在类似的意义上变得operator+=( a, b )
。
现在,在+=
方面实现+
是一种常见的模式;如果你这样做,你会得到:
a = a + b
成为
a = ((auto)(a) += b);
其中(auto)
是新的 C++20/C++23"创建参数的临时副本"功能。
从根本上说,a+=b
可以直接重用a
的内容,而a = a + b
不能;在评估a+b
的那一刻,它不知道a
很快就会被覆盖。
一些库使用称为"表达式模板"的技术来处理此问题;a+b
不是一个值,而是表达式a+b
的编译时描述,当分配给a
时,它实际上用于用数据填充a
。 使用表达式模板,消除了a+=b
知道的多于a=a+b
的基本问题。
现在,具体来说std::string
,a+b
创建一个临时字符串对象,然后a=(a+b)
将其移动到a
中(它可以重用临时字符串对象的缓冲区或a
的缓冲区,标准对此事保持沉默)。
a+=b
必须重复使用a
缓冲区中的任何多余容量。 因此,如果您a.reserve(1<<30)
(10 亿),a+=b
无法分配更多。