《C++标准程序库》笔记之三
本篇博客笔记顺序大体按照《C++标准程序库(第1版)》各章节顺序编排。
--------------------------------------------------------------------------------------------
11 Strings 字符串
本章单独列出,讲述C++标准程序库中的string(字符串)型别,包括针对基本template class basic_string<>及其标准特化型别string和wstring的详细内容。 C++标准程序库中的string class 使你可以将string当做一个一般型别而不会令用户感觉有任何问题。你可以像对待基本型别那样地复制、赋值和比较string,再也不必担心内存是否足够、占用的内存实际长度等问题,只需运用操作符操作函数即可。简而言之,C++标准程序库对于string的设计思维就是,让它的行为尽可能像基本型别,不会在操作上引起什么麻烦。
11.1
以下语句:
strring::size_type idx = filename.find('.');
(1)在字符串filename中搜寻第一个'.'字符。有好几个函数可以在字符串内实施搜寻功能,函数find()是其中之一。你也可以从后向前搜寻,或是搜寻子字符串,或是在字符串的某个范围内搜寻,或是同时搜寻数个字符。所有这些搜寻函数都返回第一个匹配位置(一个索引)。没错,返回值是个整数,而不是迭代器。字符串的一般接口并不依赖STL概念。然而字符串的确也提供了数种迭代器。所有搜寻函数的返回型别都是string::size_type,这是string class 定义的一个无正负号整数型别。当然啦,第一个字符的索引值为0,最后一个字符的索引值是numberOfCharacters-1.注意,numberOfCharacters并不是一个有效索引。和C-string不同,string对象的字符串尾部并没有一个特殊字符'\0'.
(2)如果搜寻失败,必须返回一个特殊值来表示,该值就是npos,定义于string class 中。如下面这一行语句用来检验搜寻动作是否失败: if (idx == string::npos) 注意,当你打算检验搜寻函数的返回值时,应该使用string::size_type型别而不是int或unsigned。否则上述与string::npos的比较动作将无法有效运行。
11.2
(1)string型别和wstring型别
C++标准程序库提供了两个basic_string<>特化版本:
1. string是针对char而预先定义的特化版本:
namespace std
{
typedef basic_string<char> string;
}
2. wstring 是针对wchar_t而预先定义的特化版本:
namespace std
{
typedef basic_string<wchar_t> wstring;
}
(2)操作函数(Operations)
表11.1 列出针对字符串而设计的所有操作函数
(3)string的构造函数和析构函数(Constructors and Destructors)
(4)Strings和C-Strings C++ Standard 将字符串字面常数的型别由char* 改为const char*。为了提供向下兼容性,C++ Standard规定了一个颇有争议的隐式转换,可从const char*隐式转换为char*。由于字符串字面常数的型别并非string,因此在新的string object和传统的C-strings之间必须存在一种强烈关系:在“strings和string-like object共通的操作场合”(例如比较、追加、插入等等动作)都应该可以使用C-strings(也即,对于string object提供支持的函数,也都应该相应的支持C-string)。或者具体地说,存在一个从const char* 到strings的隐式型别转换。然而却不存在一个从string object 到C-string的自动型别转换。这是出于安全考虑,防止意外转型导致奇异行为(char*经常有奇异的行为)和模棱两可(例如在一个结合了string和C-string的表达式中,既可以把string转化为char*,也可以反其道而行,这就导致模棱两可)。有好几种办法可以产生或改写/复制C-string。更明确地说,c_str()可以得到“string 对应的C-string”,所得结果和“以‘\0’为结尾的字符数组”一样。运用copy(),你也可以将字符串内容复制或写入既有的C-string或字符数组内。
请注意:
1. ‘\0’ 在string之中并不具有特殊意义,但在一般C-string中却用来标识字符串结束。在string中,字符‘\0’和其它字符的地位完全相同。
2. 千万不要以null指标(NULL)取代char*作为参数,这样会导致奇异行为,因为NULL具有整数型别,在单整数型别的重载函数版本上会被解释为数字0或“其值为0”的字符。
有三个函数可以将字符串内容转换为字符数组或C-string:
1. data() 以字符数组的形式返回字符串内容。由于并未追加‘\0’字符,所以返回型别并非有效的C-string。
2. c_str() 以C-string形式返回字符串内容,也就是在尾端添加‘\0’字符。
3. copy() 将字符串内容复制到“调用者提供的字符数组”中。不添加‘\0’字符。
std::string s("");
atoi(s.c_str());
f(s.data(), s.length()); char buffer();
s.copy(buffer, );
s.copy(buffer, , );
注意,data() 和 c_str() 返回的字符数组由该字符串拥有。也就是说调用者千万不可修改它或释放其内存。例如:
std::string s;
...
foo(s.c_str()); // s.c_str() is valid during the whole statement const char* p;
p = s.c_str(); // p refers to the contents of s as a C-string p
foo(p); // OK(p is still valid)
s += "ext"; // invalidates p
foo(p); // ERROR:argument p is not valid
一般而言,整个程序中你应该坚持使用strings,直到你必须将其内容转化为char*时才把它们转换为C-string。请注意c_str()和data()的返回值有效期限在下一次调用non-const成员函数时即告终止。
让string拥有足够的容量是很重要的,原因有二:
1. 重新分配会造成所有指向string的references、pointers和iterators失效。
2. 重新分配很耗时间。
容量概念应用于string和应用于vector是相同的;但有一个显著差异:面对string你可以调用reserve()来缩减实际容量,而vector的reserve()却没有这项功能。string的reserve()是一种非强制性适度缩减请求——不保证一定可以如愿。C++ Standard 规定,唯有在响应reserve() 调用时,容量才有可能缩减。因此即使发生“字符被删除或被改变”的事情,任何其他字符只要位于“被操作字符”之前,指向它们身上的那些references、pointers和iterators就仍然保持有效。
(5)元素存取(Element Access)
String有两种方法可以访问单一字符:subscript(下标)操作符[] 和 成员函数 at()。但有两点区别:
1. operator [] 并不检查索引是否有效,at() 则会检查。如果at()指定的索引无效,系统会抛出out_of_range异常。如果调用operator[]指定的索引无效,其行为未有定义——可能存取非法内存,引起边缘效应或崩溃。
2. 对于 operator[] 的const 版本,最后一个字符的后面位置也是有效的。此时的实际字符数是有效索引。在此情况下 operator[] 的返回值是“由char型别值default构造函数所产生”的字符。因此,对于型别为string的对象,返回值为“\0”字符。
其他任何情况(包括成员函数at() 和 operator[] 的non-const 版本),实际字符数都是个无效索引。如果使用该索引,会引发异常,或导致未定义行为。下例:
const std::string cs("nico"); // cs contains: 'n' 'i' 'c' 'o'
std::string s("abcde"); // s contains: 'a' 'b' 'c' 'd' 'e'
s[] // yields 'c'
s.at() // yields 'c' s[] // ERROR:undifined behavior
s.at() // throws out_of_range s.[s.length()] // ERROR:undefined behavior
cs[cs.length()] // yields '\0'
s.at(s.length()) // throw out_of_range
cs.at(cs.length()) // throw out_of_range
为了允许更改string内容,operator[] 的 non-const 版本和at() 都返回字符的reference。一旦发生重分配行为,那些reference立即失效:
std::string s("abcde"); // s contains: 'a' 'b' 'c' 'd' 'e' char& r = s[]; // reference to third character
char* p = &s[]; // pointer to fourth character r = 'X'; // OK, s contains: 'a' 'b' 'X' 'd' 'e'
*p = 'Y'; // OK, s contains: 'a' 'b' 'X' 'Y' 'e' s = "new long value"; // realloction invalidates r and p r = 'X'; // ERROR: undefined behavior
*p = 'Y'; // ERROR: undefined behavior
(6)I/O操作符 ">>", "<<"
String classes 在命名空间std内还提供了一种用于逐行读取的特殊函数:std::getline() 。该函数读取所有字符,包括开头的空格符,直到遭遇分行符号或end_of_file。分行符号可指定,不可添加。缺省情况下分行符号为newline字符。
(7)表11.5 列出来了Strings搜寻函数。
注意,所有搜寻函数都返回符合搜寻条件之字符区间内的第一个字符的索引。如果搜寻不成功,则返回npos,其类型为 std::string::size_type。 不能以 int 或 unsigned 作为返回值型别;否则返回值与string::npos之间的比较可能无法正确执行。这是因为npos在basic_string类模板里面被设计为-1。不幸的是size_type(由字符串配置器allocator定义)需为无正负号整数型别。因为缺省配置器以型别size_t作为size_type。于是 -1 被转换为无正负号整数型别,npos也就成了该型别的最大无符号值。
(8)Strings对迭代器的支持
1. string是字符的有序群集,所以C++标准程序库为strings提供了相应接口,以便将字符串当做STL容器使用。
2. String迭代器是random access(随机存取)迭代器。
3. 对迭代器而言,如果发生重分配(reallocation),或其所指值发生某些变化,迭代器就会失效。
表11.6 提供了一份迭代器操作函数列表
(9)Strings 和 Vectors
两者都是一种动态数组。因此可以把strings视为一种“以字符作为元素”的特定vectors。实用上可把string当作STL容器使用。当两者的主要差异在于各有目标:
1. vectors 首要目标是处理和操作容器内的元素,而非容器整体。因此实作时通常会为“容器元素的操作行为”进行优化。
2. strings 主要是把整个容器视为整体,进行处理和操作,因此实作时通常会为“整个容器的赋值和传递”进行优化。
不同的目标导致完全不同的实作手法。例如strings 通常采用reference counting(这种手法可以加速string的复制和赋值)手法,vectors则决不会如此。
《C++标准程序库》笔记之三的更多相关文章
- C++标准程序库笔记之一
本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ---------------------------------------------------------- ...
- 《C#图解教程》读书笔记之三:方法
本篇已收录至<C#图解教程>读书笔记目录贴,点击访问该目录可获取更多内容. 一.方法那些事儿 (1)方法的结构:方法头—指定方法的特征,方法体—可执行代码的语句序列: (2)方法的调用:参 ...
- sc7731 Android 5.1 LCD驱动简明笔记之三
此篇笔记基于sc7731 - android 5.1,对lcd的gralloc库做一个简明笔记. 第一部分 调用gralloc.sc8830.so所谓的Gralloc模块,它就是一个模块,一个操作ke ...
- Django 学习笔记之三 数据库输入数据
假设建立了django_blog项目,建立blog的app ,在models.py里面增加了Blog类,同步数据库,并且建立了对应的表.具体的参照Django 学习笔记之二的相关命令. 那么这篇主要介 ...
- 《深入实践Spring Boot》阅读笔记之三:核心技术源代码分析
刚关注的朋友,可以回顾前两篇文章: 基础应用开发 分布式应用开发 上篇文章总结了<深入实践Spring Boot>的第二部分,本篇文章总结第三部分,也是最后一部分.这部分主要讲解核心技术的 ...
- 【Visual C++】游戏编程学习笔记之三:游戏循环的使用
本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44208419 作者:Zee ...
- Kinect开发笔记之三Kinect开发环境配置具体解释
0.前言: 首先说一下我的开发环境,Visual Studio是2013的,系统是win8的64位版本号,SDK是Kinect for windows SDK 1.8版本 ...
- 学习笔记之三十年软件开发之路 - Things I Learnt The Hard Way (in 30 Years of Software Development)
三十年软件开发之路 https://mp.weixin.qq.com/s/EgN-9bIHonRid1DM0csQDw https://blog.juliobiason.net/thoughts/th ...
- Vue.js 学习笔记之三:与服务器的数据交互
显而易见的,之前的02_toDoList存在着一个很致命的缺陷.那就是它的数据只存在于浏览器端,一但用户关闭或重新载入页面,他之前加入到程序中的数据就会全部丢失,一切又恢复到程序的初始状态.要想解决这 ...
随机推荐
- QMDP-Net: Deep Learning for Planning under Partial Observability
一篇用deep neural network做POMDP的论文
- String和inputstream互转【转文】
URLConnection urlConn = url.openConnection(); // 打开网站链接s BufferedReader reader = new BufferedReader( ...
- qualcomm batch 烧录脚本
在烧录android系统候用到了windows的批处理文件,拿出来分析一下,顺便记录一下高通平台烧录系统的命令. @echo off :: @ :不显示后面的命令,就是后面的"echo of ...
- 通过C#脚本实现旋转的立方体
一.介绍 目的:通过一个简单的例子(鼠标点击,使立方体旋转和变色)熟悉Unity中C#脚本的编写. 软件环境:Unity 2017.3.0f3 . VS2013. 二.C#脚本实现 1,启动Unity ...
- 性能优化系列二:JVM概念及配置
一.虚拟机组成 虚拟机主要由三部分组成:编译器(执行引擎),堆与栈. 1. 编译器 编译器分为即时编译器与解释器. 即时编译器将代码编译成本地代码存于code区.因此它快,但它有内存限制! 解释器逐行 ...
- Solr系列一:Solr(Solr介绍、Solr应用架构、Solr安装使用)
一.前言 前面已经学习了Lucene的分词.索引详解.搜索详解的知识,已经知道开发一个搜索引擎的流程了.现在就会有这样的一个问题:如果其他的系统也需要使用开发的搜索引擎怎么办呢?这个时候就需要把开发的 ...
- Mybatis表关联多对一
在上章的 一对多 中,我们已经学习如何在 Mybatis 中关联多表,但在实际项目中也是经常使用 多对一 的情况,这些查询是如何处理的呢,在这一节中我们来学习它.多表映射的多对一关系要用到 mybit ...
- HDU 3455 Leap Frog(线性DP)
Problem Description Jack and Jill play a game called "Leap Frog" in which they alternate t ...
- 利用Android NDK编译lapack
最近有这方面的需要,但是在网上查了一下,几乎没有讲这个的.后来发现了外国某个大牛在github上的project.拉下来自己编译了一下竟然通过了,记录如下: 1.从https://github.com ...
- SharePoint 2013 显示“以其他用户身份登录”菜单项
最近在SharePoint 2013的网站上发现,没有看到有切换不同用户登录的入口,在SharePoint 2010中是存在这样的菜单项能够很方便的进行用户切换的,不知道为什么,SharePoint ...