string超出内存限制:C++ string的operator+=、operator+、append与push_back

沉寂了很久,从今天开始重操旧业,做点题啥的,一方面是比较功利性的,另一方面……好吧确实是比较功利性的,没什么特殊的原因。

今天遇到了一个很有意思的问题:string超出内存限制。

题目其实很简单,也没什么太多的坑;虽然说测试用例里的字符串确实很大,但理论上来说是不应该出现这个问题的,并且我在本地测试也没有问题(本地是MinGW的g++)。

后来发现问题出在这里,比如我要实现一个字符串反转(以下是示意,实际使用大概率还是使用STL提供的reverse方法):

string reverse(string S)
{
    int length = S.length();
    string result;
    for (int i = length - 1; i >= 0; --i)
    {
        result = S[i] + result; // here
    }
    return result;
}

就是这个operator+导致的超出内存限制。为什么呢?我们来看看C++ standard里是怎么写的:

// operator+
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);
Returns: basic_string<charT,traits,Allocator>(lhs).append(rhs)

template<class charT, class traits, class Allocator>
  basic_string<charT,traits,Allocator>
    operator+(basic_string<charT,traits,Allocator>&& lhs,
              const basic_string<charT,traits,Allocator>& rhs);
Returns: std::move(lhs.append(rhs))
// operator+=
basic_string&
  operator+=(const basic_string& str);
Effects: Calls append(str).
Returns: *this.

basic_string& 
  operator+=(charT c);
Effects: Calls push_back(c);
Returns: *this.

各看一个例子就已经可以说明问题了,多放几个是因为有助于后续的说明。从函数的signature中可以看出,operator+=返回的是引用,而operator+返回的是,使用operator+在C++11之前很有可能会导致大量的拷贝行为,需要有额外的空间来存储临时创建的右值,在string很大的情况下,就有可能超出内存限制。

在C++11之后,引入了右值引用和move函数,避免了这个问题。从standard中也可以看到,有一个版本的重载就是调用的move函数(从上往下数第二个)。

C++11之前没有右值引用这一说法,但有的编译器会有返回值优化,遇到这种情况会进行优化,所以如果不开启11的编译选项,在某些编译器上也会自动进行优化,直接导致了在部分编译器上不会触发转移构造函数的奇怪行为。如果不需要返回值优化RVO,在GCC上可以加上-fno-elide-constructors来禁止返回值优化的行为。

当然,这里的重点不是右值引用的问题,如果需要进一步了解,有一篇文章写得很好,值得一看:C++ rvalue references and move semantics for beginners

这篇文章其实到这里已经可以结束了,但之前只用过string的operator+和operator+=,不知道string类还有append和push_back两个函数可以进行添加(这同时也坐实了string是容器这一事实,没必要再争论string算不算容器,机考能不能用这种问题了……)

一图胜千言,这张图就足以概括他们的主要区别了(来源是参考资料2):

事实上,从standard中也可以看到,operator+=其实就是调用的appendpush_back;也可以从这里看到运算符重载的好处。

参考资料

  1. 为什么超出内存限制?
  2. std::string::append vs std::string::push_back() vs Operator += in C++
  3. string::op+= / online c++ standard draft
  4. C++ rvalue references and move semantics for beginners
发布了110 篇原创文章 · 获赞 132 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/HermitSun/article/details/104089053