C++核心技术:视图
20 分钟阅读
•
935 字
+
3019 词
在不关心所有权时,我们可以考虑视图
string_view只保存一个const char *和一个size_t
begin,end,front,back,size,empty,data,operator[]
也有一些特有的成员函数
到了C++20,真正有了std::ranges::view这个概念
需要包含< ranges>头文件
string_view (C++ 17)
不要这样使用string_view
string_view name = string("C++");//string是一个临时对象
原先如果传参const string &确实对string很友好,但是对字符串字面量就不友好了,如果传参一个字符串字面量,涉及到临时string对象的构造,将字符串字面量的内容全部复制进去,然后函数返回还得销毁这个临时对象
string_view不是string,但是它的成员函数和string非常相似
同样有data,size,begin,end,find等方法。
不同的点在于:
- 不能修改字符串的内容,data返回的是const char * 。
- 没有c_str()函数,并且它的data函数完全不保证返回的字符串是零结尾的。
- substr成员函数返回的是一个新的string_view,而非string。
- 额外有成员函数remove_prefix和remove_suffix
以下是
std::string_view
的初始化方法和常用成员函数的详细说明,适用于 C++17 及更高版本:
一、初始化方法
std::string_view
提供多种构造函数,用于创建对字符串的非拥有视图:
1.
默认构造
cpp复制std::string_view sv; // 空视图,data() = nullptr,size() = 0
2.
从 C 风格字符串构造
cpp复制const char* cstr = "Hello";
std::string_view sv1(cstr); // 长度自动计算(直到 '\0')
std::string_view sv2(cstr, 3); // 指定长度("Hel")
3.
从
std::string
构造
cpp复制std::string str = "Hello";
std::string_view sv(str); // 视图指向 str 的数据
std::string_view sv_part(str, 1, 3); // 从索引 1 开始,长度 3("ell")
4.
从字符数组构造
cpp复制char arr[] = {'H', 'e', 'l', 'l', 'o'};
std::string_view sv(arr, 5); // 视图指向 arr,需手动指定长度
5.
从另一个
string_view
构造
cpp复制std::string_view sv_original = "Hello";
std::string_view sv_copy(sv_original); // 复制视图
std::string_view sv_sub(sv_original, 1, 3); // 从索引 1 开始,长度 3("ell")
6.
从迭代器范围构造
cpp复制std::string str = "Hello";
std::string_view sv(str.begin(), str.end()); // 视图指向整个字符串
二、常用成员函数
std::string_view
提供与
std::string
类似的接口,但不可修改底层数据。
1.
访问数据
-
data(): 返回指向底层数据的指针(可能非空终止)。 -
size()/length(): 返回视图长度。 -
max_size(): 返回可支持的最大长度。 -
empty(): 检查视图是否为空。
2.
元素访问
-
operator[](size_t pos): 返回索引处的字符(不检查边界)。 -
at(size_t pos): 返回索引处的字符(越界时抛出std::out_of_range)。 -
front(): 返回首字符。 -
back(): 返回尾字符。
3.
修改视图范围
-
remove_prefix(size_t n): 从头部移除n个字符。 -
remove_suffix(size_t n): 从尾部移除n个字符。
4.
子视图操作
- : 返回子视图。
substr(size_t pos = 0, size_t count = npos)cpp复制std::string_view sv = "Hello"; std::string_view sub = sv.substr(1, 3); // "ell"
5.
比较操作
-
compare(string_view other): 返回比较结果(类似strcmp)。 -
支持运算符:
==,!=,<,>,<=,>=。
6.
查找操作
-
find(char ch, size_t pos = 0): 查找字符。 -
rfind(char ch, size_t pos = npos): 反向查找。 -
find_first_of(string_view chars, size_t pos = 0): 查找任意字符首次出现。 -
find_last_of(string_view chars, size_t pos = npos): 查找任意字符最后一次出现。 -
find_first_not_of,find_last_not_of: 类似逻辑。
7.
迭代器
-
begin()/end(): 返回迭代器。 -
cbegin()/cend(): 返回常量迭代器。 -
rbegin()/rend(): 返回反向迭代器。
三、示例代码
cpp复制#include
#include
int main() {
// 初始化
std::string_view sv1 = "Hello"; // 自动推导长度
std::string_view sv2("World", 3); // "Wor"
std::string str = "Hello World";
std::string_view sv3(str.data() + 6, 5); // "World"
// 成员函数
std::cout << sv1.substr(1, 3) << std::endl; // "ell"
sv1.remove_prefix(1); // 视图变为 "ello"
std::cout << sv1.find('l') << std::endl; // 1(第一个 'l' 的索引)
return 0;
}
四、注意事项
-
生命周期管理
:
string_view不管理内存,需确保底层数据在其生命周期内有效。 -
空终止符
:
data()返回的指针可能不以\0结尾,直接传递给 C 函数需谨慎。 -
性能优化
:适合作为函数参数替代
const std::string&,避免不必要的拷贝。
span(C++ 20)
//假设有一个通用的打印整数序列的函数
void print(span<int> sp){
for(int n:sp){
cout<<n<<' ';
}
cout<<'\n';
}
//以下容器都是可以传参给print的
array a{1,2,3,4,5};
int b[]{1,2,3,4,5};
vector v{1,2,3,4,5};
//而不提供连续存储的容器不能这么用,比如list
span< char>允许更改底层数据,和string_view不一样
如果不需要修改内容其实应该使用span< const int>
span的成员函数:
- size_bytes:用于字节数计算的序列大小
- first:开头若干项组成的新的span,不修改自身
- last:结尾若干项组成的新spanm
- subspan:类似substr
C++20里的视图
//std::views::transform是一个映射视图
std::cout<<(a|std::views::transform([] (int n){return n*n;}));
//过滤操作
std::cout<<(a|std::views::filter([] (int n){return n%2==0;}));
//反转操作
std::cout<<(a|std::views::reverse);
//对于包含多个子元素的容器
std::map<int,string> mp{{1,"one"},{2,"two"},{3,"three"}};
std::cout<<(mp|std::views::values);