C++是由C、object、STL以及泛型等四大块组成的C++语言联盟。下面我们就简单的了解以下STL中的string类。

我们使用的所有库函数都是编译器帮我们实现的,编译器根据C/C++或者其它语言的标准实现相应的库函数。所以有可能导致内个编译器实现的路径不同(版本不同)。

在C语言中提供的都是函数,而在C++中提供的将不再是函数了,而是变成类了。

需要特别注意的是

#include  // C语言的字符串操作函数头文件#include  // C语言中符合C++标准的字符串操作函数#include  // C++的STL可编程字符串头文件 // string和string.h这两个头文件是完全不同的using std::string; // 我们可以指定只导入string类,这样避免了命名空间的污染。

我们先看一下什么是模板函数(泛型)

模板是一个自推倒类型,将会根据给出来的类型自动推倒。

比如我们的函数是支持重载的,我们的函数可以放入不同的类型,只要实现了一些相关类型的参数,它就能根据我们编写的这些函数自动匹配,但是这些重载函数需要写很多个才能满足我们的要求,如果我们使用模板,那么就可以用一个模板函数来实现。这个时候就是我们所说的模板编程或者泛型编程。

下面我们来看一下模板编程的一个交换例子

#include #include using std::string;template  // 此时的 T 就相当于一个占位符void Swap(T &lhs, T &rhs){ T temp; temp = lhs; lhs = rhs; rhs = temp;}int main(){ char c1 = 'A', c2 = 'B'; std::cout << "交换前:" << c1 << " " << c2 << std::endl; Swap(c1, c2); std::cout << "交换后:" << c1 << " " << c2 << std::endl << std::endl; int i1 = 1, i2 = 2; std::cout << "交换前:" << i1 << " " << i2 << std::endl; Swap(i1, i2); std::cout << "交换后:" << i1 << " " << i2 << std::endl << std::endl; double d1 = 1.1, d2 = 2.2; std::cout << "交换前:" << d1 << " " << d2 << std::endl; Swap(d1, d2); std::cout << "交换后:" << d1 << " " << d2 << std::endl << std::endl; return 0;}

运行结果如下

我们只写一个模板函数就解决了以前要写好多个重载函数的问题,这是不是很高大上?这里看起来我们调用的是同一个函数,但是事实上是不是呢?我们一起来看一下反汇编吧,来探个究竟。

从反汇编上我们可以看出,看起来我们调用的同一个函数,但是事实上调用的函数地址是不一样的,这几个函数地址是编译器自动帮我们生成的,在功能上等同于帮我们自动生成了重载函数。

所谓的泛型,我们可以的鼻祖就是void*,它是一个内存级的交换,但是C++中的一些特性,使得我们不仅仅可以做一些内存级的交换,因为C++相对于C语言多了运算符重载,那么我们就可以通过运算符对我们的语句进行再次的抽象,所以我们能达到和void*效果一样的功能。

我们再举一个例子,来说明一些问题,请看下面代码:

#include #include using std::string;template  // 此时的 T 就相当于一个占位符void Swap(T &lhs, T &rhs){ T temp = lhs; lhs += rhs; rhs += temp;}int main(){ char c1 = 'A', c2 = 'B'; std::cout << "交换前:" << c1 << " " << c2 << std::endl; Swap(c1, c2); std::cout << "交换后:" << c1 << " " << c2 << std::endl << std::endl; int i1 = 1, i2 = 2; std::cout << "交换前:" << i1 << " " << i2 << std::endl; Swap(i1, i2); std::cout << "交换后:" << i1 << " " << i2 << std::endl << std::endl; double d1 = 1.1, d2 = 2.2; std::cout << "交换前:" << d1 << " " << d2 << std::endl; Swap(d1, d2); std::cout << "交换后:" << d1 << " " << d2 << std::endl << std::endl; return 0;}

此时编译还能通过,说明我们传递进去的不管是char、int还是double类型,它们都有+=运算符的重载。如果我们自己定义一个新的类型,但是我们不重载+=运算符,那么此时还能通过编译码?请看下面的代码:

