命名空间的using声明
目前用到的库函数基本上都属于命名空间std;通过using声明(using declaration)实现更简单的途径使用到命名空间中的成员。
标准库类型string
string表示可变长的字符序列,必须先包含string头文件(#include <string>);作为标准库的一部分,string定义在命名空间std中。
定义和初始化string对象:
string s1;//默认初始化,s1是一个空字符串
string s2(s1);//s2是s1的副本
string s2 = s1;//等价于上式
string s3 = "hiya";//s3是该字符串字面值的副本,除了字面值最后的那个空字符外
string s4(10, 'c');s4的内容是cccccccccc
直接初始化和拷贝初始化:如果使用等用(=)初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新建的对象中去;与之相反,如果不使用等号,则执行的是直接初始化。
string s5 = "hiya";//拷贝初始化
string s6("hiya");//直接初始化
string s7(10, 'c');//直接初始化,s7的内容是cccccccccc
string对象上的操作:
操作 | 解释 |
os<<s | 将s写到输出流os当中,返回os |
is>>s | 从is中读取字符串赋给s,字符串以空白分隔,返回is |
getline(is, s) | 从is中读取一行赋给s,返回is |
s.empty() | s为空返回true,否则返回false |
s.size() | 返回s中字符的个数 |
s[n] | 返回s中第n个字符的引用,位置n从0计起 |
s1+s2 | 返回s1和s2连接后的结果 |
s1=s2 | 用s2的副本代替s1中原来的字符 |
s1==s2 | 如果s1和s2中所含的字符完全一样,则它们相等,string对象的相等性判断对字母的大小写敏感 |
s1 != s2 | 不相等判断 |
<, <=, >, >= | 利用字符在字典中的顺序进行比较,且对字母的大小写敏感 |
读写string对象
c++中使用标准库中的iostream来读写int、double等内置类型的值,同样也可以使用IO操作符来读写string对象。
string对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到遇见下一处空白为止。
#include <iostream>
using namespace std;
int main()
{
string s;
cin >> s;
cout << s << endl;
return 0;
}
/*
如果程序的输入是“ Hello World! ”(注意开头和结尾处的空格),则输出将是“Hello”,输出结果没有任何空格。
*/
string对象多个输入或者多个输出可以连写在一起;如下面代码,输入是“ Hello World! ”,则输出是“HelloWorld!”。
#include <iostream>
using namespace std;
int main()
{
string s1, s2;
cin >> s1 >> s2;
cout << s1 << s2 << endl;
return 0;
}
//输入是“ Hello World! ”,则输出是“HelloWorld!”。
getline使用
想要在最终得到的字符串中保留输入时的空白符,应该用getline函数代替原来的>>运算符;getline函数的参数是一个输入流和一个string对象,函数从给定的输入流中读入内容,直到遇到换行符为止,此时换行符也被读进来;然后把所读的内容存入到那个string对象中去,此时不存换行符。
getline函数只要一遇到换行符就结束读取操作并返回结果,即使一开始输入就是换行符也是如此。
string的empty和size操作
empty函数 根据string对象是否为空返回一个对应的布尔值;empty是string的一个成员函数,使用点操作符指明是哪个对象执行了empty函数即可。
//每次读入一整行,遇到空行直接跳过
while(getline(cin, line))
if(!line.empty())
cout << line << endl;
size函数返回string对象的长度,即string对象中字符的个数。
size函数返回的是一个无符号整型数,因此在一条表达式中如果有了size函数就不要再使用int了,这样可以避免混用int和unsigned可能带来的问题。
比较string对象
相等性运算符(==和!=):分别检验两个string对象相等或不相等,意味着长度相同且所包含的字符也全相同。
关系运算符(<、<=、>、>=):分别检验一个string对象是否小于、小于等于、大于、大于等于另外一个string对象。
注意:
如果两个string对象的长度不同,而且较短string对象的每个字符都与较长string对象对应为止上的字符相同,则较短string对象小于较长string对象。
如果两个string对象在某些对应的位置上不一致,则string对象比较的结果其实是string对象中第一对相异字符的比较结果。
string对象相加
两个string对象相加:
两个string对象相加得到一个新的string对象,其内容是把左侧的运算对象与右侧运算对象串接起来。
字面值和string对象相加:
标准库允许把字符字面值和字符串字面值转换成string对象。
当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符(+)的两侧运算对象至少有一个是string。
string s1 = "hello";
string s2 = "world";
string s4 = s1 + ", ";//正确
string s5 = "hello" + ", "; //错误:两个运算对象都不是string
string s6 = s1 + ", " + "world";//正确
string s7 = "hello" + ", " + s2;//错误:不能把字面值直接相加
处理string对象中的字符
cctype头文件中定义了一组标准库函数来处理string对象中的字符。
处理string对象中的每个字符,使用基于范围的for语句。
string str("some string");//每行输出str中的一个字符
for(auto c : str)
cout << c << endl;//输出当前字符,后面紧跟一个换行符
想改变string对象中字符的值,必须把循环变量定义成引用类型。
string s("Hello World!!!");
//转换成大写形式
for(auto &c : s)
c = toupper(c);//c 是一个引用,因此赋值语句将改变s中字符得到值
cout << s << endl;
只处理string对象中一部分字符,用下标运算符([])。
string s("some string");
if(!s.empty()){
s[0] = toupper(s[0]);//将第一个字符变成大写
}
//返回 Some string
标准库类型vector
标准库类型vector表示对象的集合,其中所有对象的类型都相同。
集合中的每个对象都有一个与之对应的索引,索引用于访问对象。
因为vector容纳着其他对象,所以它也常被称为容器(container)。
想要使用vector,包含头文件#include <vector>。
#include <vector>
using namespace std
main()
{
vector<int> ivec;//ivec保存int类型的对象
vector<Sale_item> Sales_vec;//保存Sales_item类型的对象
vector<vector<string>> file; //该向量的元素是vector对象
return 0;
}
定义和初始化vector对象
//列表初始化vector对象
vector<string> v1 {"a", "an", "the"};//列表初始化
vector<string> v2 ("a", "an", "the");//错误
//创建指定数量的元素
vector<int> ivec(10, -1);//10个int类型的元素,每个都被初始化为-1
//值初始化
vector<int> ivec2(10);//10个元素,每个都被初始化为0
vector<string> svec(10);//10个元素,每个都是空string对象
//列表初始值还是元素数量
vector<int> v3(10);//v3有10个元素,每个的值都是0
vector<int> v4{10};//v4有1个元素,该元素值是10
向vector对象中添加元素
利用vector的成员函数push_back向其中添加元素,push_back负责把一个值当成vector对象的尾元素“压到push”vector对象的“尾端(back)”。
string word;
vector<string> text;
while(cin >> word){
text.push_back(word);//把word添加到text后面
}
vector其他操作
操作 | 解释 |
v.empty() | 如果v不含有任何元素,返回真,否则返回假 |
v.size() | 返回v中元素的个数 |
v.push_back(t) | 向v的尾端添加一个值t的元素 |
v[n] | 返回v中第n个位置上元素的引用 |
v1 = v2 | 用v2中元素的拷贝替换v1中的元素 |
v1 = {a, b, c...} | 用列表中元素的拷贝替换v1中的元素 |
v1 == v2 | v1和v2相等,数量和对应位置的元素值都相同 |
v1 != v2 | |
<、<=、>、>= | 以字典顺序进行比较 |
迭代器介绍
可以使用下标运算符来访问string对象的字符或vector对象的元素,还有另外一种更通用的机制,即迭代器(iterator)。
标准库容器都可以使用迭代器
和指针不一样的是,获取迭代器不是使用取地址符,有迭代器的类型同时拥有返回迭代器的成员。
迭代器也提供了对对象的间接访问,就迭代器而言,其对象是容器中的元素或者string对象中的字符;使用迭代器可以访问某个元素,迭代器也能从一个元素移动到另外一个元素。
操作 | 解释 |
*iter | 返回迭代器iter所致元素的引用 |
iter->men | 解引用iter并获取该元素的名为men的成员,等价于(*iter).men |
++iter | 令iter指示容器中的下一个元素 |
--iter | 令iter指示容器中的下一个元素 |
iter1 == iter2 | 判断两个迭代器相等 |
iter1 != iter2 | 判断两个迭代器不相等 |
string s("some string");
if(s.begin() != s.end()){
auto it = s.begin();//it表示s的第一个字符
*it = toupper(*it) //将当前字符改写成大写形式
}
//返回 Some string,跟下标运算符功能一样
数组
数组是一种类似与标准库类型vector的数据结构,与vector相似的地方是数组也存放类型相同的对象的容器,这些对象本身没有名字,需要通过其所在位置访问;与vector不同的地方是,数组的大小确定不变,不能随意向数组中增加元素。
如果不清楚元素的确切个数,使用vector。
定义和初始化内置数组
数组的声明形如a[d],其中a是数组的名字,d是数组的维度,维度说明了数组中元素的个数,因此必须大于0;维度d必须是一个常量表达式。
定义数组的时候必须指定数组的类型,不允许用关键字auto由初始值的列表推断类型。
显式初始化数组元素:对数组的元素进行列表初始化,此时允许忽略数组的维度。
字符数组的特殊性:字符数组有一种额外的初始化形式,可以用字符串字面值对此列数组初始化;当使用这种方式时,一定要注意字符串字面值的结尾处还有一个空字符,这个空字符也会向字符串的其他字符一样被拷贝到字符数组中。
/*
a1维度是3,a2和a3维度是4,a4定义是错误的
*/
char a1[] = {'c', '+', '+'};//列表初始化,没有空字符
char a2[] = {'c', '+', '+', '\0'};//列表初始化,含有显式的空字符
char a3[] = "c++";//自动添加表示字符串结束的空字符
const char a4[6] = "Daniel";//错误:,没有空间存放空字符
不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。
int a[] = {1, 2, 3};
int a2[] = a;//错误:不允许使用一个数组初始化另外一个数组
a2 = a;//错误:不能把一个数组直接赋值给另外一个数组
访问数组元素
和标准库vector和string一样,数组的元素也能使用范围for语句或下标运算符来访问。
数组的索引从 0 开始。
数组与指针
使用数组的时候编译器一般会把它转换成指针。
对数组的元素使用取地址符就能得到指向该元素的指针。
在用到数组名字的地方,编译器都会自动将其替换为一个指向数组首元素的指针。
string nums[] = {"one", "two", "three"};//数组的元素是string对象
string *p = &nums[0];//p指向nums的第一个元素
string *p = nums;//等价于p2 = &nums[0];
多维数组
C++语言中没有多维数组,通常所说的多维数组其实是数组的数组。
当一个数组的元素仍然是数组时,通常使用两个维度来定义它,一个维度表示数组本身大小,另外一个维度表示其元素(也是数组)大小。
int ia[3][4];//大小为3的数组,每个元素是含有4个整数的数组
使用双范围for语句来处理多维数组。
当程序使用多维数组的名字时,也会自动将其转换成指向数组首元素的指针。
//多维数组指针申明时,圆括号必不可少
int *ip[4];//整型指针的数组
int (*ip)[4];//指向含有4个整数的数组
参考
C++ Primer(第5版).