3.1 命名空间的using声明
1、每个名字都需要独立的using声明,头文件中不应包含using声明。
using namespace::name;
3.2 标准库类型string
string:可变长的字符序列
(1)string对象可采用默认初始化方式得到一个空的字符串;也可通过字符串字面值初始化,string对象中包含除了字面值的空字符外的所有字符;或提供一个数字和一个字符进行初始化,则string对象内容给定字符的重复序列。
in
//标准库类型,对其定义初始化,操作
string s1; //默认初始化,s1是一个空串
string s2(s1);//s2是s1的副本,直接初始化
string s2 = s1;//等价于上述s2(s1),拷贝初始化
string s3("value"); // s3是字面值value的副本
string s3 = "value";
string s4(10, 'c');//把s4初始化为由连续n个字符C组成的串,拷贝初始化
(2)string读写操作同样采用IO操作符:cin和cout;在输出操作中自动忽略开头空白,输出字符后遇空白结束;读取未知数量的string对象可采用while循环。
(3)getline(cin, line); 保留输入的空白符,读取以遇到换行符为止(读取了换行符但是不保存)。
string line; //定义空串
while (getline(cin, line)){ //以getline作为条件逐行读取字符串赋值给line,遇到换行符停止,赋值时丢弃换行符
if (!line.empty() && line.size() > 10) //如果读取的行不为空且长度大于10则输出
cout << line << endl; //逐行输出
}
(4)empty函数 根据string对象是否为空返回布尔值类型,使用点操作符指明对象。size函数返回string对象长度(string::size_type类型 ,无符号值),同样使用点操作符指明对象。
(5)使用关系运算符(==、!=、<、<=、>、>=)检验两个string对象,string对象比较过程中对大小写字母敏感,若两个string对象在某些位置不一致,最终比较结果是第一对相异字符比较的结果;若对应每个位置字符相同,则较短的小于较长的。
(6)使用 ‘+’ 将两个string对象相加即串接起来,可将字面值与string对象相加,但是需要确保加法运算符两侧的对象至少有一个是string对象。
(7)cctype头文件中定义了一系列函数处理string中单个字符,函数详见C++primer第五版82页。
(8)范围for语句 遍历序列中每个元素;若想该笔那string对象中字符的值,需将循环变量定义为引用类型 。
for (declaration : expression)
statement
//string s("Hello World !!!");
//decltype(s.size()) punc_cnt = 0;
//for (auto c : s){
// if (ispunct(c))
// ++punc_cnt;
//}
//cout << punc_cnt << " punctuation characters in " << s << endl;
//string s("Hello,World!!!");
//for (auto &c : s) //处理s中的每一个字符
// c = toupper(c); //c作为引用,每次对于c的处理将改变s中的字符
//cout << s << endl;
(9)使用下标完成对string对象部分字符的处理,**下标运算符[ ]**接收的参数为string::size_type类型,该参数表示要访问字符的位置,返回该位置字符的引用。
//string s("some strings");//定义一个已经赋初值的字符串
if (!s.empty())
s[0] = toupper(s[0]);
定义一个index类型为string::size_type ,初始值为0
若下标不超出字符串长度,且该字符不为空对其进行处理
//for (decltype(s.size()) index = 0; index != s.size() && !isspace(s[index]); ++index)
// s[index] = toupper(s[index]); //将当前字符改写为大写形式
//cout << s << endl;
//把0-15的十进制数改写为十六进制
//const string hex = "0123456789ABCDEF"; //常亮字符串对象保存0-15的十六进制数字
//cout << "Enter a series of number between 0 and 15"
// << " separated by spaces, Hit ENTER when finished; "
// << endl;
//string result; //空串,存放最终转换结果值
//string::size_type n; //保存读取的十进制数
//while (cin >> n) //确保有读取
// if (n < hex.size()) //确保N在0-15之间
// result += hex[n];//得到对应的十六进制数
//cout << result << endl; //输出最终的转换结果
3.3 标准库类型vector
vector是一个类模板,表示对象的集合,其中所有对象类型相同,且集合中每个对象都有与之对应的索引便于访问。引用不是对象所以不存在包含引用的vector。
(1) vector对象有多种初始化方法:默认初始化 创建一个指定类型的空vector对象;列表初始化方法 用花括号括起来初始元素赋给vector对象;创建指定数量的元素 包含元素数量和统一初始值;值初始化 只提供元素数量,库会创建值初始化的元素初值。
vector<string> articles = { "a", "an", "the" }; //列表初始化vector对象,包含三个元素
//vector<string> art{ "a", "an", "the" }; //列表初始化,与上一条语句等价,不可使用圆括号初始化
//vector<string> vec(10, "hi!"); //10个string类型的元素,值都为hi!,创建指定数量的元素
//vector<int> ivecc(10, -1);//10个int类型对象
//vector<int> m(10); //10个元素初始化为0,值初始化
//vector<string> svecc(10);//10个string元素,都为空串
//vector<vector<int>> ivec;
vector<string> svec = ivec;
//vector<string> svec(10, "null");
//cout << svec[3] << endl;
(2)初始化时若用花括号一般均为列表初始化,在无法执行列表初始化时(花括号中的值与元素类型不同)会考虑其他初始化方式。
(3)使用函数push_back向vector对象中添加元素,压入对象尾端,不能使用下标形式添加元素 ,可用于访问已存在的元素。
(4)如果循环体内包含有向vector对象添加元素的语句,不可使用范围for循环。
(5)vector对象的比较方法与string对象相同;empty函数与size函数同样与string的同名函数功能一致;下标类型是由vector定义的size_type类型;
vector<int> num; //定义一个vector对象int类型空
int m; //定义int类型存储读取的整数
while (cin >> m) //进行读取直至到文件结尾
num.push_back(m); //将读取的整数依次添加至vector尾部
for (auto c : num) //依次遍历输出
cout << c << endl;
3.4 迭代器介绍
所有的标准库容器都可使用迭代器,但只有少数几种才同时支持下标运算。迭代器类似于指针类型也提供对对象的间接访问,迭代器分为有效和无效,有效的迭代器指向某个元素或尾元素的下一位置,其他情况均为无效。
(1)begin和end时返回迭代器的成员,begin成员返回指向第一个元素的迭代器,end成员负责返回指向容器尾元素下一位置的迭代器也称为尾后迭代器。
(2)迭代器的部分运算
*iter | 返回得带起iter所指元素的引用 |
---|---|
iter->mem | 解引用iter并获取该元素名为mem的成员,等价(*iter).mem |
++iter | 令iter指示容器中的下一个元素 |
–iter | 令iter指示容器中的上一个元素 |
(3)大多标准看库容器的迭代器都定义了==和!=,但是大多数都未定义<运算符。
(4)迭代器分为iterator和const_iterator类型,第二种即为常量类型只能读取不可修改所指元素值。
(5)解引用迭代器可获得迭代器所指对象的值,若想检查元素是否为空,可采用 (*it).empty(),其中圆括号必不可少,可使用箭头运算符(->)简化上述操作 it->empty()。
(6)在范围for循环向vector添加元素或者改变vector对象容量的操作,会使vector对象的迭代器失效。
(7)vector和string迭代器支持的运算包括,+,-,+=,-=,>, >=, <, <=。 参与比较的迭代器必须合法且指向同一容器的元素;两个迭代器相减得到这两个迭代器间的距离,类型是difference_type的带符号整数。
3.5 数组
数组也是存放类型相同的对象的容器,与vector不同的是数组大小确定不变,不能随意向数组中增加元素。
(1)数组维度必须是一个常量表达式,默认情况下数字元素被默认初始化;定义数组必须指定数组的类型不可使用auto关键字;同样不存在引用的数组。
(2)可对数组元素进行列表初始化,若数组维度大于给定初始值数量,则初始化靠前元素,剩下元素默认初始化;可使用字符串字面值初始化字符数组,结尾后的空字符也会被拷贝进字符数组;数组间不允许相互拷贝和赋值。允许定义数组的指针和引用。(理解数组最好由内向外阅读)
数组初始化
//const unsigned sz = 3;//数组维度必须时常量表达式
//int ia1[sz] = { 1, 2, 3 }; //数组维度为3
//int a2[] = { 1, 2, 3 }; //若未指定维度,编译器根据初始值数量计算并推测
//int a3[5] = { 1, 2, 3 }; //如果维度比初始值数量大,用初始值初始化靠前的,其他默认初始化{1,2,3,0,0}
int a5[2] {1, 2, 3}; //错误,初始值不可比指定的维度大
字符数组
//char a1[] = { 'C', '+', '+' }; //列表初始化,没有空字符
//char a2[] = { 'C', '+', '+', '\0' };//含有显示的空字符
//char a3[] = "C++";//维度为4,自动添加表示字符串结束的空字符串
int arr[10];
int *ptrs[10]; //ptrs是含有10个整型指针的数组
int (*parray)[10] = &arr; //parray指向一个含有10个整数的数组
int(&arrref)[10] = arr; //arrref引用一个含有10个整数的数组
int *(&arry)[10] = ptrs; //arry是数组的引用,该数组含有10个指针
(3)数组元素同样使用范围for 语句或下标进行访问,下标类型为
size_t类型,在cstddef头文件中定义。
(4)在很多用到数组名字的地方,编译器自动将其替换为一个指向数组首元素的指针。同时当使用数组作为一个auto变量的初始值时,得到的类型是指针而不是数组。使用decltype关键字不会发生上述转变。
(5)指针也可作为迭代器,通过递增运算符和递减运算符改变指针所指位置,同时可解引用该结果指针;也可使用指针遍历数组中元素,通过标准库函数begin和end获取指向数组第一个元素和尾元素下一位置的指针。不过begin和end是将数组作为它们的参数。也可将两指针相减得到它们之间的距离(difference_t类型)。
(6)对数组进行下标操作实际上是对指向数组元素的指针进行下标运算;标准库类型限定使用的下标必须为无符号类型,内置的而下表运算符无此要求。
(7)C风格字符串是表达和使用字符串形成的约定俗成的写法,若放在字符数组中需要以空字符作为结尾。可通过下列函数对C语言字符串进行操作:
strlen§ | 返回p的长度,空字符不计算在内 |
---|---|
strcmp(p1,p2) | 比较p1和p2的相等性,等于返回0,大于返回正值,小于返回负值 |
strcat(p1,p2) | 将p2附加到p1之后,返回p1 |
strcpy(p1,p2) | 将p2拷贝给p1,返回p1 |
因为使用数组时其实真正用的是指向数组首元素的指针,所以不能直接对其进行操作,需调用上述函数,传入上述这些函数的指针必须指向以空字符作为结束的数组。
//数组比较
//const char a3[] = "This apple ia very sweet";
//const char a4[] = "I have a very sweet apple";
//if (strcmp(a3, a4) == 0)
// cout << "a3 和a4相等" << endl;
//else if (strcmp(a3, a4) < 0)
// cout << "a3小于a4" << endl;
//else
// cout << "a3大于a4" << endl;
//练习3.40
char a5[] = "hi,sir,are you ok?";
char a6[] = "i am fine, thank you.";
char a7[100];
strcpy_s(a7, a5);
//strcpy_s(a7, a6);
//strcat_s(a7, " ");
//strcat_s(a7, a5);
strcat_s(a7, " ");
strcat_s(a7, a6);
cout << a7 << endl;
(8)在string对象中可使用字面值对其进行初始化和将字面值与string串接,而对于任何地方出现的字符串字面值都可以用以空字符串字符数组来代替。
即首先允许使用空字符结束的字符数组初始化string对象或为其赋值;其次在string对象的加法运算中允许使用以空字符串结束的字符数组作为其中一个运算对象,且在string对象的复合赋值运算中允许使用以空字符结束的字符数组作为右侧运算对象。
(9)第八条中的性质反过来即不成立,无法用string对象来代替C风格字符串,需要使用c_str函数对string对象进行操作,返回一个指针,该指针指向以空字符结束的字符数组。
string s("Hello World");
const char *str = s.c_str();
(10)可通过数组初始化vector对象,指明要拷贝区域的首元素地址和尾后地址即可。也可用数组的一部分进行初始化vector对象。
int a8[] = { 0, 3, 4, 5, 6, 7, 8 };
vector<int> ivec(begin(a8), end(a8));
for (auto i : ivec)
cout << i << endl;
3.6 多维数组
(1)多维数组可以看作为数组的数组,通过两个维度定义多维数组,一个维度表示数组本身大小,一个表示其元素大小。允许使用花括号括起来的一组值初始化多维数组。
(2)使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型,否则数组将会被自动转换为指针。
(3)程序使用多为数组的名字是,也会自动将其转换成指向数组首元素的指针。
(4)可使用using或typedef获得类型别名简化多维数组的指针。
//多维数组
int main(){
int a[3][5];
int st = 0;
for ( auto &i:a)
for (auto &j : i){
j = st;
++st;
}
//for (auto &i : a)
// for (auto j : i)
// cout << j << endl;
//练习3.43,范围for
//for (int(&i)[5] : a)
// for (int &j : i)
// cout << j << endl;
//using ff = int[5];
//typedef int ff[5];
//for (ff &i : a)
// for (int &j : i)
// cout << j << endl;
//for (size_t i = 0; i != 3; ++i)
// for (size_t j = 0; j != 5; ++j)
// cout << a[i][j] << endl;
//using si = size_t;
typedef size_t si;
for (si i = 0; i != 3; ++i)
for (si j = 0; j != 5; ++j)
cout << a[i][j] << endl;
//for (int(*p)[5] = a; p != a + 3; ++p)
// for (int *q = *p; q != *p + 5; ++q)
// cout << *q << endl;
//using ff = int[5];
//typedef int ff[5];
//for (ff *p = a; p != a + 3; ++p)
// for (int *q = *p; q != *p + 5; ++q)
// cout << *q << endl;
system("pause");
return 0;
}