-
迭代器
- 所有标准库容器都可以使用迭代器,比如vector,严格来讲,string对象不属于容器类型,但是string支持很多与容器类型类似的操作,string支持迭代器。
- 使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素。
- 可以使用迭代器的这些类型中,都有begin和end成员,这两个成员可以返回迭代器:begin负责返回指向第一个元素(或第一个字符)的迭代器;end负责返回指向容器(或string对象)“尾元素的下一位置”的迭代器,也就是说,end返回的这个迭代器指向的是容器的一个本不存在的“尾后”元素,这样的迭代器没有什么实际含义,仅是一个标记而已,表示我们已经处理完了容器中的所有元素。end 成员返回的迭代器常被称作尾后迭代器或者简称为尾迭代器。
- 特殊情况下如果容器为空,则 begin 和 end 返回的是同一个迭代器,都是尾后迭代器。
- 和指针类似,也能通过解引用迭代器来获取它所指示的元素,执行解引用的迭代器必须合法并确实指示着某个元素。 试图解引用一个非法迭代器或者尾后迭代器都是未被定义的行为。
- 检查一个字符串是否为空,可以通过检查 begin 和 end 返回的结果是否一致,如果结果一样,说明字符串为空。
- 箭头运算符(->):箭头运算符把解引用和成员访问两个操作结合在一起,解引用运算符的优先级低于点运算符,(*it).empty()可写成it->empty()。
*1.因为 end 返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用的操作。
*2.一般来说我们也不知道(其实是无须知道)迭代器的精确类型。而实际上,那些拥有迭代器的标准库类型使用 iterator 和 const_iterator来表示迭代器的类型。(const_iterator:能读取但不能修改)
*3.如果 vector 对象或 string 对象是一个常量,begin和end返回const_iterator;
*4.如果 vector 对象或 string 对象不是常量,begin和end返回iterator。
*5.如果对象只需读操作而无须写操作的话最好使用常量类型 ( 比如const_iterator)。为了便于专门得到const_iterator类型的返回值 , C ++11 新标准引入了两个新函数 , 分别是 cbegin和 cend。
*6.不能在范围 for 循环中向 vector 对象添加元素。但凡是使用了迭代器的循环体,都不要向迭代器所属的容器添加元素。(在范围 for 语句中 , 预存了 end ( ) 的值 . 一旦在序列中添加 ( 删除 ) 元素 , end 函数的值就可能变得无效了)
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
string str("hello world");
if (str.begin() != str.end())
{
auto it = str.begin();
if (islower(*it))
{
*it = toupper(*it);
}
}
cout << str << endl;
return 0;
}
vector<string> sVec(2, "hello world");
for (vector<string>::const_iterator it = sVec.cbegin(); it!= sVec.cend() && !it->empty(); ++it)
{
cout << *it << endl;
}
- 迭代器运算符:
- string和vector提供了更多额外的运算符:
- 向前、向后移动多个元素:iter+n、iter-n;
- 两个迭代器相减,获取距离:距离指的是右侧的迭代器(减数,减号右边的迭代器)向前移动多少位置就能追上左侧的迭代器(被减数,减号左边的迭代器),其类型是名为 difference_type 的带符号整型数。string 和vector都定义了difference_type。因为这个距离可正可负,所以difference _ type 是带符号类型的。
//寻找字符串中是否有2
string text("012345678");
auto begin = text.begin();
auto end = text.end();
auto mid = begin + (end - begin) / 2;
cout << *mid << endl;
while (mid != end && *mid != '2')
{
if (*mid > '2')
{
end = mid;
}
else {
begin = mid + 1;
}
mid = begin + (end - begin) / 2;
cout << *mid << endl;
}
if (mid == end)
{
cout << "不存在要找的数";
}
else {
cout << "存在要找的数";
}
-
数组
- 与vector相似,数组也是存放类型相同的对象的容器(所以不存在引用的数组),但与vector不同的是,数组的大小确定不变,不能随意向数组中增加元素。
- 定义数组时,必须指定数组类型,不允许用auto关键字由初始值的列表推断。
- C++声明数组时,格式为a[d]:a表示数组的名字,d表示数组元素的个数(必须大于0),d必须是一个常量表达式。
- 对数组元素进行列表初始化时,可以忽略数组维度,编译器会根据初始值的数量计算并推断出来;不忽略数组维度时,维度应大于等于元素个数。
- 字符数组还有一种初始化的形式,使用字符串字面值进行初始化。使用这种方式时,应该注意字符串字面值的结尾处还有一个空字符,这个空字符也会像字符串的其他字符一样被拷贝到字符数组中去。
- 在使用数组下标的时候,通常将其定义为 size_t 类型。 size_t是一种机器相关的无符号类型,它被设计得足够大以便能表示内存中任意对象的大小。
C#:
int numLength = 10;
int[] nums=new int[numLength];
C++:
constexpr int numLength = 10;
int nums[numLength];
C++数组初始化:
int nums1[5] = { 0,1,2,3 };//5个元素{0,1,2,3,0}
int nums2[] = { 0,1,2,3 };//4个元素
//int nums3[2] = { 0,1,2 };//错误,维度小于元素个数
string strs1[3] = { "hello","world" };//3个元素{ "hello","world","" }
char chars[] = "hello";//6个元素
int length= sizeof(chars) / sizeof(chars[0]);
cout << length << endl;//6
-
指针和数组
- 对数组的元素使用取地址符(&)就能得到指向该元素的指针;
- 在很多用到数组名字的地方,编译器都会自动地将其替换为一个指向数组首元素的指针。
- 当定义一个auto变量,将数组作为其初始值时,该变量类型是指针而不是数组。
- 当使用decltype关键字获取数组的类型时,获取到的类型和数组的原类型相同。
string nums[] = { "one","two","three" };
string *p = &nums[0];
string *p = nums;//同上
auto nums1(nums);//nums1是一个字符串类型的指针,指向nums的第一个元素
auto nums1(&nums[0]);//同上
decltype(nums) nums2;//nums2是一个含有三个字符串元素的数组,即 string nums2[3];
- 指针也是迭代器,使用指针也可以遍历数组中的元素;
- 获取指向数组第一个元素的指针:&nums[0]或者使用begin(nums)函数;
- 获取指向数组尾元素的下一位置的指针:&nums[nums的长度]或者使用end(nums)函数;尾后指针不指向具体的元素,所以,不能对尾后指针执行解引用或者递增的操作。
- 因为数组不是类类型,和vector等容器不同,所以,begin和end不是成员函数,正确的使用形式是将数组作为它们的参数。这两个函数定义在iterator头文件中。
- 和迭代器一样 , 两个指针相减的结果是它们之间的距离。参与运算的两个指针必须指向同一个数组当中的元素 。
- 两个指针相减的结果的类型是一种名为 ptrdiff_t 的标准库类型,和 size_t 一样,ptrdiff_t 也是一种定义在 cstddef 头文件中的机器相关的类型。因为差值可能为负值,所以ptrdiff_t 是一种带符号类型。(在vcruntime.h文件中,发现ptrdiff_t 就是int类型)
#include <string>
#include <iterator>
using namespace std;
int main()
{
int nums[] = {0,1,2,3,4,5};
auto *beginNums = begin(nums);//beginNums是int类型的指针,指向nums的第一个元素
int *endNums = end(nums);
int *midNums = nums + 2;//midNums指向nums的第二个元素
ptrdiff_t dis1 = endNums - midNums;
cout << dis1 << endl;//4
string strs[3] = { "hello","world" };
auto *beginStrs = begin(strs);//beginStrs是string类型的指针,指向strs的第一个元素
string *endStrs = end(strs);
int dis2 = endStrs - beginStrs;
cout << dis2 << endl;//3
}
*1.内置的下标运算符所用的索引值不是无符号类型,标准库类型(vector)与string限定使用的下标必须是无符号类型。
*2.对数组执行下标运算其实是对指向数组元素的指针执行下标运算。
int nums[] = { 0,1,2,3 };
int *p = &nums[2];
int a = p[1];//p[1]想当于*(p+1),即nums[3]
int b = p[-2];//p[-2]想当于*(p-2),即nums[0]
-
数组和vector
- 不允许使用一个数组为另一个内置类型的数组赋初值 , 也不允许使用 vector 对象初始化数组。
- 允许使用数组来初始化 vector 对象,要实现这一目的 , 只需指明要拷贝区域的首元素地址和尾后地址就可以了。
{
int nums[] = { 0,1,2,3 };
int nums1[] = nums;//错误
vector<int> numVec{ 0,1,2,3 };
int nums2[] = numVec;//错误
//vector<int> numsVec = { begin(nums), end(nums) };
//vector<int> numsVec{ begin(nums), end(nums) };
vector<int> numsVec(begin(nums), end(nums));//正确
cout << numsVec.size() << endl;//4
for (auto a : numsVec)
{
cout << a << endl;
}
}
-
多维数组
- 严格来说, C ++ 语言中没有多维数组,通常所说的多维数组其实是数组的数组。
- 当一个数组的元素仍然是数组时,通常使用两个维度来定义它:一个维度表示数组本身大小,另外一个维度表示其元素(也是数组)大小。
- 对于二维数组来说,常把第一个维度称作行(row),第二个维度称作列(col)。
- 多维数组初始化:a.允许使用花括号括起来的一组值初始化多维数组;b.类似于一维数组,在初始化多维数组时也并非所有元素的值都必须包含在初始化列表之内,其他未列出的元素执行默认值初始化。
- 如果表达式含有的下标运算符数量和其数组的维度一样多,该表达式的结果将是给定类型的元素;反之,如果表达式含有的下标运算符数量比其数组的维度小,则表达式的结果将是给定索引处的一个内层数组。
- 使用范围 for 语句只读多维数组时,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型(避免数组被自动转成指针);要修改多维数组时,都应该是引用类型。
int array0[3][4] = { {0,1,2,3},{4,5,6,7},{8,9,10,11} };
int array1[3][4] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
int array2[3][4] = { {0},{1} };//{0,0,0,0,1,0,0,0,0,0,0,0}
int array3[3][4] = { 0, 1 }; //{0,1,0,0,0,0,0,0,0,0,0,0}
int a[3][4];
int b[2][4] = { 0,1,2,3,4,5,6,7 };
a[2][3] = b[1][1];// b[1][1]下标运算符数量为2=数组b维度2
int (&c)[4] = b[1];// b[1]下标运算符数量为1 != 数组b维度2
int b[2][4] = { 0,1,2,3,4,5,6,7 };
int offset = 1;
for (auto &row : b) {
for (auto &col : row)
{
col += offset;
}
}
for (auto &row : b) {
for (auto col : row)
{
cout << col << endl;
}
}
- 指针和多维数组
- 当程序使用多维数组的名字时,也会自动将其转换成指向数组首元素的指针。
- 通过使用 autc 或者 decltype就能尽可能地避免在数组前面加上一个指针类型了。
int a[3][4];
int *p1[4]; //相当于int* p1[4];p1是存放int指针类型的数组
int (*p2)[4] = a;//p2是指针,指向int类型的数组;p2指向a的首元素,该首元素是一个数组,含有4个整型数
p2 = &a[2];//p2指向a的尾元素
auto p3 = a;//相当于 int (*p3)[4] = a;