#include #include using std::string;class Demo{};template  // 此时的 T 就相当于一个占位符void Swap(T &lhs, T &rhs){ T temp = lhs; lhs += rhs; rhs += temp;}int main(){ char c1 = 'A', c2 = 'B'; std::cout << "交换前:" << c1 << " " << c2 << std::endl; Swap(c1, c2); std::cout << "交换后:" << c1 << " " << c2 << std::endl << std::endl; int i1 = 1, i2 = 2; std::cout << "交换前:" << i1 << " " << i2 << std::endl; Swap(i1, i2); std::cout << "交换后:" << i1 << " " << i2 << std::endl << std::endl; double d1 = 1.1, d2 = 2.2; std::cout << "交换前:" << d1 << " " << d2 << std::endl; Swap(d1, d2); std::cout << "交换后:" << d1 << " " << d2 << std::endl << std::endl; Demo demo1, demo2; Swap(demo1, demo2); return 0;}

此时我们再编译的时候,就会出现下面的错误

这是因为我们的自定义类中没有重载+=运算符,解决的办法很简单,我们自己实现一个+=运算符就ok了。

#include #include using std::string;class Demo{public: Demo &operator+=(const Demo &other) { return *this; }};template  // 此时的 T 就相当于一个占位符void Swap(T &lhs, T &rhs){ T temp = lhs; lhs += rhs; rhs += temp;}int main(){ char c1 = 'A', c2 = 'B'; std::cout << "交换前:" << c1 << " " << c2 << std::endl; Swap(c1, c2); std::cout << "交换后:" << c1 << " " << c2 << std::endl << std::endl; int i1 = 1, i2 = 2; std::cout << "交换前:" << i1 << " " << i2 << std::endl; Swap(i1, i2); std::cout << "交换后:" << i1 << " " << i2 << std::endl << std::endl; double d1 = 1.1, d2 = 2.2; std::cout << "交换前:" << d1 << " " << d2 << std::endl; Swap(d1, d2); std::cout << "交换后:" << d1 << " " << d2 << std::endl << std::endl; Demo demo1, demo2; Swap(demo1, demo2); return 0;}

此时编译就能够顺利通过。

stl中的可变长字符串string类

它有以下的特性:

1. 可变长

它的基类有两个

basic_string

basic_string

其实,无论是char还是wchar_t都是由这两个基类来实现的。

那么string和wstring有什么区别呢?它们的差别在于编码集的不同,一个是窄字节(1byte),一个是宽字节(2byte),这两者只是字节上的不同,而并非编码上的不同。

typedef basic_string, allocator >

string;

typedef basic_string, allocator >

wstring;

截取固定长度,从头开始

string str(“123456”, 3);

取前n个字符进行构造的功能

如果我们截取的个数大于我们传入进去的字符串的长度时会怎样呢?会出错吗?我们来做一个实验看看就知道了,我给他传一个长度10进去,看看效果

这不是很好嘛!一点儿问题都没有,我们再给它的长度再大一点儿,换个100试试

这是什么?怎么会面跟了一行字符串,我们看一下监视窗口的str的值

从监视窗口来看,str确实是”123456”这个这个值啊,那么为什么我们用cout输出的时候,就会出现后面多余的字符串呢?我们再看一下其它的输出信息

从这个信息上可以看出,我们指定大小之后,编译器会给我们分配那么大的空间,但是超出我们给定的字符串长度的内容是怎么来的,我们看不出来,也先不去关心这个,最重要的是当我们使用str.c_str()这个方法时,就能正确的打印出”123456”,所以我们可以看出,string类实现的输入输出流跟我们想象的不一样。具体在哪儿不一样,我们以后再讨论。

但是问题又来了,跟我们上面出现的一样,当我们传入的数值大于我们传入的对象的长度会怎样呢?

从这个结果上来看,并没有给我们分配我们传入的大小,这一点我们就先记住吧,现在也解释不了太清楚,以后再解释吧!

string类的构造函数的一些总结,代码方式的总结:

