字符串、向量和数组
命名空间using声明
声明后无需前缀就能使用对应名字,对每个名字都需要进行独立的using声明。头文件不应包含using声明,防止与调用文件产生名字冲突。
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int main() {
cout << "输入两个值" << endl;
int v1, v2;
cin >> v1 >> v2;
cout << "sum:" << v1 + v2 << endl;
return 0;
}
标准库类型string
1.头文件及声明
#include<string>
using std::string;
2.定义及初始化string对象
string s1;//默认为空
//拷贝初始化,有等号
string s2 = "123";
string s3 = s2;
//直接初始化
string s4(10,'c');
//使用string或字符串数组构造初始化
string str1 = "hhdyzwhy";
string str2(str1,4,4); //输出zwhy,(string变量,第几个开始,拷贝几个)
//可使用string或者const char*对象进行初始化
string str3(cp,n); //cp是字符串数组,第一项为指针,第二项为个数,str3是从cp开始的n个字符的拷贝
string str4(s2,pos2);//s2为string类型,从下标开始拷贝至空字符结束
//eg:空字符为'\0'
const char* cp = "hhdyzwhy!"; //必须以空字符结尾
string s = "hhdyzwhy!";
string s1(cp,4); //hhdy
string s6(cp + 1, 4); //hdyz
string s2(s, 4); //从下标4开始
string s3(cp, 2, 1);//d
cout << s1 << '\n' << s6<<'\n'<<s2 << '\n' << s3 << endl;
//char cs[] = {'a','b','\0'};
char cs[] = "abc"; //必须以空字符结尾
string s4(cs);//abc
string s5(cs, 1);//a
cout << s4 << '\n' << s5 << endl;
//使用substr拷贝初始化
string s7 = s.substr(0, 4); //hhdy
string s8 = s.substr(4);//zwhy!
cout << s7 << '\n' << s8 << endl;
//使用替换函数进行赋值
s.assign(4,'h');//hhhh
s.assign(s7.begin(),s7.begin()+1);//h
3.string修改操作
- 顺序容器常规操作
- insert和erase除迭代器外接受下标位置定位,insert支持插入C风格字符数组以及字符串
c.append(str); //字符串末尾插入
c.replace(pos,n,str);//将下标位置处开始的n个字符串替换为str
4.string搜索操作
- 6个搜索函数,4个重载版本
- 成功返回string::size_type值,失败返回string::npos静态成员
- 注意,last相关查找是从后往前,pos位置写的是起始位置,如果找0到10内是否有字符串,pos = 10。
string s = "yhhdyzwhhhhhdy!";
string s2 = "zwhy";
char cstr[] = "dy";
vector<string::size_type> i(7,0);
i[0]= s.find("hhdy");//字符串第一次出现的位置,返回1
i[1] = s.find("zwhy");//找不到,返回npos
i[2] = s.rfind(cstr);//dy最后一次出现位置,返回12
i[3] = s.find_first_of("dy", 5);//dy在s[5]之后第一次出现的位置,返回12
i[4] = s.find_last_of(cstr, 0, 1);//返回npos,因为last相关是从后往前查找,pos = 0,从0查到0
i[5] = s.find_first_not_of("hdy");//第一次不出现“hdy”中字符的位置,返回5(z)
i[6] = s.find_last_not_of("hdy");//最后一次不出现“hdy”中字符的位置,返回14(!)
const char cstr1[] = "hdy";
i[7] = s.find_last_of(cstr1,14,1); //返回11
5.string 对比函数
- s.compare
//()内可省略
(pos1,n1),s2,(pos2,n2)//和string对比,对比pos开始n个字符
(pos1,n1),cp,(n2)//字符数组指针cp,n2个字符比较
6.数值转换
to_string(val) //val是任意算术类型
//string转换为int、long、unsignedlong、longlong、unsignedlonglong
stoi(s,p,b)//p为s下标,b为基数(默认10)。
stol(s,p,b)
stoul(s,p,b)
stoll(s,p,b)
stoull(s,p,b)
//string转换为float、double、long double
stof(s,p)
stod(s,p)
stold(s,p)
4.string对象操作
对象操作
//os,is为输入输出流,istringstream,ostringstream
os<<s 将s写入输出流os,返回os
is>>s 从is中读取字符串给s,返回is
getline(is,s) 从is中读取一行赋给s,返回is
//读写操作
cin>>s; //忽略开头的空白,输出到下一处空白为止。
getline(cin, s3) //每次获取一行,不包括换行符
//内存操作
s.empty() //是否为空
s.size() //返回字符串长度
//比较操作,按照字典序比较
>=,<=,>,<,==,!=//关系运算符和相等性运算符都可以使用
//赋值,相加
//字符串和字面值相加,两侧对象都要至少有一个string变量,不能两个字面值相加赋给字符串变量,可以写在一起变成赋值语句。
//原因是c++语言中字符串字面值不是标准库string类型。
string str1 = "hhdyzwhy",str;
str2 = str1;//hhdyzwhy
str2 +=str1;//hhdyzwhyhhdyzwhy
str2 = str2 +"!"; //hhdyzwhyhhdyzwhy!
str2 = "hhdyzwhy";
//string对象中的字符操作
for(auto c:s)//循环中,处理每个字符
s[0]~s[s.size()-1]//下标运算符,索引
decltype(s.size()) index = 0;//对于下标索引的定义
size()函数返回类型
string::size_type //在类string里定义,如string的类型实际为unsigned long long;
字符判断库 <cctype>
isalnum(c)//字母或数字
isdigit(c)//数字
islower(c)//小写字母
isupper(c)//大写字母
isalgha(c)//字母
tolower(c)//大写字母输出小写,不是大写字母输出c
toupper(c)//同上,小转大
iscntrl(c)//控制字符
isgraph(c)//不是空格但可打印
ispunct(c)//标点符号
isspace(c)//空白(空格、横向制表符、回车符、换行符)
注:C++版本的C标准库
从C的c.name->C++的cname,c代表属于C语言标准库的头文件。头文件中定义的名字属于命名空间std。
标准库vector类型
vector是对象的集合,集合中每个对象都有一个对应的索引。vector也常被称为容器。
头文件及声明
#include<vector>
using std::vector;
类模板和函数模板
模板不是类和函数,是作为编译器生成类或函数编写的说明。编译器中根据模板创建类或函数的过程称为实例化。实例化时需要指出类或函数的数据类型。
早期c++
vector<vector<int> > //最右>前加空格
vector操作
//初始化和列表初始化
vector<int> num(10,1);//圆括号构造对象,10个元素,值为1
vector<int> num{10,1};//花括号,列表初始化,2个元素,值为10和1
vector<string> strs{10}; //10不能作为列表初始化,这里是构造10个默认初始化元素
vector<string> strs{10,"hi"};//10个值为"hi"的元素
vector<int> num = {{1},{2},{3}};
//拷贝、赋值与添加元素,添加元素时,不能使用范围for循环
num.push_back(1); //尾部插入元素
num[n] //引用,0~num.size()-1,用于访问元素,保持下标合法的手段是合理使用for循环
v1 = v2//拷贝
v1 = {1,2,3} //拷贝替换
//内存个数
num.empty();
num.size();
//比较,按照字典序来
迭代器
访问对象方法
下标引用
迭代器,对于容器更通用,有些容器不支持下标访问。
注:string对象不属于容器,但支持很多容器类型的操作。
迭代器定义
提供对对象的间接访问,有效迭代器指向某个元素或者指向容器中尾元素的下一个位置,可以将迭代器理解为特殊的指针。
创建迭代器同时返回迭代器成员,包括begin成员和end成员,指向第一个元素和尾元素的下一个位置。其中,end成员又被称为尾后迭代器,表示已经处理完容器中所有元素。当容器为空时,begin和end返回同一个迭代器(尾后迭代器)。
迭代器通常有三种含义:迭代器概念本身、容器定义迭代器类型、某个迭代器对象。
迭代器类型
一般来说无需精确直到迭代器类型,可以使用auto进行初始化。实际迭代器类型表示:
vector<int>::iterator it; //可以读写
vector<int>::const_iterator it;//只能读不能写
每个容器定义一个名为“iterator”的类型,并支持迭代器相应操作。
begin和end
除了begin和end,还有cbegin和cend返回const_iterator 类型。
vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); //vector<int>::iterator
auto it2 = cv.begin();//vector<int>::const_iterator
auto it3 = v.cbegin();//vector<int>::const_iterator
auto it4 = v.cend(); //vector<int>::const_iterator
迭代器解引用
*iter //解引用,此时iter必须确实指示某个元素,例end()不能引用
iter->mem //解引用iter,返回其中的mem成员
解引用获取迭代器指向的对象,如果说对象的类型是类,可以进一步访问他的成员。
(*it).empty(); //圆括号必须有
it->empty();
it->mem //和下一行意思一样
(*it).mem
限制
当容器容量变化时,迭代器会失效。
迭代器操作
++iter //指示容器中下一个元素,例end()不能递增
--iter //指示容器中上一个元素
iter + n;
iter - n;
iter += n;
iter -= n;
iter1 - iter2 //二者之间的距离,类型的difference_type,由于距离可负可正,所以是带符号整数
//一个简单调用
for (auto i = str.begin();i != str.end(); ++i) {
if(islower(*i)){
*i = toupper(*i);
}
cout << *i;
}
cout << endl;
数组
存放固定数量的对象,与vector相比,数组大小固定。
定义和初始化
初始化大小必须是一个常量表达式,不允许使用auto指定数组类型。
不允许使用数组给数组拷贝和数组赋值,有一些编译器可能支持数组赋值,这是所谓的编译器扩展,是非标准特性;
constexpr unsigned sz = 42;
int arr[10];
int *parr[sz]; //含有42个整形指针的数组
int a3[5] = {0,1,2};
复杂数组声明
复杂数组的阅读是从内向外阅读。
int arr[10];
int (*parr)[10] = arr;
int (&refarr)[10] = arr;
int *(&arry)[10] = refarr;
数组访问
下标访问
0~arr.size(),类型size_t,cstddef头文件中定义size_t类型。
指针访问
- 数组与指针:数组使用时编译器会把它转换成指针,数组的操作实际上是指针的操作。可以对对象使用取地址符获得指向该对象的指针。直接使用数组名字,编译器会将它编译为一个指向数组首元素的指针。
- 通过首尾指针实现数组遍历:数组的指针可以实现和迭代器一样的功能。实现遍历时,需要知道数组首地址,和尾的后一个地址。首地址直接是数组名,尾后地址可以通过数组size数获得。或者可以使用iterator头文件中的begin()和end()函数。这两个函数不是成员函数,将数组作为他们的参数。
- 指针运算:1.给一个指针加减某整数值,仍为指针。2.指针相减是二者之间的距离,类型是ptrdiff_t的标准库(cstddef)带符号类型3.比较。
- 对于空指针同样可以使用以上指针运算,空指针可以加减0,两个空指针可以彼此相减,值为0。
- 解引用和指针运算交互
string nums[] = {"one", "two", "three", "four"};
//数组与指针
string *p1 = &nums[2]; //指向"three"的地址
string *p2 = nums; //相当于&nums[0]
auto p3(nums); //是一个string类型指针
decltype(nums) strnum = {"a", "b", "c"}; //返回nums类型
cout << *p2 << endl; //"one"
cout << *strnum << endl; //"a"
//通过首尾指针实现数组遍历
string beg = nums;
string last = &nums[4];
#include<iterator>
string *beg1 = begin(nums);
string *last1 = end(nums);
//指针运算
++p2; //&nums[1],"two"
string *p4 = nums+2; //指向"three"
auto n = p4 - p2;//值为1
//解引用和指针运算交互
int narr[] ={1,2,3,6} ;
int last = *(narr+3); //6
int n4 = *narr+3;// 4
指针与下标关系
- 对于下标操作,相当于先将数组转化为指向数组首地址的指针,然后通过下标值进行指针移位。
- 标准库类型(string、vector)下标是无符号类型(必须大于0)。内置(数组)的下标运算没这个要求。
int n[] = {0,1,2,3,4,5,6,7,8};
int i1 = ia[2];
//相当于下方的两个操作
int *p = ia;
int i2 = *(p+2);
int *p = &ia[2]; //2
int j = p[1]; //3
int k = p[-2];//0
C风格字符串(char型数组)
字符串存放在字符数组中,以空字符结束(‘\0’)。
头文件
#include<cstring>
C风格字符串操作
- 相当于char型数组,C风格字符串名返回首个char字符地址。
- 比较时不能使用>/<等比较符,要使用strcmp函数。
- 也不能向string一样进行加减拼接操作。
//初始化
char p1[] = {'C', '+', '+', '\0'};
char p2[] = {'h', 'h', 'd', 'y', 'z', 'w', 'h', 'y', '\0'};
cout << strlen(p1) << endl; //不算p在内的长度
cout << strcmp(p1, p2) << endl; //比较,相等返回0,大于返回正值,小于返回负值
cout << strcat(p1, p2) << endl; //p2附加到p1之后,返回p1,返回"C++hhdyzwhy"
cout << p1 << endl; //返回"C++hhdyzwhy"
strcpy(p1, p2); //p2拷贝给p1
cout << p1 << endl; //"hhdyzwhy"
与旧代码接口
string和C风格字符串
- C风格字符串->string
char p2[] = {'h', 'h', 'd', 'y', 'z', 'w', 'h', 'y', '\0'};
string s = p2;
string s1 = s+p2;
s1 = s+p2;
- string->C风格字符串
const char *pstr = s.c_str(); //返回常量char型指针
vector和数组
- 数组->vector
int n[] = {0,1,2,3,4,5,6,7,8};
vector<int> num(begin(n),end(n));
vector<int> num1(n,n+4); //左闭右开
多维数组
c++中的数组可以理解为数组的数组。
int ia[3][4]; //大小为3的数组,每个元素是含有4个整数的数组,第一个维度是行,第二个维度是列
int arr[10][20][30] = {0}; //arr是一个大小为10的数组,数组里每个元素是大小为20的数组,数组里每个元素为30个int类型整数。
多维数组初始化
int ia[3][4] = {{1,3,2,3},{2,3,4,5},{3,4,5,6}};
int ia[3][4] = {1,3,2,3,2,3,4,5,3,4,5,6};
int ia[3][4] = {{0},{4},{8}};
int ia[3][4] = {1,3,2,3};
多维数组访问
下标引用
int ia[3][4] = {{1,3,2,3},{2,3,4,5},{3,4,5,6}};
int ia[0] //数组
int ia[0][1]//数组内元素
int (&row)[4] = ia[0] //row是ia第一行的引用
当时for循环处理数组时,除了最内层循环外,其他所有循环的控制变量都应该是引用类型,防止数组被自动转换为指针。
int ia[3][4] = {{1,3,2,3},{2,3,4,5},{3,4,5,6}};
for(const auto &row:ia)
for(auto col:row)
cout<<col<<endl;
指针和多维数组
- 使用多维数组名字时,会将其自动转换为数组首元素的指针(首元素可以是数组)。
- 定义时p的圆括号必不可少
- 可以使用类型别名简化指针
int ia[3][4] = {{1,3,2,3},{2,3,4,5},{3,4,5,6}};
int (*p)[4] = ia; //指向ia[0]的地址,相当于&ia[0]
++p; //指向ia[1]的地址,相当于&ia[1]
int* pp = *p; //指向p的首地址
cout << *pp << endl; //pp的值,1
//为防止数组前加指针类型,使用auto
for(auto p = ia;p!=ia+3;++p){
for(auto q = *p;q!=*p+4;++q)
cout<<*q<<' ';
cout<<endl;
}
//另一种写法
for(auto p = begin(ia);p!=end(ia);++p){
for(auto q = begin(*p);p!=end(*p);++q)
cout<<*q<<' ';
cout<<endl;
}