C++核心技术:视图

20 分钟阅读 935 字 + 3019 词
在不关心所有权时,我们可以考虑视图
string_view (C++ 17)
string_view只保存一个const char *和一个size_t
不要这样使用string_view
string_view name = string("C++");//string是一个临时对象
原先如果传参const string &确实对string很友好,但是对字符串字面量就不友好了,如果传参一个字符串字面量,涉及到临时string对象的构造,将字符串字面量的内容全部复制进去,然后函数返回还得销毁这个临时对象
string_view不是string,但是它的成员函数和string非常相似
同样有data,size,begin,end,find等方法。
不同的点在于:
  1. 不能修改字符串的内容,data返回的是const char * 。
  2. 没有c_str()函数,并且它的data函数完全不保证返回的字符串是零结尾的。
  3. substr成员函数返回的是一个新的string_view,而非string。
  4. 额外有成员函数remove_prefix和remove_suffix
如果用一个string_view对象sv去调用原型为f(const string &)的函数,我们需要写f(string(sv));
以下是 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;
}

四、注意事项
  1. 生命周期管理 string_view 不管理内存,需确保底层数据在其生命周期内有效。
  2. 空终止符 data() 返回的指针可能不以 \0 结尾,直接传递给 C 函数需谨慎。
  3. 性能优化 :适合作为函数参数替代 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的成员函数:
begin,end,front,back,size,empty,data,operator[]
也有一些特有的成员函数
  1. size_bytes:用于字节数计算的序列大小
  2. first:开头若干项组成的新的span,不修改自身
  3. last:结尾若干项组成的新spanm
  4. subspan:类似substr
且span的长度可以在编译器确定
span是语法糖,程序员需要保证底层数据合法存在,不要释放后使用
C++20里的视图
到了C++20,真正有了std::ranges::view这个概念
需要包含< ranges>头文件
//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);