第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;

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。