C++17之std::string_review字符串视图

30 篇文章 17 订阅

背景

在进行参数传递时,当数据占用的内存比较大是,减少数据的拷贝可以有效提升程序的性能。在C语言中使用的是指针,在C++中使用了安全性更高的引用类型。所以C++中const std::string&成为了传递只读数据的不二方式。但是从实践来看,也存在一些问题:

  1. 字符串字面值、字符数组、字符串指针的传递仍要数据拷贝
    这三类低级数据类型与string类型不同,传入时,编译器需要做隐式转换,即需要拷贝这些数据生成临时string对象。const string&指向的实际上是这个临时对象。通常字符串字面值较小,性能损耗可以忽略不计;但字符串指针和字符数组某些情况下可能会比较大(比如读取文件的内容),此时会引起频繁的内存分配和数据拷贝,会严重影响程序的性能。
  2. substr O(n)复杂度
    这是一个特别常用的函数,好在std::string提供了这个函数,美中不足的是其每次都返回一个新生成的子串,很容易引起性能热点。实际上我们本意并不是要改变原字符串,为什么不在原字符串基础上返回呢?

在c++ 17中,c++标准库采用了一个特殊的字符串类std::string_view,它允许我们处理字符串之类的字符序列,该类型不会为数据分配存储空间,而且该数据类型只能用来读。该数据类型可通过{数据的起始指针,数据的长度}两个元素表示,实际上该数据类型的实例不会具体存储原数据,仅仅存储指向的数据的起始指针和长度,所以这个开销是非常小的。

使用这样的string视图既便宜又快捷(通过值传递string_view总是便宜的)。但是,它也有潜在的危险,因为与原始指针类似,在使用string_view时,由程序员来确保引用的字符序列仍然有效)。 

string类重载了stringstring_view的转换操作符:
operator std::basic_string_view<CharT, Traits>() const noexcept;

所以,string_view foo(string("abc"))实际执行了两步操作:

  1. string("abc")转换为string_view对象a
  2. string_view使用对象本篇文章从string_view引入的背景.

std::string_view使用陷阱

1. string_view范围内的字符可能不包含\0

#include <iostream>
#include <string_view>

int main() {
    std::string_view str{"abc", 1};

    std::cout << str.data() << std::endl;

    return 0;
}

输出结果为abc。这是因为字符串相关的函数都有一条兼容C的约定:\0代表字符串的结尾。上面的程序打印从开始到字符串结束的所有字符,虽然str包含的有效字符是a,但cout\0。好在这块内存空间有合法的字符串结尾符,如果str指向的是一个没有\0的字符数组,程序很有可能会出现内存问题,所以我们在将string_view类型的数据传入接收字符串的函数时要非常小心。

2.从[const] char*构造string_view对象时间复杂度O(n)

这是因为获取字符串的长度需要从头开始遍历。如果对[const] char*类型仅仅是一些O(1)的操作,相比直接使用[const] char*,转为string_view是没有性能优势的。只不过是相比const string&string_view少了拷贝的损耗。实际上我们完全可以用[const] char*接收所有的字符串,但这个类型太底层了,不便使用。在某些情况下,我们转为string_view可能仅仅是想用其中的一些函数,比如substr

3.string_view指向的内容的生命周期可能比其本身短
string_view并不拥有其指向内容的所有权,用Rust的术语来说,它仅仅是暂时borrow(借用)了它。如果拥有者提前释放了,你还在使用这些内容,那会出现内存问题,这跟悬挂指针(dangling pointer)或悬挂引用(dangling references)很像。Rust专门有套机制在编译时分析变量的生命期,保证borrow的资源在使用期间不会被释放,但C++没有这样的检查,需要人工保证。下面列出一些典型的问题情况:

//情况一
std::string_view sv = std::string{"hello world"};
//情况二
string_view foo() {
    std::string s{"hello world"};
    return string_view{s};
}
//情况三
auto id(std::string_view sv) { return sv; }

int main() {
    std::string s = "hello";
    auto sv = id(s + " world"); 
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值