#include #include using std::string;int main(){ string str("123456789", 10); // 截取固定长度 从头开始 //string str(10, 'A'); // 拿着后面相同的字符进行填充 10 个 //string int string str1(str, 3); // 从str下标为 3 的位置开始截取,直到str的末尾 //const char * int string str4("123455", 3); // 取前三个 string str2(str, 3, 30000); // 从str下标为 3 的位置开始截取3个字符,不一定到末尾 string str3("123455", 3, 3);// 先调用转换构造,生成一个临时对象,然后再调用上面的函数 // 上面的一行代码等价于下面的两行代码 // string temp("123455"); // string(temp, 3, 3); std::cout << str << std::endl; std::cout << str1 << std::endl; std::cout << str2 << std::endl; std::cout << "str2.size():" << str2.size() << std::endl; std::cout << "str2.length():" << str2.length() << std::endl; std::cout << "str2.capacity():" << str2.capacity() << std::endl; std::cout << str3 << std::endl; return 0;}

字符串的长度

#include #include using std::string;int main(){ string str("123456789", 10); // 截取固定长度 从头开始 // 需要注意的是,这里面传递的是一个迭代器 string str1(str.begin() + 3, str.begin() + 5); // 如果使用C++的风格来截取字符串的话,推荐使用这种方式 std::cout << str1 << std::endl; string str2(str, 3, str.length() - 8); // 如果使用C语言的风格来截取字符串的话,推荐使用这种方式 std::cout << str2 << std::endl; return 0;}

stl中的通用查找函数std::find函数的返回值是一个迭代器,而类中的专用的find函数的返回值是一个pos位置信息,所以在使用这个返回值的时候,需要判断一下是不是npos(无效的位置),

#include #include using std::string;int main(){ string str; std::cout << str.max_size() << std::endl << std::endl; // 最最大值 std::cout << str.capacity() << std::endl << std::endl; // 在不改变空间大小的情况下能存储的大小,是一个动态改变的 std::cout << str.size() << std::endl; std::cout << str.length() << std::endl; std::cout << str.empty() << std::endl << std::endl; // true 为空 false为不空 str = "1"; std::cout << str.size() << std::endl; std::cout << str.length() << std::endl; std::cout << str.empty() << std::endl << std::endl; str = "12345678901234567890";; std::cout << str.size() << std::endl; std::cout << str.length() << std::endl; std::cout << str.capacity() << std::endl; std::cout << str.empty() << std::endl << std::endl; str.resize(100); // 重新分配大小 std::cout << str.size() << std::endl; std::cout << str.length() << std::endl; std::cout << str.capacity() << std::endl; std::cout << str.empty() << std::endl << std::endl; str.resize(100, 'A'); std::cout << str.size() << std::endl; std::cout << str.length() << std::endl; std::cout << str.capacity() << std::endl; std::cout << str.empty() << std::endl << std::endl; str = ""; std::cout << str.size() << std::endl; std::cout << str.length() << std::endl; std::cout << str.empty() << std::endl << std::endl; return 0;}

字符串的查找

查找字符

#include #include using std::string;int main(){ string str("12345678"); std::size_t pos = str.find('5'); // 返回要查找的字符的索引值 std::cout << pos << std::endl; pos = str.find('9'); std::cout << pos << std::endl; if (pos == std::string::npos) { std::cout << "未找到" << std::endl; } else { std::cout << pos << std::endl; } return 0;}

查找字符串

需要补充的是,在查找字符的时候,我们也可以指定开始查找的位置

上面的都是find函数例子,string类中还有一个rfind的函数,它与find的功能是一模一样的,所不同的是find从前面查找,而rfind是从后面查找。以及find_first_of和find_last_of函数的用法都是一样的。

下面是关于find的总结:

