本章主要介绍string, vector,迭代器等标准库类型和内置类型数组。
一:命名空间的using声明
例如using std::cin,以后使用cin不必说明std了。还可以用using namespace std;
二:string类型的初始化
第三个和第五个是拷贝初始化,其他几个是直接初始化。
三:字符串的输入
定义string s; 可以使用cin>>s;输入字符串,读取方式是遇到空白符跳过,直到遇到第一个真正的字符,然后开始读入s,当遇到空白符结束。也可以使用getline(cin, s); 此时从缓冲区读取字符到s,直到遇到换行符,换行符被丢弃。
四:string的相加
string s = "hello";
string s1 = s + "," + "world";
string s2 = "ximing" + "," + s;
在上面的代码中,s1定义合法,s2定义错误。s2定义中,先执行"ximing"+",",而编译器无法执行该语句。因为在c++中,字符串字面值不是string类型啊,而是字符数组类型鸭。”+“的运算符重载是在string中定义的鸭。
五:头文件cctype
六:cctype是ctype.h,有.h后缀到c前缀说明是继承于c语言的类库。但是c前缀的头文件的名字都是包含于std的命名空间,所以建议使用c前缀鸭。
七:for循环新形式
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
int main()
{
string s = "hello, world";
for (auto &c : s)
{
if (isalpha(c))
c = 'x';
}
cout << s << endl;
return 0;
}
上面这段代码将字符串中的字母转换为‘x'。变量c是引用类型。这种范围for循环的循环体内不能有改编遍历对象大小的语句
八:vetor容器
vector<string> s = {"hello", "hi", "nihaoa"};
vector<string> s1(10, "hi");
对s使用的是列表初始化,对s2使用的是构造vector对象。前者是花括号,后者是圆括号。
s1.push_back("i am coming");
运用push_back函数向容器添加元素
vector容器也可以比较大小,根据字典序方式比较。但是如果元素本身不可以比较大小,那么vector容器不能比较大小
九:迭代器
所有的标准库容器都可以使用迭代器,但是只有少数几种能同时支持下标索引。容器可以返回迭代器成员,例如begin()返回第一个元素迭代器,end()返回的是最后一个元素的下一个位置的迭代器,常被称为“尾后迭代器”。若容器为空,则begin()和end()返回同一个迭代器。还有一对函数,cbegin()和cend(),返回的是begin()和end()的const版本,即只能通过迭代器读元素而不能写元素。
在上面的代码中,注意以下几点。第一:若要访问元素,则和指针的用法类似,通过解引用符号。第二,迭代器访问下一个元素用自增运算符 。 比较迭代器和尾后迭代器用“!=”而不是"<"。
但凡使用了迭代器的循环体,都不要向迭代器指向的容器添加元素。
vector迭代器的特殊功能如下图
十:数组
int *a[10];
int *(&array)[10] = a;
在上面的代码中,a是一个含有十个元素的数组,数组元素是int指针。array是一个引用,引用了一个含有十个元素的数组,数组元素是int指针。
#include <iostream>
#include<vector>
#include <string>
#include <cctype>
using namespace std;
int main()
{
int a[10] = { 1,2,3,4,5 };
for (auto i: a)
{
cout << i << " ";
}
cout << endl;
while (1) {}
return 0;
}
在上面的代码中,会输出十个元素,分别是1,2,3,4,5,0,0,0,0,0.因为定义时候规定了数组维度是十,所以使用范围for循环会输出十个元素。
int i = 9;
int *p = &i;
int ai[] = { 1,2,3,4,5 };
auto ai2(ai);
decltype(ai) ai3 = { 3,4,5,6,7 };
ai2 = p;
ai3 = p;
在上面的代码中,ai2的类型是int*类型,所以ai2 = p是正确的,而ai3是int [5]类型,是不能赋值的,所以ai3 = p是错误的。
#include <iostream>
#include<iterator>
using namespace std;
int main()
{
int a[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
auto abegin = begin(a);
auto aend = end(a);
int sum = 0;
for (auto p = abegin; p != aend; p++)
{
sum += *p;
}
cout << sum << endl;
return 0;
}
在上面的代码中,输出是120。用到了c++11标准中新加的两个函数begin()和end(),在头文件iterator中定义,可以返回数组的首元素和最后一个元素的下一个元素。
在上图中,指针p也可以用下标访问数组元素。其实下标访问和解引用访问是一样的。p[1]就是*(p+1)。还可以看出,可以用负号作为数组的访问下标索引值,但是vector和string的下标索引只能是正数。
十一:c风格字符串
c语言风格的字符串就是字符数组,以‘'\0'结束那种。上面的变量p,p1,p2都是c风格的字符串。
char s1[] = "hello,";
char s2[] = "my dear";
char s3[20];
strcat_s(s1, s2);
strcpy_s(s3, s1);
cout << s3 << endl;
上面这段代码运行时侯会出错,因为s1的大小不能存放s1和s2连接后的字符串。所以使用c风格的字符串不如使用string安全高效。
string s = "hello, my dear";
const char *ch = s.c_str();
s = "hello, my another dear";
cout << ch << endl;
上面这段代码将string类型的字符串转化为c风格的字符串。注意最后的输出是不确定的字符串,s更改,ch也就变成了无效字符串了。现在的c++应该尽量使用vector和迭代器代替数组和指针,使用string代替c风格的字符串。
十二:多维数组
int a[2][3];
int cnt = 0;
for(auto &row:a)
for (auto &col : row)
{
col = cnt++;
}
for (auto &row : a)
{
for (auto &col : row)
{
cout << col << " ";
}
cout << endl;
}
在上面的代码中,通过范围for循环访问二维数组。这里要注意除了最内层循环之外,其余都要用引用类型,因为不用引用编译器会把数组a转换为整数指针赋值给row,这样row就是int*类型的,那么内层循环就不合法了。对于这段代码的输出循环,还可以用指针的方式输出,如下代码所示
for (auto row = a; row < a + 2; row++)
{
for (auto col = *row; col < *row + 3; col++)
{
cout << *col << " ";
}
cout << endl;
}
注意内层循环的解引用符号。row的类型是 int (*row)[3], col的类型是int* col,可以看出auto的使用非常方便啊。还记得iterator头文件中定义的begin()和end()函数吗?用这两个函数循环输出也很简洁。代码如下所示
for (auto row = begin(a); row != end(a); row++)
{
for (auto col = begin(*row); col < end(*row); col++)
{
cout << *col << " ";
}
cout << endl;
}