80.如何让C++字符串更快
字符串实际上是字符数组,所以对于string的主要问题,可能就是字符串格式化以及字符串的操作,这是因为它们都要分配内存
🍅概述问题
之前说过内存分配在堆和栈上的区别和建议:能分配在栈上就别分配到堆上,因为把内存分配到堆上会降低程序的速度。
然而,std::string 和它的很多函数都喜欢分配在堆上,这实际上并不理想
#include <string>
#include <iostream>
void PrintName(const std::string& name) {
std::cout << name << "\n";
}
int main() {
std::string name = "Cherno";
PrintName(name);
return 0;
}
而就像上面的这么简单的程序,系统便为我们分配了八个字节的内存,堆分配了一次
而我们堆分配发生的地方,就是在创建字符串的地方std::string name = "CHerno" ;
而即使是最简单的
PrintName("Cherno");
在调用这个函数时,其构造函数依旧会分配其内存
然而这个例子还不够猛,还要再来点东西
#include <string>
#include <iostream>
void PrintName(const std::string& name) {
std::cout << name << "\n";
}
int main() {
std::string name = "Cherno";
std::string firstName = name.substr(0, 3);
std::string lastName = name.substr(3, 3);
PrintName(name);
return 0;
}
而在上面的例子中,程序分配了三次8字节的内存,每一次都分配到堆上
而做了这么点事情就有三次了,便可以想象这种事可能会在你的程序里经常发生了,相当损害速度
而如何解决呢?
🍅解决方案
在使用substr 时,这个函数会自己处理完原字符串后创建出一个全新的字符串,它可以变换并有自己的内存
但是我们真正要想的是那个字符串的视图(意思是我只想看看原字符串剪完以后是什么样子的,不想再利用剪完后的全新的串)
💡💡💡而这就是string_view 发挥作用的地方了
std::string_view 是C++17中的一个新类(但是还是可以用别的原始的方法去实现这个做法)
它的本质上,只是一个指向现有内存的指针,换句话说,就是一个const char 指针,指向其他人拥有的现有字符串,再加上一个大小size
- 我可以有一个指向第一个字符的指针,然后大小是3,而这就是我的子字符串。
- 我可以有一个指针,指向那个字符串的开头加上四个字节,把我带到子字符串的开头
换句话说,我在创建一个窗口,一个进入现有内存的小视图,而不是分配一个新的字符串,不是用substr() 创建一个新的字符串
做这种事其实很简单,所以早在C++17之前,人们就在做这样的事情,这实际上是很常见的。观察已有字符串是没有内存分配的,按值传递字符串视图是非常轻量级的。
所以重写上面的程序
#include <string>
#include <iostream>
void PrintName(std::string_view name) {
std::cout << name << "\n";
}
int main() {
std::string name = "Cherno";
std::string_view firstName(name.c_str(), 0);
std::string_view lastName(name.c_str() + 3, 3);
PrintName(name);
return 0;
}
利用string_view 去观察子字符串,去分配函数形参之后,测试出来的内存分配结果是:我们只是从这个原始字符串中得到仅仅一次堆上的分配
这很好了,但还可以更好,可以完全不需要分配内存!
做到这一点,则需要完全不使用std::string
#include <string>
#include <iostream>
void PrintName(std::string_view name) {
std::cout << name << "\n";
}
int main() {
const char* name = "Cherno";
std::string_view firstName(name, 0);
std::string_view lastName(name + 3, 3);
PrintName(name);
return 0;
}
这般,完成在堆上完全0分配内存
💡💡💡这里的"Cherno" 其实还是字符串,但是被分配到了栈上
亦可笼统称为字符串视图
所以如果不是有特殊的需求,尽量开始用string_view 去替换掉这些字符串
|