C++17 string_view 详细案例

(鄙人总结,希望和大家交流,切莫转载,谢谢!)

C++17 使用std::string_view来观测一段字符数组,减少内存的拷贝,或者说不至于像string对象移动后,前一个对象放弃那块本属于它的内存。

// 原码:
// TYPEDEFS FOR basic_string_view
using string_view = basic_string_view<char>;

// CLASS TEMPLATE basic_string_view
template <class _Elem, class _Traits>
class basic_string_view {
public:
	using const_pointer          = const _Elem*;
    using size_type              = size_t;
	...
private:
	const_pointer _Mydata;
    size_type _Mysize;
}

可以看到string_view的成员变量只有2个,const_pointer _Mydata:指向字符串首地址的指针。 size_type _Mysize:字符串的长度(不含"\0")

比如下例:使用string s给string_view sv初始化

	// 例1:
	string s{ "Hello" };
	string_view sv{ s };

完成sv的初始化后,我们分别监视s和sv:
在这里插入图片描述
在这里插入图片描述
可以看到sv是对s的buf的观测,即s.data() == sv.data() == sv._Mydata,且sv._Mysize = s.size(),至此sv初始化完成,它刻画一个字符串,这个字符串的长度为5(不能说长度为s.size()),起始地址为s.data().

	// 例子2 给s拼接一个" World"
	string s{ "Hello" };
	string_view sv{ s };
	s += " World";

	cout << s << endl;
	cout << sv << endl;

结果显示:

Hello World
Hello

再次监测s和sv
在这里插入图片描述

在这里插入图片描述
发现s在capacity范围内(当前环境得出的capacity如上图s的监视图的[capacity]所示为5),增加了" World",而sv只是刻画访问那个内存的5个字节的字符串,所以sv的输出依旧是"Hello"。

那么,如果s增加的字符数大于原本的capacity,势必会迎来成长,发生内存的搬移,sv依旧访问原本的内存,此时就会得到意料之外的值。比如:

	// 例3 突破此环境(MSVC)下capacity为15的上限
	s += " World World World World World";
	
	cout << s << endl;
	cout << sv << endl;

结果显示:

Hello World World World World World
萯?o

stringstring_view都有返回子串的方法:substr(pos, cnt),看看它干了什么事:

    _NODISCARD constexpr basic_string_view substr(const size_type _Off = 0, size_type _Count = npos) const {
        // return a new basic_string_view moved forward by _Off and trimmed to _Count elements
        _Check_offset(_Off);
        _Count = _Clamp_suffix_size(_Off, _Count);
        return basic_string_view(_Mydata + _Off, _Count);
    }

它返回一个临时对象的拷贝,这个临时对象的_Mydata = 原对象的_Mydata + _Off_Mysize 为第二入参_Count,因此可以知道返回值依旧是指向同一块内存的不同位置的指针,其长度为_Count,因此子串仍旧会出现上述因为string成长所带来的问题。

stringstring_view都提供.data()的方法,返回一个指向字符串的指针。当使用::printf("%s", ptr)时,或者cout << ptr << endl来打印字符串时,稍微注意string_view的输出

	// 例4 sv_sub的内部指针指向sv的第3个字符的地址
	string s{ "Hello" };
	string_view sv{ s };
	string_view sv_sub = sv.substr(2, 2);

	cout << sv_sub << endl;
	::printf("%s", sv_sub.data());

结果显示:

ll
llo

这里printf的输出并不能达到只打印子串所观测的2个字符,它会一直打到字符串结束为止。稍微注意一下即可。

又由于string_view避免了字符串对象在非移动赋值时的内存申请,和N个字符的拷贝,因此从效率上来说是相当高的,这里做一个对比。

// 例5 添加ctime头文件用于获取tick
#include <ctime>

	...
	
	const long _LOOP_MILLION = 1000000; // 循环一百万次
	/* 在ctime中有这样一个定义,tick的差值除以1000就等于时间差(毫秒级)
	// The number of clock ticks per second
	#define CLOCKS_PER_SEC  ((clock_t)1000)
	*/
	clock_t startTime, endTime;
	startTime = clock();
	for (long i{}; i < _LOOP_MILLION; ++i)
	{
		string sub(std::move(s.substr()));  // string子串涉及内存拷贝,然后移动给string sub
	}
	endTime = clock();
	cout << "The run time is: " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;

	startTime = clock();
	for (long i{}; i < _LOOP_MILLION; ++i)
	{
		string_view sub(move(sv.substr()));  // string_view子串不涉及内存拷贝,构造string_view sub时传入地址和长度
	}
	endTime = clock();
	cout << "The run time is: " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;

结果显示:

The run time is: 3.975s
The run time is: 0.174s

所以在设计优秀的算法之外,优秀的编程方式也很重要。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
string_viewstring都是C++ STL中的字符串类型,但它们有着不同的特点和用途。 stringC++中常用的字符串类型,它是一个可变长的字符串容器,可以动态增加或删除字符。它存储的字符串是一个连续的字符数组,可以通过下标或迭代器进行访问和修改。string支持很多字符串操作,如查找、替换、插入、删除、子串等。 string_viewC++17新增的类型,它是一个不可变的字符串视图。它本质上是一个只包含指向原始字符串的指针和长度信息的结构体,它不拥有原始字符串的内存空间,也不会对原始字符串进行修改。它主要用于读取和处理字符串,可以提高程序的效率和安全性。string_view可以用于任何可以转换为const char*的类型,如string、字符数组、字面量等。 下面是string_viewstring的区别和联系: 1. 内存管理方式不同:string拥有自己的内存空间,而string_view不拥有内存空间,只是指向原始字符串的一个视图。 2. 可变性不同:string是可变的,可以修改字符串内容;而string_view是不可变的,只能读取字符串内容。 3. 使用场景不同:string_view主要用于只读操作,可以提高程序效率和安全性,特别是在处理大量字符串时。而string则适用于需要频繁修改字符串的场景。 4. 接口相似:string_viewstring都支持类似的操作,如查找、比较、子串等。 总之,string_viewstring都是C++中常用的字符串类型,它们各有优点和适用场景。在实际编程中,可以根据需要选择合适的字符串类型。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

歪锅锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值