三、字符串、向量和数组
3.1命名空间的using声明
1.std::cin表示从标准库中读取内容,可以通过using声明,using std::cin;
2.每个名字都需要独立的using声明;
3.头文件一般不适用using声明,以免使用的文件都包含一些名字;
3.2标准库类型string
可变长的字符序列
3.2.1定义和初始化
1.string s4(n,'c'); //定义s为n个字符c组成的串。
直接初始化() 和 拷贝初始化=,多个对象时需要直接初始化;
2.IO操作指令,从第一个真正字符开始,到下一处空白结束(无空白符号);多IO操作可以连着一起;
3.getline ,输入流读取,直到换行符;
getline(cin , stringname)
对于string类的输入函数,它会自动忽略开头的空白(空格、制表符、换行等等),从第一个真正的字符开始直到下一个空白。
对于getline()函数,它会保存字符串中的空白符,它读入数据,直到遇到换行符位置。
4.只输出非空行
string line;
while (getline(cin, line))
if (!line.empty())
cout << line << endl;
5.string对象的比较
1.如果长度不同,每个字符都一样,则短的小;
2.如果对象的某些对应位置不一致,则比较第一对相异字符比较的结果;
6.string对象可以与(字符字面值或者字符串字面值)相加;
字符串字面值与string是不同的类型!!0
以下部分csdn没自动保存,
读写string对象
在执行读取操作时,string
对象会自动忽略开头的空白(空格符、换行符、制表符等)并从第一个真正的字符开始读取,直到遇见下一处空白为止。
使用getline读取一整行
当希望能在输出的字符串中 保留输入的空白字符 时,可以使用 getline
函数。
string 的empty和size操作
empty 函数根据 string 对象是否为空返问一个对应的布尔值;
size 函数返回 string 对象的长度(即 string 对象中字符的个数)。
string::size_type类型
size函数返回值其实是 string::size_type 类型,这是一种无符号类型,而且能足够存放下任何string对象的大小。因此,所有用于存放string类的size函数返回值的变量,都应该是string::size_type类型。
处理每个字符?使用基于范围的for语句
如果想对string对象中的每个字符都做点什么操作,目前最好的办法是使用C++11提供的范围 for(range for)语句,可以遍历给定序列中的每个元素并对序列中的每个值执行某种操作。语法形式:
for (declaration : expression)
statement
其中,expression 部分是一个对象,用于表示一个序列。declaration 部分负责定义一个变量,该变量被用于访问序列中的基础元素。每次迭代,declaration 部分的变量都会被初始化为 expression 部分的下一个元素值。
3.3 标准库类型vector
标准库类型 vector 表示对象的集合,因为vector 容纳着其他对象,所以也叫做 容器(container),定义在头文件 vector 中。vector 中所有对象的类型都相同,集合中的每个对象都有一个与之对应的索引, 并用于访问该对象。
C++语言既有类模板(class template),也有函数模板。
vector 是一个 类模板,模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为 实例化(instantiation),当使用模板时,需要指出编译器应把类或函数实例化成何种类型。
对于类模板,需要通过提供一些额外信息来指定模板到底实例化成什么样的类,需要提供哪些信息由模板决定。提供信息的方式:在模板名字后面跟一对尖括号,在括号内放上信息。
3.3.2 向vector对象中添加元素
先创建一个空vector,然后在运行时再利用vector的成员函数push_back向其中添加元素。push_back负责把一个值当成vector对象的尾元素 “压到(push)” vector对象的 “尾端(back)” 。
警告:
vector
对象(以及string
对象)的下标运算符可用于访问已存在的元素,而不能用于添加元素。
3.4迭代器的选择
所有标准库容器都可以使用迭代器,但其中少数可以支持下标运算符。
实现对对象的间接访问,不是通过取地址,拥有返回迭代器的成员。
1.不清楚迭代器准确的类型是什么,通过auto定义。
2.可以通过解引用获取指向的元素。
例:实现将单词改为大写
for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
*it = toupper(*it);
3.实际上迭代器的类型有iterator和const_iterator,对象读取而无需写操作,最好采用const_iterator,C++11引入cend和cbegin,返回值都是const_iterator。
解引用和成员访问操作结合
(*it).empty和it->empty作用一致
上上上句可以改为下句:其中对s的元素只是读取没有操作,则用了cbegin和cend
for (auto it = s.cbegin(); it != s.cend() && !s.empty(); ++it)
3.4.2迭代器运算
与整数运算,返回值是移动后的迭代器;迭代器差值是距离;比较符是比较指向前后;
没有定义两个迭代器之间的+运算符;
迭代器用于二分搜索
auto beg = text.begin(), end = text.end();
auto mid = beg + (end - beg) / 2;
for (mid != end && *mid != sought)
{
if (sought < *mid)
end = mid;
else
beg = mid+1;
mid = beg + (end - beg) / 2;//新的中间点
}
3.5数组
#include<cstddef>
1.存放类型相同的对象的容器,通过位置访问;与vector不同的是数组的大小确定,维度必须是常量表达式
unsigned cnt=42;string bad [cnt];//错误,其中cnt不是常量表达式,应该使用 constexpr unsigned 命名
2.不允许用auto根据初始值列表指定类型,不存在引用的数组;
3.字符数组的字面值初始化,注意结尾处有一个空字符
char a[ ]={"c",'+','+','\0'};
char a[ ]="C++";//自动添加表示字符串结束的空字符,注意字符串大小是4
4.允许定义
指针数组:int *ptrs[10];
数组的指针 : int (*Parray) [10]=&arr;// 数组而言,由内向外阅读比较好
数组的引用:int (&arrRef) [10] =arr;
例:int *(&arry) [10] =ptrs; //arry是一个数组的引用,该数组有10个指针。
3.5.2访问数组元素
1.数组下标为size_t类型
2.可以使用范围for遍历数组的所有元素:
unsigned scores [11] ={};
for(auto i : scores )
cout<<i<<" "<<endl;
练习3.32
#include<iostream>
#include <string>
#include<cstddef>
#include<vector>
using namespace std;
int main()
{
unsigned ai[10];
for (size_t i = 0; i != 10; i++)
ai[i] = i;
unsigned ai2[10];
for (size_t j = 0; j != 10; j++)
ai2[j] = ai[j];
vector<unsigned> iv(10);
for (auto iter = iv.begin(); iter != iv.end(); ++iter)
*iter = iter - iv.begin();
vector<unsigned> iv2{ iv };
for (auto k : iv2)
cout << k << " ";
cout<< endl;//循环外输入endl,避免每次都endl导致分行
return 0;
}
3.5.3指针和数组.
1.auto和decltype返回值推断的不同,指针和数组(p118)
2.指向数组的指针也是迭代器,有尾指针,指向尾元素的下一个位置的指针
C++11引入begin和end函数,在iterator头文件中:
int *beg =begin (ia);
int *last= end(ia);
3.迭代器相减类型为ptrdiff_t:auto n =end(arr) - begin(arr);
练习3.36,比较两个数组是否相等,比较两个vector是否相等
#include<iostream>
#include<cstddef>
#include<iterator>
using namespace std;
int main()
{
unsigned ia[10] = { 1,2,3 }, ia2[10] = {1,2,3};
bool compare=true;
if (sizeof(ia) != sizeof(ia2))
compare = false;
else
{
for (auto i = 0; i != sizeof(ia); i++)
{
if (ia[i] != ia2[i])
compare = false;
}
}
if (compare = false)
cout << "No" << endl;
else cout << "Yes" << endl;
return 0;
}
vector可以直接比较
#include<vector>
#include<iterator>
using namespace std;
int main()
{
vector<unsigned> vec1 = { 0,1,2 }, vec2 = { 0,1,2 };
if (vec1 == vec2)
cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
3.5.4使用标准库string要比C风格字符串更安全
3.5.5与旧代码的接口
允许用数组初始化vector:
int int_arr[ ]={ 0,1,2,3,4,5};
vector <int> ivec(begin(int_arr),end(int_arr));
用vector拷贝给数组
for (int* i = begin(arr); i != end(arr); i++)
*i = ivec[i - begin(arr)];
3.6多维数组
1.一个维度表示数组本身大小(称为行),一个维度表示其元素大小(称为列);
此处与C一致
2.用范围for语句遍历赋值
size_t cnt=0;
for (auto& row : ia)
for (auto& col : row)
col = cnt++;
上述中,必须控制变量row和col为引用,因为要改变元素的值;若为auto row则默认为指向数组首元素的指针,下一个步不能操作。
范围for处理多维数组,除了最内层,其他所有循环的控制变量都应该是引用类型!
3.多维数组的指针,指向第一个内层数组的指针;
C++11中通过auto和decltype避免数组前加上指针类型-----或者通过标准库中begin和end也可以
4.可以用类型别名实现,多维指针
using int_arry = int[4]; 或者 typedef int int_arry[4];//将类型“4个整数组成的数组”,命名为int_array,用类型名定义外循环的控制变量。