[c++实践]关于标准库中字符串的高效处理
无论什么程序,都需要大量的使用字符串,c++标准库提供了 std::string
对字符串进行处理。熟悉 std::string
实现逻辑的都知道,std::string
库比较耗时的操作主要在内存的分配与字符串的拼接。因为内存分配实际上还是使用的 malloc
,但是在多线程环境下调用该函数会有一个全局的锁,如果频繁的调用该函数会导致性能的严重下降。而字符串拼接的性能底下则归咎于 std::string
库实现的原理。比如如下代码:
std::string log_path = log_dir + "/" + log_name + ".log";
这短短的一段代码,缺构造了多个临时变量,这主要是由 std::string::operator+
操作会构造新对象引起的,以下为gcc9.4.0版本中的实现:
template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT, _Traits, _Alloc>
operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
const basic_string<_CharT, _Traits, _Alloc>& __rhs)
{
basic_string<_CharT, _Traits, _Alloc> __str(__lhs);
__str.append(__rhs);
return __str;
}
可以看到虽然它返回的是一个 basic_string 对象而非引用,因此每+一次都会够早一个临时变量,而构造一个std::string
会调用std::string::operator(const std::string &
并在内部重新分配内存,然后进行内存拷贝。
从上面可以知道,只要消除临时变量的创建以及减少内存的分配就可以提高std::string
拼接字符串的效率。我们可以先使用 std::string::reserve
一次分配足够的内存,然后使用 std::string::operator+=
和 std::string::append
代替上面的 std::string::operator+
,因为他们返回的是对象的引用,不会构造临时对象。
除了字符串的拼接,有时候还需要传递字符串,比如作为参数传递或者作为结果返回。我们可以使用引用作为参数传递以减少对象的拷贝,而当std::string
作为返回值时我们可以使用std::move将它转换为右值(实际上不需要我们显示的进行,因为编译器在优化的时候会做的比std::move更好,可以见关于右值引用的讨论)。
更进一步的,如果使用c++17,则可以使用新引入的 std::string_view
,这里不做介绍,因为我们的工程使用的是 c++11标准。