第3章 字符串、向量、数组
3.2 string
初始化
string s1; //默认初始化,s1是一个空串 string s2(s1); //使用s1初始化s2 string s2=s1; //同上 string s3("value"); //s3是字面值"value"的副本,但是不包括\0 string s3="value"; //同上 string s4(10,‘c‘); //使用10个c初始化s4
操作
os<<str //将str写入输出流,并返回os is>>str //将is中读取的字符串空格分割赋给str,并返回is getline(is, str) //从is中读取一行赋值给str,换行符也读取了(流中的位置在换行符后),但是没有赋值给str str.empty() str.size() //返回字符的个数 str[n] //返回str中第n字符的引用 str1+str2 str1=str2 //将str2的副本代替str1中的内容 str1 == str2 str1 != str2 <, <=, >, >=
warming
string::size_type string::size();
size函数返回的是size_type类型,这其实是一个无符号的类型,所以应该注意与负数的比较:
表达式:str.size() < -1,将返回为true,因为-1需要转换成无符号进行比较。
str1+str2
字面值之间不能相加,比如"asd" + "1256"
warming
string的类型和字符串"string"的类型是不一样的,后者是char[]
处理字符
使用如下语句处理:
for( auto c : str) { statement..... }
isalnum(c) //c为字母或数字时为真 isalpha //字母 iscntrl //控制字符 isdigit //数字 isgraph //不是空格,但是可以打印 islower //小写字母 isprint //可打印,包括空格 ispunct //标点符号 isspace //空白字符 isupper //大写字母 isxdigit //十六进制数字 tolower //变成小写 toupper //变成大写
warming
刚才的基于范围的for循环,仅仅是获取string中字符的副本,如果要改变string中的值,必须使用引用:
for (auto &c : s)
3.3 vector容器
#include<vector>
using std::vector;
vector是一个类模板。
模板本身不是类或者函数,它是对类或函数的说明,编译器根据说明(尖括号中的说明)创建类或者函数(这个过程叫做实例化)。
vector能够容纳绝大数类型的对象,但是引用不是对象,不能包含进去。
warming
在早期版本的编译器中,vector中若存放vector,则内层尖括号需要有空格分割,如:vector< vector<int> >
初始化
vector<T> v1; //v1是空vector,类型为T,执行默认初始化 vector<T> v2(v1); //v2中包含v1中的所有元素 vector<T> v2 = v1; //同上 vector<T> v3(n, val); //v3包含n个重复的val vector<T> v4(n); //v4包含了n个重复的默认初始化的T对象,必须能够有默认的初试化的T vector<T> v5{a, b, c, d}; //v5包含了初始值 vector<T> v5={a, b, c, d}; //同上
warming
vector<string> v_str("hello");
这个语句是错误的,因为"hello"是char[],没有办法初始化string。
vector<string> v_str {"hello"}
这个是正确的,可以用列表初始化string的vector
vector<string> v_str {10, "hello"}
这个是正确的,初始化时时10个相同的"hello"
vector操作
/*注意在执行压入时,会改变v1的end判断,尤其是在范围for语句中可能出现错误*/ v1.push_back(t); //将t压入到v1的尾部 v.empty(); //判断不含任何元素,返回真 v.size(); //元素的个数 v[n]; //得到第n个元素 v1 = v2; //使用v2的元素的拷贝替换v1中所有的元素 v1 = {a,b,c,d}; //同上 v1 == v2; //当且仅当v1、v2元素个数一样,并且对应元素相同 v1 != v2; <, <=, >, >=
3.4 迭代器
迭代器提供了对对象的间接访问。有效的迭代器指向某个元素,或者尾元素的下一个位置,其他的情况是无效的迭代器。
for(auto iter = v.begin(); iter != v.end(); ++iter) { *iter = (*iter)+5; }
迭代器运算
v.begin(); //返回容器的第一个元素的迭代器 v.end(); //返回容器尾元素的下一个元素的迭代器 v.cbegin(); //返回容器的第一个元素的迭代器,常量形式不可修改 v.cend(); //返回容器尾元素的下一个元素的迭代器,常量形式不可修改 *iter; //返回所指元素的引用 iter->member; //解引用,并返回其所指元素的member成员,同(*iter).member ++iter; //指向下一个元素 --iter; //指向上一个元素 iter+n; //超出最后一个元素不会报错 iter-n; //超出第一个元素不会报错 iter1 - iter2; //两个迭代器之间的距离 iter1 == iter2; //判断是同一个元素 iter1 != iter2; >, >=, <, <= //判断所处位置的前后
迭代器的类型分为iterator和const_iterator,如:vector<int>::iterator iter;
迭代器的距离类型为difference_type,是一个带符号的整数型。
如果容器为空,则begin和end返回的都是尾元素的下一个位置。
如果容器为常量,则begin与cbegin返回的都是常量形式,end亦然.
3.5 数组
与vector一样,数组的元素不能是引用。复杂数组的声明
在编译时,就需要知道数组的大小,因此,数组初始化时,维度应该为常量。
与其他内置类型(int、double)一样,数组在函数外定义时,才会进行默认初始化;在函数内定义时,并没有初始化。为了给数组默认初始化,可以将一个空的列表赋值给数组。
int a[]={1, 2, 3}; //显式初始化 int a[10]={}; //显式初始化,初始化后都为0 int d[]=a; //错误,不能拷贝 int size=sizeof(a)/sizeof(*a); //获取数组的大小
复杂数组的声明
int *ptrs[10]; //存放10个整形指针 int (*Parray)[10] = &arr; //Parray指向含有10个整形的数组 int (&arrRef)[10] = arr; //arrRef是一个含有10个整形数组的引用 int *(&arrRef)[10] = arr; //arrRef是数组的引用,数组中有10个指针
数组的访问
数组下标的类型为size_t,定义在<cstddef>头文件中,它设计的足够大,可以表示内存中任意对象的大小。
指针与迭代器
编译器一般会将数组名当成首个元素的指针。
在头文件<iterator>中定义了名为begin和end的函数,用于得到数组的手元素的指针和尾元素下一位置的指针。
int a[10]={}; for(int *pbeg=begin(a);pbeg!=end(a);pbeg++) cout<<(*pbeg)<<endl;
两个指针相减,得到他们之间的距离,这两个指针也必须是指向同一个数组中元素的指针。
这个距离是类型为ptrdiff_t,定义在<cstddef>中。
C风格字符串
char类型的数组中,以‘\0‘字符结尾的字符序列。其所有的运算都要通过<cstring>中的函数完成。
C++与C的接口
//C字符串可以通过构造函数转换成string //string可以通过c_str()函数转换成C字符串 char cStr[] = "字符串"; string cppStr(cStr); const char *ctr = cppStr.c_str(); //使用数组初始化vector,只需指明首尾数组地址 int int_arr[]={1, 2, 3, 4, 5}; vector<int> ivec(begin(int_arr), end(int_arr));
3.6 多维数组
多维数组实际上是数组的数组。在定义时,可以跟一个空的列表进行默认初始化。
为什么多维数组还要要求每一维中的元素个数相同?因为数组要求其中存储的类型相同,数组类型起始包含了数组的长度的,所以每一个个数都要求相同。
对于二维数组,常将第一维成为行,第二维称为列。
warming
在使用范围for循环遍历多维数组时,除了最内层循环其他都要用引用遍历,避免数组被自动转换为指针。
for( auto &row : table) for( auto line : row) cout<<line<<endl;
如果在外层for循环中,不使用auto,则需要为二维数组中的内层维度指明类型。
例如对于int table[10][5],就需要知道内层维度为int[5],所以auto的类型应该是int[5]。但是如果写成int row[5],则每次迭代,迭代器会将table中的当前维度的副本赋值给row,由于数组不能够直接赋值,所以此处只能使用对int[5]的引用:
for( int (*row)[5] : table) for( int line : row) cout<<line<<endl;
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。