string对象
使用string对象之前,要在代码头部加上#include <string> 和using namespace std::string;
(使用命名空间中的名字之前应该用using 声明引入该名字,但是注意using 声明不要放在头文件中,以避免使用了该头文件的文件中会产生名字冲突)
string对象的初始化
空string对象 string s1;
用字符串字面值初始化 string s2="hello";或者string s2("hello");
指定数量和初始值 string s3(10,'c');
拷贝构造函数 string s4(s3);
用拷贝赋值运算符string s5=s4;
读string对象
string对象可以用iostring对象来读写。注意用标准输入读的时候,string对象会自动忽略开头的空白,从第一个真正的字符开始读起,遇到空白终止。
string s;cin>>s;
输入 hello world,则s得到hello
用getline可以保留输入时的空白符。getline从输入流读入内容,直到遇到换行符为止。注意得到的string对象不包含换行符。
string对象size()函数的返回值
是string::size_type类型(这个类型是在类string中定义的),这是一个无符号的值。因此在含此类型的表达式中不要使用int.(此时int会转换为unsigned int,可能会带来非预期想要的运算结果)
string对象的比较
简单说来,是两个对象第一对相异字符的比较结果。采用ascii码大小比较,大小写敏感。因此对于string s1("hello"),s2("hF");有s1>s2
string对象相加
string对象重载了+运算符,因为标准库允许把字符字面值和字符串字面值转换成string对象,所以string s1="hello”;string s2=s1+","+'u';得到string对象s2是hello,u.注意加法运算符的两侧的运算对象至少有一个是string对象.因此string s2="hello"+','是不正确的。字符串字面值没有加用的加法运算符。
C++头文件与C头文件
C中的头文件name.h,在C++中被命名为cname.也就是说二者内容是一样的。区别是cname的头文件中定义的名字属于命名空间std,而name.h中的名字则不是。
访问string对象中的字符
用C++11新规则遍历读取:用如下方法
string str("some string");
for(auto c:str){cout<<c<<endl;}这有点像javascript,php等语言中的写法,很简洁方便
用C++11新规则遍历写,要将写循环变量定义成引用类型。用如下方法
string str("some string");
for(auto &c:str){
c=toupper(c);
}
用下标运算符[]
for(decltype(s.size()) index=0;index<s.size();++index){...}
[]的参数是string::size_type类型的值,其返回值是该位置上字符的引用。因此,既可用来读,也可作为左值,修改该位置的字符。(当然此时string对象不能是const对象。)
用下标运算符[]可实现很方便的随机访问
注意,用下标运算符[],应保证其合法性。首先下标要定义成string::size_type类型(这样可以确保其大于或等于0),其次要检查下标是否小于string对象的长度。
vector对象
使用vector对象前,需要添加头文件和using声明
#include <vector>
using std::vector;这与使用string对象类似
vector是一个类模板,使用它时,要提供vector存放对象的类型信息,以实例化为类。
vector对象的初始化
默认初始化 vector<T> v1;
指定大小和元素初始值 vector<T> v2(n,val);
只指定大小 vector<T> v3(n);
列表初始化 vector<T> v4{a,b,c,d};或者vector<T> v4={a,b,c,d};C++11新增方法,vs2010测试不通过
拷贝构造函数 vector<T> v5(v4);
拷贝赋值运算符 vector<T> v6=v5;
C++的几种初始化方法
直接初始化int i(0);
拷贝初始化int i=0;
列表初始化int i={0};或者int i{0};
大多数情况下,这些初始化方式可以相互等价地使用,但是会有例外,如下
1.提供多个初始值时,不能用拷贝初始化string s1(10,'c');无法用拷贝初始化方式写出等价的定义
2.如果提供类内初始值,则不能用直接初始化
struct Sales_data{
std::string bookNo;
unsigned units_sold=0;
double revenue=0.0;
};这是类内初始化,初始值也可放在{}内,但是不能用()
3.如果提供的是初始元素值,只能用列表初始化
vector<string> v1{"a","an","the"};
4.不能用拷贝初始化实现只指定元素个数
不能将vector<int> ivec(10);写成vector<int> ivec=10;
对于vector<int>,也不能用列表初始化实现只指定元素个数。
对于列表初始化中,初始化过程会尽量将其当成 是元素的初始值列表。若不能执行列表初始化,则会考虑用其中的值来构造vector对象。
vector<string> v1{"hello","hi"};被当作列表初始化
vector<string> v2{10,"hi"};被等价于vector<string> v2(10,"hi");
构造vector对象,不能使用拷贝初始化。只能用直接初始化或列表初始化。而列表初始化常被用来为vector内的元素提供初始值。当花括号内的元素不能用来实施列表初始化时,它会转为直接初始化。例,上面的vector<string> v2{10,"hi"};被等价于vector<string> v2(10,"hi");
vector对象初始化方式的选择
初始个数已知,用vector<T> v1(n);
初始个数已知,且元素初始值都一样,用vector<T> v2(n,value);
初始值是另一个vector对象的副本,用vector<T> v3(v2);
最常用且最高效的是,使用默认初始化函数,定义一个空vector对象,运行时再向其中添加具体值
范围for循环体内不应该改变其所遍历序列的大小。
或者说,如果循环体内包含向容器添加元素的操作,那么,不能使用范围for循环。(for(auto i:v){..})因为范围for循环预存了迭代器end()的值。一旦在序列中添加或删除了元素,end函数的值可能变得无效了。
vector对象的遍历和随机访问
与string对象类似,用for循环和下标运算符。不同的是,vector对象的下标类型是vector<T>::size_type
向vector对象添加元素
用push_back,千万不能用下标形式添加。下标只能用于操作已存在的元素。记住,vector对象和string对象的下标运算符只能用于访问已存在的元素。
迭代器
string和标准库容器都 支持迭代器。所以迭代器是一种通用的访问string或者容器对象的元素的方法。
容器都有成员begin()和end().分别用来返回指向第一个元素的迭代器,和指向尾元素的下一个位置的迭代器。
通过解引用来获取它指向的元素时,一定要保证迭代器合法且确实指向着某个元素,因此要先保证容器(或string对象)不为空,用v.begin()!=v.end()判断。
用迭代器实现遍历容器或string对象
for(auto it=v.begin();it!=v.end();++it){cout<<*it<<endl;...}用;it!=v.end()判断是否已经遍历,而不用<运算符是因为所有标准窗口的迭代器都实现了==和!=,大多数都没有定义<运算符。这也是泛型编程的体现。
迭代器类型
有iterator和和const_iterator两种,分别表示一般迭代器和指向常量的迭代器。后者指向的元素值不能被修改。
const string对象或者容器对象只能用const_iterator
迭代器失效
任何一种改变容器容量的操作,会使得迭代器失效。因此,但凡使用了迭代器的循环体,都不要向迭代器所属的容器添加元素
迭代器的算术运算
string和vector对象的迭代器支持多种运算,如加减、比较运算。支持it+n,it-n,it1-it2,it1<it2等,其中n是整数。it1-it2的类型是difference_type类型的带符号整型数