暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Tip of the Week #3:String Concatenation and operator+vs StrCat()

程序员的Cookbook 2020-10-30
314

Tip of the Week #3: String Concatenation and operator+ vs. StrCat()

Originally published as totw/3 on 2012-05-11
Updated 2017-09-18; revised 2018-01-22

当一个reviewer说"不要使用string的连接操作,这不高效",这个通常会让提交代码的人很惊讶。std::string::operator+
是如何低效的呢? 是不是很难弄错?

事实证明,这种低效并不明显,这两个片段在实际执行中有着接近相同的执行时间:

    std::string foo = LongString1();
    std::string bar = LongString2();
    std::string foobar = foo + bar;


    std::string foo = LongString1();
    std::string bar = LongString2();
    std::string foobar = absl::StrCat(foo, bar);
    复制

    但是对于下面两个片段却不是如此:

      std::string foo = LongString1();
      std::string bar = LongString2();
      std::string baz = LongString3();
      string foobar = foo + bar + baz;


      std::string foo = LongString1();
      std::string bar = LongString2();
      std::string baz = LongString3();
      std::string foobar = absl::StrCat(foo, bar, baz);
      复制

      当我们分解一下foo + bar + baz
      表达式中发生的情况,就可以理解上面两种情况有所不同的原因了。在C++中没有三个参数的操作符,所以必须要执行二次string::operator+
      操作才能完成三个字符串的相加。在两次调用之间会构造出一个临时的字符串因此std::string foobar = foo + bar + baz
      等同如下:

        std::string temp = foo + bar;
        std::string foobar = std::move(temp) + baz;
        复制

        具体来说就是foo和bar连接后的内容在放入foobar之前必须先复制到一个临时的位置(有关std::move
        ,看Tip of the Week #77: Temporaries, moves, and copies[1])。

        C++11
        允许第二次连接操作的时候不需要创建一个新的string
        对象的:std::move(temp) + baz
         等同于std::move(temp.append(baz))
        。然而有可能其内部buffer大小不够导致内存重新分配(会导致额外的拷贝),因此在最坏的情况下,n
        个字符串连接的时候需要O(n)
        次内存重分配。

        一个好的替代方法就是使用absl::StrCat()
        ,一个不错的帮助函数其实现在 absl/strings/str_cat.h[2]文件中,通过计算必要的字符串长度,预先分配大小,并将所有输入数据进行连接,其复杂度优化到O(n)
        ,同样对于以下情况:

          foobar += foo + bar + baz;
          复制

          使用abs::StrAppend
          可以带来同样的优化:

            absl::StrAppend(&foobar, foo, bar, baz);
            复制

            同样,absl::StrCat()
             and absl::StrAppend()
            对除了字符串类型意外的类型进行操作: 可以使用absl::StrCat
            /absl::StrAppend
            int32_t
            uint32_t
            int64_t
            uint64_t
            float
            double
            const char*
            , and string_view
            等类型进行转换,像如下这样:

              std::string foo = absl::StrCat("The year is ", year);
              复制

              References

              [1]
               Tip of the Week #77: Temporaries, moves, and copies: https://abseil.io/tips/77
              [2]
               absl/strings/str_cat.h: https://github.com/abseil/abseil-cpp/blob/master/absl/strings/str_cat.h


              文章转载自程序员的Cookbook,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论