#include #include using std::string;int main(){ string str("12345678"); std::size_t pos = str.find("45"); // 返回要查找的字符串的索引值 if (pos == std::string::npos) { std::cout << "未找到" << std::endl; } else { std::cout << pos << std::endl; } std::cout << std::endl; //pos = str.find("78"); //pos = str.find("78", 4); // 我们可以指定开始查找的位置 对于查找字符串和查找字符都是试用适用的 pos = str.find('7', 4); pos = str.find("123488888", 0, 4); // 此时相当于在str中查找 "1234"这四个字符组成的字符串 if (pos == std::string::npos) { std::cout << "未找到" << std::endl; } else { std::cout << pos << std::endl; } return 0;}

下面两个函数比较有意思,我们重点体验一下

分别是find_first_not_of以及find_last_not_of两个函数

代码如下

#include #include using std::string;int main(){ string str(" dhdhFIOWEHFIOjfoijewdiogjweOPJG POGJIOJS 8123456789 FBFKDJKVNDKVNKAHNCKOSAJNCKOVKDOIVJSDFIOKJ"); std::size_t pos_begin = str.find_first_of("1234567890"); // 返回的是第10个元素,说明找的是 // 当前字符串中任意一个字符不在str // 中的位置的结果 std::size_t pos_end = str.find_first_not_of("1234567890", pos_begin); string num(str.begin() + pos_begin, str.begin() + pos_end); std::cout << num << std::endl; // 我们这样做的目的是很方便的就找到了中间的数字部分 return 0;}

下面是字符串类中的比较函数的使用

at函数和下标[]运算符的区别

#include #include using std::string;int main(){ string str(" dhdhFIOWEHFIOjfoijewdiogjweOPJG POGJIOJS 8123456789 FBFKDJKVNDKVNKAHNCKOSAJNCKOVKDOIVJSDFIOKJ"); std::size_t pos_begin = str.find_first_of("1234567890"); // 返回的是第10个元素,说明找的是 // 当前字符串中任意一个字符不在str // 中的位置的结果 std::size_t pos_end = str.find_first_not_of("1234567890", pos_begin); //string num(str.begin() + pos_begin, str.begin() + pos_end); // 我们还可以使用substr函数来提取字符串,它和上一行的代码实现的功能是一样的 string num = str.substr(pos_begin, pos_end - pos_begin);// std::cout << num << std::endl; num = str; //std::cout << num.at(4) << std::endl; //std::cout << num[4] << std::endl; // 当下标没有越界的情况下,这两者的功能是一样的,但是如果越界的话就会变得不同了 std::cout << num[150] << std::endl; // 而这种情况会直接将程序退出,没有异常检测 std::cout << num.at(150) << std::endl; // 此时在这里会抛出一个异常 // 不知道怎么回事,我的电脑始终没有出现程序崩溃的现象,先把这个代码保存下来吧,以后再试试 return 0;}

字符串类中的c_str()和data()函数

#include #include using std::string;void Foo(char *str){}int main(){ string str("POGJIOJS 8123456789 "); Foo(const_cast(str.c_str())); return 0;}

最后,如果你想学C/C++可以私信小编“01”获取素材资料以及开发工具和听课权限哦!

