第 3 章知识点总结
3.1 .1 命名空间的using 声明
提醒
头文件不应包含 using 声明,因为头文件的内容会拷贝到所有引用它的文件夹中,如果头文件里有某个 using 声明,那么每个使用了该头文件的文件都会有这个声明。对于某些程序来说,由于不经意间包含了一些名字,反而可能产生始料未及的名字冲突。
3.2.1 定义和初始化 string 对象
string s1 // 默认初始化,s1是空串
string s2(s1) // s2 是 s1 的副本
string s3("value") // s3 是字面值“value"的副本,除了字面值最后的那个空字符外
string s3 = "value" // 等价于 s3("value")
string s4(n, 'c') // 把 s4 初始化为由连续 n 个字符c组成的串
直接初始化和拷贝初始化
如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化(copy initialization),编译器把等号右侧的初始值拷贝到新创建的对象中去。与之相反,如果不使用等号,则执行的是直接初始化(direct initialization)
当初始值只有一个时,使用直接初始化或拷贝初始化都可以。如果初始化要用到的值有多个,一般来说只能使用直接初始化的发生
string s3 = "value"; // 拷贝初始化
string s4(n, 'c'); // 直接初始化,把 s4 初始化为由连续 n 个字符c组成的串
string s8 = string(10, 'c'); // 拷贝初始化
// 等价于
string temp(10, 'c');
string s8 = temp;
3.2.2 string 对象上的操作
提醒
由于 size 函数返回的是一个无符号整型数,因此切记,如果在表达式中混用了带符号数和无符号数可能产生意想不到的结果。例如,假设 n 是一个具有负值的 int,则表达式 s.size() < n 的判断结果几乎是 true。这是因为负值 n 会自动转化成一个比较大的无符号值。
因此如果一个表达式中已经有了 size() 函数就不要再使用 int 了,这样可以避免混用 int 和 unsigned 可能带来的问题。
比较 string 对象( vector 也一样 )
① 如果两个 string 对象的长度不同,而且较短 string 对象的每个字符都与较长 string 对象对应位置上的字符相同,就说较短 string 对象小于较长 string 对象。
② 如果两个 string 对象在某些对应的位置上不一致,则 string 对象比较的结果其实是 string 对象中第一队相异字符比较的结果。
string str = "Hello";
string phrase = "Hello World";
string slang = "Hiya";
// 比较结果
slang > phrase > str
字面值和 string 对象相加
当把 string 对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+) 的两侧的运算对象至少有一个是 string 对象
string s1 = "hello", s2 = "world";
string s4 = s1 + ", " ; // 正确
string s5 = "hello" + ", "; // 错误:两个都不是 string 对象
string s6 = s1 + ", " + "world"; // 正确:每个加法运算符都有一个运算对象是 string
string s7 = "hello" + ", " + s2; // 错误:不能把字面值直接相加
// string 加法的工作机理和连续输入连续输出是一样的(即从左到右),所以:
s6 = (s1 + ", ") + "world"; // 括号里可以相加,加完仍为 string对象,可继续加
s7 = ("hello" + ", ") + s2; // 括号内加不了
提醒
C++ 语言中的字符串字面值并不是标准库类型 string 的对象。切记,字符串字面值与string 是不同的类型
3.2.3处理 string 对象中的字符
建议
使用 C++ 版本的 C 标准库头文件,C 语言的标准库形如 name.h ,C++ 则将这些文件命为 cname。也就是去掉了 .h 的后缀,而在文件名之前添加了字母 c。使用使用 C++ 版本的 C 标准库头文件可统一。
处理每个字符?使用基于范围的 for 语句
如果相对 string 对象中每个字符做点儿什么,目前最好的办法是使用 范围 for(range for)语句。 这种语句遍历给定序列中的每个元素并对序列中的每个值执行某种操作。
// 范围 for 语句语法形式
for (declaration : expression) {
statement
}
// 其中,expression 部分是一个对象,用于表示一个序列。declaration 部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration 部分的变量会被初始化为 expression 部分的下一个元素值。
// eg 逐行输出 string 对象中的字符
string str("some string");
for (auto c: str){
cout << c <<endl;
}
使用范围 for 改变字符串中的字符
如果想要改变 string 对象中字符的值,必须把循环变量定义成引用类型。记住,所谓引用只是给定对象的一个别名,因此当使用引用作为循环控制变量时,这个变量实际上被依次绑定到了序列的每个元素上。使用这个引用,我们就能改变它绑定的字符。
// 将字符串改写为大写
string s("Hello World!!!");
for (auto &c : s) {
c = toupper(c); // c 是一个引用,因此赋值语句将改变 s 中字符的值
}
cout << s <<endl;
只处理一部分字符?
① 使用下标
② 使用迭代器
题目
// 以下程序合法,输出 \0;
string s;
cout << s[0] <<endl;
3.3.0 标准库类型 vector
C++ 语言既有类模板(class template),也有函数模板,其中 vector 是一个类模板。
模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化(instantiation),当使用模板时,需要指出编译器应把类或函数实例化成何种类型。
对于类模板来说,我们通过提供一些额外信息来指定模板到底实例化成什么样的类,需要提供哪些信息由模板决定。提供信息的方式总是这样:即在模板名字后面跟一对尖括号,在括号内放上信息。
vector<int> ivec;
vector<vector<string>> file;
注意
vector 对象可以动态的增长,但是有一些副作用:
① 不能在范围 for 循环中向 vector 对象添加元素。
② 任何一种可能