第三章
- string是可变长字符串,vector是可变长集合,标准库定义了这些具有更高级性质的类型,它们尚未直接实现到计算机硬件中。(不像int和char)
- 头文件中不应该含有using声明,因为头文件的内容会拷贝到引用了它的文件中去,也许会造成意想不到的命名冲突。
string
- string s1 = “hiya”; 拷贝初始化(使用等号初始化一个变量时)string s2(“hiya”);直接初始化(不使用等号时)
- 希望保留输入的空白符时,要使用getline替代**>>**
- 假如heheda是一个string变量,getline(cin,heheda)会从cin流中读入内容,一直到换行符,同时换行符也读进来了,但是把所读内容存到heheda中时,没有把换行符存进去。(**触发getline返回的那个换行符实际上被丢弃掉了,得到的string对象中并不包含该换行符)
- 使用加号“+”运算符时,要注意两侧的运算对象至少有一个是string的。比如 string s = “hello” + “,”+s1;,这就是错误的,可以改成string s = “hello” +(","+s1);
vector
- vector是模板而非类型, string是一种类型
- vector能容纳绝大多数类型的对象作为元素,但是引用不是对象,所以不存在包含引用的vector。
- 注意一些老的编译器,当vector的元素还是vector的时候,需要多加一个空格,示例如下:vector<vector<int> >
- vector使用的一般操作是:先定义一个空的vector对象,之后在运行时动态添加元素(比如push_back())。
- 范围for语句是C++11的一个新特性,常用在遍历一个对象中的所有元素,但是在对vector使用时要注意,如果循环体内包含有向vector对象中添加元素的语句,是不能使用vector的,原因是范围for语句体内不应该改变其遍历序列的大小。
- 使用size_type(*.size()的返回值类型),要注意它是由那种类型定义的,string中的是string::size_type,而vector中的要使用如下形式:vector<int>::size_type ,不是 vector::size_type.
- vector和string的下标运算符能访问已经存在的元素,不能用于添加元素
- 确保下标合法的一种有效手段:尽可能使用范围for语句。
- 列表初始化vs值初始化:
#include <vector>
vector<int> ivec1{10,1};//ivec有两个元素,分别是10和1
vector<int> ivec2(10,1);//ivec2有10个元素,每个元素都是1
vector<int> ivec3{10};//ivec3有1个元素,是10
vector<int> ivec4(10);//ivec4有10个元素,每个值默认初始化为0
迭代器 iterator
- 所有标准库容器都可以使用迭代器(严格来说,string对象不属于容器类型,但是string支持迭代器)
- 有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置。
- begin()返回容器中的第一个元素;end()返回尾元素的下一个位置,仅是个标志,表示我们处理完了容器中的所有元素。如果对象为空,则begin()和end()指向相同。
- 因为end返回的迭代器并不实际指向某个元素,所以不能对其进行递增和解引用
- 泛型编程:C++喜欢用 != 而不是< ,喜欢用迭代器而不是下标, 因为这种编程风格在标准库提供的所有容器上都有效,但是并不是所有容器都有下标和大于、小于号,因此我们养成用 != 和迭代器的习惯,就不用太在意用的到底是哪种容器。
- 迭代器的类型有两种,iterator(能读能写)和const_iterator(只能读)。如果vector对象或string对象是一个常量,那只能使用const_iterator;如果不是常量,那既能使用iterator也能使用const_iterator.
- cbegin()和cend()返回的迭代器类型都是const_iterator,而不会管调用他们的对象是不是常量类型。
- **但凡是使用了迭代器的循环体,都不要向容器中添加元素。**这一点和范围for循环很像。
数组
- 如果不清楚元素的确切个数,请使用vector。
- 数组可以通过标准库的begin和end获得开始指针和尾后指针,但是毕竟数组不是类类型,因此这两个函数不是成员函数,不能用点操作符。正确使用方式如下:
int ia[] = {0,1,2};
int *beg = begin(ia);
int *end = end(ia);//end函数返回的是ia尾元素下一位置的指针
- 内置的下标运算符所用的索引值不是无符号类型(比如数组的下标可以是负数),这一点与vector和string不一样。
- 使用标准库string要比使用C风格字符串更安全、更高效。
//这段代码就很不安全,如果largeStr的空间不够,将引发严重错误
#include <cstring>
char largeStr[100];
char ca1[] = "abc";
char ca2[] = "def";
strcpy(largeStr,ca1);
strcat(largeStr," ");
strcat(largeStr,ca2);
- 混用string和C风格字符串:string的c_str()成员函数
string s("hello world!");
char *str = s; //错误,不能用string对象初始化char*
const char *str = s.c_str();//正确
6.vector对象不能用来初始化数组,但是反过来是成立的,可以用数组初始化vector对象,比如下面的代码:
int int_arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ivec(begin(int_arr),end(int_arr));//ivec中会有6个元素,分别是int_arr的对应副本
- 现代的C++程序应当尽量使用vector和迭代器,避免使用内置数组和指针,因为使用数组和指针很容易出错,不论是概念上的还是语法上的。
多维数组
- 严格来说,C++语言中没有多维数组,通常说的多维数组其实是数组的数组,谨记这一点,对今后理解和使用多维数组大有益处。比如对下面代码的理解:
int a[3][4];//大小为3的数组,每个元素都是含有4个整数的数组
- 要使用范围for循环处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型。(好好理解这个知识点)