用法 stl_PoEdu培训第四课-C++之STL相关推荐

  1. 第十四课 k8s源码学习和二次开发原理篇-调度器原理

    第十四课 k8s源码学习和二次开发原理篇-调度器原理 tags: k8s 源码学习 categories: 源码学习 二次开发 文章目录 第十四课 k8s源码学习和二次开发原理篇-调度器原理 第一节 ...

  2. 2019微生物组——16S扩增子分析专题培训第四期

    文章目录 课程简介 课程大纲 一.生信基础知识和技巧 二.图表解读和绘制 三.扩增子基础和分析流程 四.可重复计算和统计绘图 五.功能预测和机器学习 六.网络和环境因子分析 往期精彩回顾 主讲教师 助 ...

  3. 商务汇报PPT制作的七堂课-第四课:文字美化

    我们课程已经进入到第四课了,那我想针对前边的三天课程和大家做一个小小的总结,我们第一天讲了配色,第二天讲了模板制作,第三天讲了结构搭建,其实你发现,这就是我们做一个PPT的步骤,你打开电脑之后,根据你 ...

  4. 第四课 程小奔之避障

    广西●河池学院 广西高校重点实验室培训基地 系统控制与信息处理重点实验室 本篇博客来自河池学院: 409教育机器人组 写作时间: 2020年8月8日 程小奔第四课 回顾上节课内容 课程简介 开始创作之 ...

  5. 第四课 建站准备—配置文件修改

    第四课 建站准备-配置文件修改 时间:2011-06-18 23:51来源:未知 作者:admin点击: 103 次 这一节课,我们来给大家说一下织梦CMS配置文件的修改.为什么要修改呢?我们先来看一 ...

  6. 自学python第四课——字符串运算符、字符串内置函数、字符串常用方法总结

    第四课 字符串 单.双.三引号 直接赋值和用 input 赋值: "+"号和"*"号在字符串中的使用 in 和 not in 字符串的格式化 r 保留原格式,即 ...

  7. 【Python的自学之路】(六):案例分析第四课-小工具2.0

    目录 序言 背景 思路 代码及解析 跋文 序言 小工具1.0版本,备份oracle存储过程的小工具上篇文章已经都了解完毕了,下面继续进行功能升级,2.0版本-表数据的导出功能. 背景 案例分析第四课- ...

  8. Linux 探索之旅 | 第三部分第四课:后台运行及合并多个终端

    -- 作者 谢恩铭 转载请注明出处 内容简介 第三部分第四课:后台运行及合并多个终端 第三部分第五课预告:延时执行,唯慢不破 后台运行及合并多个终端 上一课 Linux探索之旅 | 第三部分第三课:监 ...

  9. 孙鑫mfc学习笔记第十四课

    第十四课 网络的相关知识,网络程序的编写,Socket是连接应用程序与网络驱动程序的桥梁,Socket在应用程序中创建,通过bind与驱动程序建立关系.此后,应用程序送给Socket的数据,由Sock ...

最新文章

  1. 在家办公如何提高效率?
  2. 自己的电脑上怎么用python-如何做一个任何电脑都能用的python程序?
  3. 一个关于nvarchar字段排序,中英文混合排序的问题
  4. 前端几个笔试题及答案(bd)
  5. golang 修改nginx配置文件_「系统架构」Nginx调优,不可错过的几点
  6. 观察者模式--java jdk中提供的支持
  7. python return用法_遗传算法(Python) #4 DEAP框架入门
  8. linux下合并两个文件夹
  9. springboot+aop+自定义注解,打造通用的全局异常处理和参数校验切面(通用版)
  10. 漏洞分析中常用的堆调试支持
  11. python函数解读_通过7个Python函数来解释区块链
  12. bilibili 韩顺平Java后端学习路线
  13. 对视频中车辆车牌进行识别并圈出
  14. QQ免费企业邮箱申请配置
  15. 树突状细胞(DC细胞)特征及应用进展综述
  16. 效果超牛的基于声波通信和声音指纹的微信互动平台
  17. Android画三角形
  18. 2015年西安小升初奥数培训班奥数老师(排名排行)龙虎英雄榜
  19. 学习笔记(14):零基础掌握 Python 入门到实战-重复利用,事半功倍
  20. 给通信专业研究生——安心完成培养,你不是在劣势下和计算机学生抢饭碗来的

热门文章

  1. python用merge匹配和左连接_左手用R右手Python系列——数据合并与追加
  2. 人群场景分析--Slicing Convolutional Neural Network for Crowd Video Understanding
  3. debian/ubuntu 上安装和使用 Emacs
  4. 报错解决transmission: Error: Input/Output error和ls: reading directory '': Input/output error
  5. 手表后盖怎么装回去_链条掉了怎么装回去
  6. python 用for循环生成字典_我需要创建一个for循环来生成嵌套字典
  7. jdk和cglib代理
  8. iOS 2D绘图详解(Quartz 2D)之路径(点,直线,虚线,曲线,圆弧,椭圆,矩形)
  9. IOS后台运行机制详解(一)
  10. java ee eclipse idea,转----从Eclipse转移到IntelliJ IDEA一点心得