字符串、向量和数组
一、命名空间的using声明
std::cin
表示从标准输入中读取内容,其中使用了作用域操作符::的含义是:编译器应从操作符左侧名字所示的作用域中寻找右侧的那个名字。
但是该方法比较繁琐,可以利用using 声明的方法,在函数外将其进行声明。例如:using std::cin
即可在后续程序中直接使用cin。因此每个名字都需要独立的using声明。
二、标准库类型string
string表示可变长的字符序列。
-
定义与初始化string对象:
string s1; string s2=s1; string s3="hiya"; string s4(10,'c');
-
string对象上的操作:
- 读写string对象:读string对象是会自动忽略开头空白(空格符、换行符、制表符等),并且从第一个真正的字符开始读起,直至下一个空白。
- 读取未知数量的string对象:如下程序所示
int main()
{
string word;
while(cin>>word)
cout<<word<<endl;
return 0;
}
- 使用getline读取一整行:getline函数的参数是一个输入流和一个string对象,从给定的输入流中读取内容直至换行符(并且把换行符也读入,但不会存入string对象中)。
- string的empty和size操作:empty是string对象的成员函数,用于判断对象是否为空。size函数返回string对象的长度(即字符数)。
- string::size_type类型:string对象的size成员函数返回的类型为string::size_type类型
- 比较string对象:string类定义了几种用于比较字符串的运算符,对大小写敏感。 包括(==,!=,<,<=,>,>=)。比较规则为:(1)如果两个string对象的长度不同,且比较短的string对象的每个字符都与较长的string对象对应位置上的字符相同,就说明较短的string对象小于较长的string对象;(2)如果两个string对象在某些位置上不一致,则string对象比较的第一对相异字符的比较结果。
- 两个string对象相加:可以使用相加运算符(+)进行相加,还有复合赋值运算符(+=)。
- 字面值和string相加:需保证每次相加都是string对象与字面值相加。
-
处理string对象中的字符
面对单独处理string对象中的字符问题,比如检查一个string对象是否有空白,或者字母大小写改变的问题等。这时可以使用cctype头文件中的一些标准函数来解决。
- 对了每个字符,可以使用基于范围的for语句。范围for(range for):
for (declaration : expression)//expression部分是一个对象,表示一个序列。而declaration定义一个变量,用于访问序列的基础元素,每次迭代,declaration都变为下一个值
statement
例如统计string对象中的符号个数:
int main()
{
string str("i like c++!");
decltype(str.size()) punct_cnt=0;
for (auto c : str)
if (ispunct(c))
++punct_cnt;
cout << punct_cnt <<
" punctuation characters in " << str << endl;
return 0;
}
三、标准库类型vector
- 定义和初始化vector对象
vector也被称作容器,用于存放多个相同类型的对象。
C++中既有类模板,也有函数模板。模板本身不是类或函数,可以看作是编译器所生成的类或函数的一种说明,编译器根据模板创建类或函数的过程称为实例化。
对于类模板,需要提供一些额外的信息来指定模板到底实例化成什么类
- 使用花括号与圆括号进行初始化的区别:圆括号提供的值是用来构造vector对象,表示对象的容量和元素的初值;花括号表示列表初始化vector对象。例如
vector<int> v1(10); //v1有10个元素,初值为0
vector<int> v2{10}; //v2有1个元素,为10
vector<int> v3(10,1); //v3有10个元素,初值为1
vector<int> v4{10,1} //v4有2个元素,为10和1
-
向vector对象中添加元素
vector对象直接初始化有三种情况,第一种是初始值已知且数量较少,可以直接用序列初始化;第二种是另一个vector对象的副本;第三种也是最常见的一种情况是不清楚实际所需的元素个数及具体值,这时可以用vector的成员函数push_back向其尾端添加元素。
-
vector操作
四、迭代器
迭代器可以实现对string对象中的字符和vector对象中的元素或其余容器类型的访问。同时也提供了对对象的间接访问。
- 使用迭代器
有迭代器的类型都拥有返回迭代器的成员,比如begin和end成员,其中begin成员负责指向返回的第一个元素,而end成员负责返回指向容器的“尾元素的下一位置”,被称为尾后迭代器。
- 迭代器运算符
-
将迭代器从一个元素移动到另外一个元素
递增运算符(++),可以使迭代器从一个元素移动到下一个元素,是将迭代器向前移动一个位置。
-
迭代器类型
拥有迭代器的标准库类型使用iterator和const_iterator来表示迭代器类型。
-
begin和end运算符
begin和end返回的具体类型是由对象是否为常量所决定的,如果对象是向量,则返回const_iserator类型的迭代器。如果需要固定返回常量迭代器,可以使用cbegin和cend。
-
结合解引用和成员访问操作
如果迭代器所指的对象恰好是类,那么可进一步访问它的成员,例如
(it*).empty()
。
其中圆括号必不可少,表示是对迭代器it先解引用后再执行点运算。为了简化上述表达式,可以采用箭头运算符(->),例如it->empty()
。
- 迭代器运算
利用三个迭代器实现二分法
int main()
{
int temp,sought;
vector<int> data{1,2,3,4,5,6,7,8,9}; //data必须是有序的
cin >> sought;
auto beg = data.cbegin(), end = data.cend();
auto mid = data.cbegin() + (end-beg) / 2;
while (mid != end && *mid != sought)
{
if (sought < *mid)
end = mid;
else
beg = mid + 1;
mid = beg + (end - beg) / 2;
}
if (mid == end)
return -1;
cout << *mid << endl;
return 0;
}
五、数组
数组类似与vector,但是数组在定义时需要确定大小,相比与vector其运行性能可能会更好,但是灵活性下降了。
- 数组的定义与初始化
数组的声明如type a[d]的形式,其中a是数组的名字,d为数组的维度,必须为常量表达式。
初始化数组时,如果采用列表初始化的方法,可以忽略数组维度。但是如果指明了数组的维度,那么列表中初始值的总数量不应超出数组指定的大小,否则多出的初始值将被忽略。
不能将数组的内容拷贝给其他数组作为初值,也不能给其他数组赋值。
- 访问数组元素
数组的元素可以用下标运算符进行访问,索引从0开始,数组下标的类型时size_t类型。
- 指针和数组
数组与指针有非常紧密的联系。通常情况下,使用取地址符可以获取指向某个对象的指针。对数组的元素使用取地址符就能得到指向该元素的指针。编译器会自动将数组名字替换为一个指向数组首元素的指针。因此数组的操作在一些情况下实际上是指针的操作。
数组虽然不是容器,但是也引入了begin和end函数,可以获取指向数组首元素和尾元素的下一个位置的指针。这两个函数定义在iterator头文件中。
int ia[]={0,1,2,3,4,5,6,7,8,9};
int *beg=begin(ia);
int *last=end(ia); //尾元素的下一个位置的指针。