1、标准库类型vector:表示对象的集合。包含头文件:
1 #include2 using std:vector;
c++既有类模板也有函数模板,其中vector是一个类模板。对于类模板来说我们通过额外的一些信息来制定模板到底实例化成什么样的类。以vector为例,提供的额外信息是vector内所存放对象的类型。
1 vector<int> ivec //ivec存放int类型的对象 2 vectorsale_vec //sale_vec存放sale_item类型的对象 3 vector string>> file //该向量的元素是vector对象
定义和初始化vector对象:
1 vector<int> ex; //初始化vector对象 2 此处为ex添加一些值 3 vector<int> pr = ex; //把ex的值给pr
列表初始话vector对象:
1 vector<string> str = {"a", "b", "c"};
1 vector<int> pc = {10}; //为pc增加一个10的值 2 vector<int> pr = (10); //为pr增加10个为0的值 3 vector<int> pt = (10, 3); //为pt增加10个为3的值
向vector对象中添加对象:
1 vector<string> pk; //初始化vector对象 2 int i = 1; 3 while(i>100) 4 { 5 pk.push_back("HD"+i); //利用push_back函数为pk后面添加一个值 6 i++; 7 }
vector的其他一些函数操作,其中下标只能使用已有的值进行操作,不能用下标来增加元素。
我们用下标来统计一个班的成绩分布:
1 int main() 2 { 3 vector<int> scorse(10, 0); //初始化vector对象,有11个为0的值 4 unsigned int grade; //定义一个整型的值 5 while (cin >> grade) 6 { 7 if (grade == 0) 8 { 9 break; 10 } 11 scorse[grade / 10]++; //判断这个数的下标在那个区间内然后为那个区间加1,比如78/10=7,说明在70这个区间 12 } 13 for (size_t i = 0; i < 10; i++) 14 { 15 cout << i*10; 16 cout << "到"; 17 cout << (i + 1) * 10; 18 cout << "区间的人数为:"; 19 cout << scorse[i] << endl; 20 } 21 return 0; 22 }
2、迭代器
迭代器类似于指针,提供了对象的间接访问,其对象是容器中的元素。与指针不一样的是,他不是使用的取地址符,它拥有迭代器的类型和成员。
他们都拥有begin和end成员。begin是负责返回容器的第一个元素,end是返回容器最后一个元素的下一位置。如果s.bgein == s.end说明这个容器为空。
我们看个栗子:
1 string str("hello world"); 2 if(str.begin() != str.end()) //判断是否字符串为空 3 { 4 auto it = str.begin(); //得到str的第一个字符 5 *it = toupper(*it); //将第一个字母改写成大写 6 }
我们在if内部,申明了一个迭代器变量it并把begin的返回的结果赋给他,这样就得到了容器中第一个字符的迭代器,接下来通过解引用运算符将第一个字符更改为大写字母。顺便说一下解引用: x=*p 把指针p指向的值赋值给x。
将迭代器从一个元素移到另一个位置,这里注意的是移动的不是元素而是迭代器,就像指针一样,移动的是指针指向的位置。
需要注意的是end并没有指示某个元素,所以不能对他进行递增减和解引用操作。
1 string str("something"); 2 for(auto it = str.begin(); it != str.end() && !issapce(*it);++it) 3 { 4 *it = toupper(*it); 5 }
对于迭代器的类型我们一般来说不需要知道,不过他也是有类型的,是iterator和const_iterator。
begin和end:他们返回的类型具体由所返回的元素决定的,比如vector
这里顺便说一下.和->,我记得前面好像写过,但是没有过去复习忘了,.是成员访问符,其前面可以是一个类等等,而->箭头运算符,也是去访问成员的,不过他的前面必须是指针指向的类等等。当然现在又有迭代器这个类型。迭代器使用->表示迭代器解引用,然后去访问成元。比如 it->empty等价于(*it).empty,都是判断这个it迭代器指示的元素是否为空。
注意:但凡使用了迭代器的循环体,都没有向迭代器所属的容器增加元素。
前四个运算的结果都是迭代器,也就是容易的里元素的位置。第五个的结果是两个迭代器所指示的元素的位置之间距离的值,他的结果类型是有符号的difference_type。最后一个是比较两个迭代器所指示位置比较前后的结果的布尔值。
最后写一个有序数列的二分法搜索:
1 int main() 2 { 3 vector<int> vt(10, 0); 4 for (size_t i = 0; i < 10; i++) 5 { 6 cin >> vt[i]; //存储输入的有序数列 7 } 8 int souc = 8; //我们要搜索的值 9 auto begin = vt.begin(); 10 auto end = vt.end(); 11 auto mid = begin + (end - begin) / 2; //这里为什么不用end/2,因为是迭代器不能进行除法运算 12 while (mid != end && *mid != souc) 13 { 14 if (souc > *mid) 15 begin = mid + 1; //把mid后面的位置当作begin 16 else 17 end = mid; //把mid当作尾迭代器 18 mid = begin + (end - begin) / 2; 19 } 20 cout << (mid - vt.begin()) + 1 << endl; 21 system("pause"); 22 return 0; 23 }
3、数组
数组除了大小固定这一特点外,其用法基本与vector一致。
对于数据的首元素和尾后元素的访问,尽管通过计算能得到尾后元素,但是这种用法极容易出错,所以C++11,增加了begin和end函数来访问,他们的用法和容器的成员函数begin和end函数用法相似,但是数据不是类类型,所以没有成员函数,所以正确的形式是数组作为他们的参数。这两个函数定义在iterator中。
1 int array[10] = {0,1,2,3,4,5,6,7,8,9} 2 int *pr = begin(array); //得到第一个元素的指针 3 int *pre = end(array); //得到尾后位置的指针 4 ++pr; //指向array[1]的指针,所以指针也是迭代器
通过一个栗子来看看
1 int array[5] = {0,3,5,-1,8}; 2 int *pr = begin(array); 3 int *pre = end(array); 4 while(pr != pre && *pr >= 0) 5 { 6 ++pr; 7 }
还有一个要注意的点是:数组内置的下标运算所用的索引值不是无符号类型,这一点与vector和string不一样。
1 int array[] = {0,1,2,3,4}; 2 int *pr = &array[2]; //把数组的第三个元素的地址给pr 3 int vi = pr[1]; //等价于pr[0+1]等价于array[3] 4 int vif = pr[-2]; //等价于pr[0-2]等价于array[0]
从代码知道我们内置的下标可以用负数,而我们标准库string和vector虽然也可以使用下标,但是却只能是无符号类型。标准库类型限定使用的下标必须是无符号类型。
4、C风格字符串
C风格字符串不是一个类型,而是为了使用字符串而形成约定俗成的写法。按此写法把字符串放到字符数组中并且以空字符结束("\0")。我们一般使用指针来操作这些字符串。
操作C风格字符串不能像我们操作string一样直接操作,而是借助一些函数来操作。这些函数定义在cstring头文件中。
对大多数应用来说,使用标准库string要比使用C风格字符串更安全,更高效。
混用string对象和C风格字符串:允许以空字符结束的字符数组来初始化string对象或者赋值。在string对象的加法运算中,允许其中一个为字符数组。但是反过来如果用string对象为字符数据赋值是不行的。
但是我们string对象有一个s_str函数,他的返回值是一个C风格的字符串,也就是返回是一个指针类型,该指针指向一个以空字符结尾的字符数组,结果指针的类型是const char*。