C++ Primer学习笔记之第三章--字符串,向量和数组

3.1 命名空间的using声明

1、格式

using namespace::name;

(1)例:

using namespace::std;

3.2 标准库类型string

1、标准库类型string的定义包含在#include<string>头文件中。

2、初始化string的方式
(1)直接初始化(不用等号的初始化)

(2)拷贝初始化

(3)例:

string s1;
string s2(s1);      //直接初始化,上下等价。
string s2=s1;       //拷贝初始化,上下等价。
string s3("value");
string s3="value";
string (n,'c');     //由n个字符c组成的字符串


3.2.2 string的操作
1、string的操作:

1、os<<s;        //输出
2is>>s;        //输入
3、getline(is,s);    //从is中读取一行赋值给s,返回is
4、s.empty();        //s为返回true,非空返回false
5、s.size();         //返回s中字符的个数
6、s[n]              //返回第n个字符引用,n从0计起
7、s1=s2             //用s2的副本替s1中原来的字符
8、s1==s2            
//s1和s2所含字符完全一样,则为true,否则为false,区分大小写。
9、s1!=s2            //与s1==s2相反
10、>,<=,>=,<        //利用字符在字典中的排序,区分大小写

2、string对象会自动忽略开头的空白(即空格符,换行符,制表符等)

3、读取未知数量的string对象
(1)例:

string word;
while(cin>>word)        //反复读取,直至到达文件末尾
    cout<<word<<endl;

(2)while语句条件负责在读取时,检测输入流的情况:
如果流有效,即没有遇到文件结束标记或非法输入,就执行while内部的操作。

4、使用getline读取一整行
(1)getline函数的参数是一个输入流和string对象,函数从给定的输入流读入内容,直到遇到换行符

(2)换行符被读入,但不存入string对象

5、word.size()返回的是无符号值,类型为 string::size_type

6、比较string对象
(1)比较符号:> ; < ; >= ; <=

(2)比较规范:
<1>如果两个string对象的长度不同,而且较短的string对象的每个字符都与string对象位置上的字符相同,则说较短的string对象小于较长的string对象。
<2>如果两个string对象在某些位置上不一致,则string对象比较的结果其实是string对象中第一对相异字符比较的结果

7、字符串字面值并不是标准库类型string的对象,字符串字面值与string是不同的类型。


3.2.3 处理string对象中的字符
1、使用cctype头文件中的函数(#include<cctype>

1、  isalnum(c)      //当c是字母或数字时为真
2、  isalpha(c)      //当c是字母时为真
3、  iscntrl(c)      //当c是控制符时为真
4、  isdigit(c)      //当c是数字时为真
5、  isgraph(c)      //当c不是空格但可打印时为真
6、  islower(c)      //当c是小写字母时为真
7、  isupper(c)      //当c是大写字母时为真
8、  isprint(c)      //当c是可打印字符时为真
9、  ispunct(c)      //当c是标点符号时为真
10、 isspace(c)      //当c是空白(空格,横向制表符,纵向制表符,回车符,换行符,进纸符)时为真
11、 isxdigit(c)     //当c是十六进制数字时为真
12、tolower(c)       //如果c是大写字母,输出对应的小写字母,否则原样输出小写字母。
13、toupper(c)       //如果c是小写字母,输出对应的大写字母,否则原样输出大写字母。

2、使用基于范围的for语句处理string对象的每个字符
(1)C++11标准为我们提供了范围for(range for)语句
(2)语法形式:

for(declaration:expression)
    statement;

//①expression:是一个对象,用于表示一个序列。
//②declaration:负责定义一个变量,该变量将用于访问序列中的基础元素。

(3)例:

string str("some string");
for(auto c:str)
    cout<<c<<endl;

(4)使用范围for语句改变字符串中的字符
①把循环变量定义成引用类型即可
②range for循环的是declaration中的变量,而不是expression的变量。

(5)只处理一部分字符的方法
<1>访问string对象中单个字符的方式
①使用下标运算符[]
②使用迭代器

<2>下标运算符([])接受的输入参数是string::size_type类型的值,返回值是该位置上字符的引用。

<3>例:

string s("some string");
for(decltype(s.size) index=0;
    index!=s.size()&&!isspace(s[index]);
    ++index)
    s[index]=toupper(s[index]);

3.2.3 练习

练习3.6:编写一段程序,使用范围for语句将字符串内的所有字符用x代替

string str="abc123";
for(auto &c:str)
    c='x';
cout<<str<<endl;

练习3.10:编写一段程序,读入一个包含标点符号的字符串,将标点符号去除后输出字符串剩余的部分

string str;
getline(cin,str);
for (auto c : str)
    if (!ispunct(c))
        cout << c;
cout << endl;

3.3 标准库类型vector

1、标准库vector表示对象的集合,也称对象的容器(container),其中具有的对象类型都相同。

2、使用vector需要头文件#include

3、vector是一个类模版

4、实例化(instantiation):指编译器根据模版创建类或函数的过程。

5、vector是模版而非类型,由vector生成的类型必须包含vector中元素的类型。

6、引用不是对象,所以不存在包含引用的vector。

7、vector的使用例子,需提供额外的信息是vector内所存放对象的类型:

vector<int> ivec    //ivec保存int类型对象
vector<Sales_item> Sales_vec;   //保存Sales_item类型对象
vector<vector<string>> file;    //该vector的元素是vector对象

3.3.1 定义和初始化vector对象
1、初始化vector对象的方法

1vector<T> v1;     //v1是个空vector,潜在元素为T类型,执行默认初始化。
2vector<T> v2(v1);     //v2包含v1所有元素的副本
3vector<T> v2=v1;      //等价于v2(v1)
4vector<T>  v3(n,val);     //v3包含了n个值为val的重复元素。
5vector<T> v5{a,b,c...};   //v5包含厨师个数的元素,每个元素赋以相应的初始值。
6vector<T> v5={a,b,c...}   //等价于v5{a,b,c...}

2、如果vector对象中的元素类型不支持默认初始化,我们必须提供初始化的元素值。

3、用值初始化和列表初始化vector对象的区别
(1)用()是值初始化,用()提供的值是用来构造(ocnstruct)vector对象的。

(2)用{}提供的值是vector指定类型对象的初值,但类型需一致。


3.3.3 其他vector操作
1、vector支持的操作

1、v.empty();        //如果v不含有任何元素,返回真,否则返回假
2、v.size();         //返回v中元素的个数
3、v.push_back(t);   //向v的尾端添加一个值为t的元素
4、v[n];             //返回v中第n个位置上元素的引用
5、v1=v2;            //用列表中元素的拷贝替换v1中的元素
6、v1={a,b,c...};    //用列表中元素的拷贝替换v1中的元素
7、v1==v2;           //v1和v2相等,元素数量相同且对应位置的元素值都相同为真,否则为假
8、v1!=v2;           //与v1==v2相反
9、>;<;>=;<=         //以字典顺序进行比较     

2、vector.size()返回值类型为vector定义的size_type类型。要使用size_type,需指定它是由那种类型定义的:

vector<int>::size_type;     //正确
vector::size_type;          //错误

3、vecotr对象(以及string对象)的下标运算符可用与访问已存在的元素,而不能用于添加元素。

4、vector只能对取值已存在的元素执行下标操作

vector<int> ivecl
    cout<<ivec[0]<<endl;
    //错误,ivec不包含任何元素!!!

3.3.2 节练习

练习3.14:编写一段程序,用cin读入一组整数并将它们存入vector对象:

int a;
vector<int> v1;
while(cin>>a)
    v1.push_back(a);
for(auto i:v1)
    cout<<i<<endl;

练习3.15:改写3.14,不过这次读入字符串

string str;
    vector<string> st;

    while (getline(cin, str) && str != "quit")
        st.push_back(str);

3.3.3 节练习
练习3.17:编写一段程序,从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改为大写形式。输出改变后的结果,每个词占一行:

string str;
    vector<string> st;

    while (getline(cin, str) && str != "quit")
        st.push_back(str);
for (decltype(st.size()) i = 0;i < st.size();i++)
{
    for (auto c : st[i])
        if (islower(c))
            cout <<static<char>(toupper(c));
    cout << endl;
}

练习3.20:读入一组整数并把它们存入一个vector对象,将每对相邻整数的和输出出来。改写你的程序,这次要求线输出第一个和最后一个元素的和,接着输出第二个和倒数第二个元素的和,以此类推:

1int i;
    vector<int> i1;
    while (cin >> i)
        i1.push_back(i);
    for (decltype(i1.size()) j = 0;j < i1.size();j++)
    {
        cout<<i1[i]+i1[i+1]<<endl;
    }

(2int i;
    vector<int> i1;
    while (cin >> i)
        i1.push_back(i);
    for (decltype(i1.size()) j = 0;j < i1.size();j++)
    {
        if (j < i1.size() - 1)
            cout << "Neighbor number sum :" << i1[j] + i1[j + 1] << endl;
        else
            cout << "Neighbor number sum :" << i1[j - 1] + i1[j] << endl;
        if (j < i1.size()/ 2)
            cout << "Head and End number sum£º" << i1[j] + i1[i1.size() - j - 1] << endl;
    }

3.4 迭代器介绍

1、string对象不属于容器类型。
2、迭代器(iterator)也是提供对对象的间接访问。

3.4.1 使用迭代器
1、例:

auto b=v.begin(),e=v.end(); //b和e的类型相同

(1)begin成员函数负责返回指向第一个元素的迭代器

(2)end成员函数负责返回指向容器尾元素的下一个位置的迭代器

(3)该迭代器只是的是容器一个本不存在的“尾后(off the end)”

(4)end成员函数返回的迭代器也被称作尾后迭代器(off the end iterator)

2、如果容器为空,begin成员函数和end成员函数返回的是同一个迭代器,都是尾后迭代器

3、迭代器运算符
(1)==和!=比较两个合法迭代器是否相等

(2)如果两个迭代器指向的元素相同或都是同一个容器的尾后迭代器,则它们相等,否则就说两个迭代器不相等。

(3)标准容器迭代器的运算符

1*iter       //返回迭代器所指元素的引用
2、  iter->mem   //解引用iter并该元素名为mem的成员,等价于(*iter).mem
3++iter        //令iter只是容器中的下一个元素
4--iter        //令iter只是容器中的上一个元素
5、iter1==iter2  //迭代器相等
6、iter1!=iter2  //迭代器不相等

4、因为end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用操作

5、迭代器和迭代器类型
(1)迭代器三种不同的含义
<1>迭代器概念本身
<2>指容器定义的迭代器类型
<3>某个迭代器对象

(2)每个容器类定义一个名为iterator的类型,该类型支持迭代器概念所规定的一套操作。

6、begin和end运算符
(1)begin和end返回的具体类型由对象是否常量决定的
<1>如果是常量,begin和end返回类型为const_interator
<2>不是常量,则返回类型为iterator

(2)便于专门得到const_interator类型返回值,C++11引入两个新函数
<1>cbegin
<2>cend


3.4.2 迭代器运算
1、vector和string迭代器支持的运算

1、iter+n        //前移n个元素(前移方向为begin到end的方向)
2、iter-n        //后移n个元素
3、iter+=n       
4、iter-=n
5、iter1-iter2       //两迭代器的距离(指向同一容器或尾后迭代器),类型为difference_type()

2、迭代器中关系运算符的使用(>、>=、<、<=)
(1)要指向同一个容器或尾后迭代器

(2)关系运算符可以比较迭代器所指位置的前后
例:

it=iv.begin()+1;
mid=iv.begin()+iv.size()/2;
if(it<mid)
    ....

3.5 数组

1、数组是复合类型

2、数组的声明范例:

int *ptrs[10];      //ptrs是含有10个整型指针数组

int (*ptarray)[10]; //ptarray是一个指向含有10个整型数组的指针

int (& arrRef)[10]=arr; //arrRef引用一个含有10个整型数的数组

3.5.2 访问数组元素
1、数组使用下标[]时,下标类型为size_t(在头文件cstddef定义)

3.5.3 指针和数组
1、在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针。

2、两个指针相减的结果是一种名为ptrdiff_t的标准库类型(带符号类型)

3、下标和指针
例:

int ia[]={0,2,4,6,8}'
int i=ia[2];        
int *p=ia;
i=*(p+2);       //等价于i=ia[2]

int *q=&ia[2];  
int j=q[1];     //q[1]等价于*(q+1),即ia[3]
int k=q[-2];    //q[-2]等价于*(p-2),即ia[0]

4、内置类型的下标运算符所用的索引值是有符号类型,该点与string和vector不同。

3.5.4 C风格字符串
1、C风格字符串不是一种类型,而是为了表达和使用字符串而形成的一种俗成的写法
(1)字符串存放在字符数组中以空字符结束(’\0’)。

2、C标准库String函数(C对应string.h头文件,C++对应cstring头文件)

1、strlen(p);        //返回p的长度,不计'\0'。
2、strcmp(p1,p2);    //比较p1和p2相等性。p1==p2,返回0;p1>p2,返回正值;p2>p1;返回负值
3、strcat(p1.p2);    //将p2附加到p1之后返回p1
4、strcpy(p1,p2);    //将p2拷贝给p1,返回p1


3.5.4 节练习
练习3.39:编写一段程序,比较两个string对象。再编写一段程序,比较两个C风格字符串的内容:

string a="abced",b="lkkjilk";
if(a.size()>b.size())
    cout<<"string a>b"<<endl;
else 
    cout<<"string a<b"<<endl;

//C风格字符串
char a[]="abced",b[]="lkkjilk";
if(strlen(a)>stlen(b))
    cout<<"string a>b"<<endl;
else 
    cout<<"string a<b"<<endl;

练习3.40:编写一段程序,定义两个字符数组并用字符串字面值初始化它们,接着再定义一个字符数组存放前两个数组连接的后果。使用strcpy和strcat把前两个数组的内容拷贝到第三个数组中:

char a[] = "kingoliver";
char a2[] = "Archlinux";
char *a1 = strcat(a, a2);
cout <<"strcat(a,a2)="<< a1 << endl;
a1 = strcpy(a, a2);
cout << "strcpy(a,a2)=" << a1 << endl;

3.5.5 与旧代码的接口
1、混用string对象和C风格字符串
(1)允许使用空字符结束的字符数组初始化string对象或为string对象赋值,但反过来不行。

2、string提供了名为c_str的成员函数,可以将string对象转换为C风格字符串
(1)例:

string s=("Hello World");
char *str=s;        //错误,不能用string对象初始化字符数组char *
const char *str=s.c_str();  //正确

(2)c_str函数返回值为C风格的字符串,即返回一个指向常量字符数组的指针。

3、使用数组初始化vector对象
(1)不允许使用vector对象初始化数组
(2)可用数组初始化vector对象:只需指定拷贝区域的首元素地址和尾后地址。

(3)例:

int int_arr[]={0,1,2,3,4,5};
vector<int> ivec(begin(int_arr),end(int_arr));

//用于初始化vector对象的值也可能仅是数组的一部分
vector<int> subVec(int_arr+1,int_arr+4);


3.5.5 节练习
练习3.41:编写一段程序,用整型数组初始化一个vector对象:

int a[5] = { 1,2,3,4,5 };
vector<int> arrvec(std::begin(a), std::end(a));
cout << "Array to vector£º";
for (auto i = arrvec.cbegin();i != arrvec.cend();++i)
    cout << *i;
cout << endl;

练习3.42:编写一段程序,将含有整数元素的vector对象拷贝给一个整型的数组:

int a;
vector<int> arrv;
while (cin >> a)
    arrv.push_back(a);
int arr[5];
cout << "Array number: ";
auto j = arrv.begin();
for (auto i = begin(arr);
i != end(arr) && j != arrv.end();++i, ++j)
{
        *i = *j;
        cout << *i << " ";
}
cout << endl;

3.6 多维数组

1、多维数组的初始化
(1)理解方法:例

int ia=[3][4];      //大小为3的数组,每个元素为是含有四个整型的数组,3为行,4为列
int ia[10][20][30]; //大小为10的数组,它的每个元素均是大小为0的数组,这些数组的元素是含有30个整型的数组。
自右向左分别为列,行。高。

(2)初始化多维数组的每一行分别用花括号括起来
<1>例:

int ia[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};

2、多位数组的下标引用
<1>例:

//ia是大小为3的数组,每个元素都半酣4个整型的数组
//用arr的首元素为ia最后一行的最后一个元素赋值:
int ia[3][4]=a[0][0][0];
int (&row) [4]=ia[1];
//把row绑定到第二个4元素数组上。

3、使用范围for语句处理多维数组

int ia[3][4];
size_t cnt=0;
for(auto &row:ia)      //对于外层数组的每一个元素
    for(auto &col:row)      //对于内层数组的每一个元素
    {
        col=cnt;
        ++cnt;
    }

4、要使用范围for语句处理多维数组,除了最内层的循环之外,其他所有循环控制变量都应该是引用类型。

5、指针和多维数组
(1)多维数组实际为数组的数组,多维数组的组名为指向第一个内层数组的指针。
<1>例:

int ia[3][4];
int (*p) [4]=ia;        //指向含有4个整数的数组,ia为&ia[0]
p=&ia[2];   //p指向ia的尾元素

(2)下属声明,括号不可少

int *ip[4];         //整指针数组,元素为4个
int (*ip)[4];       //指向含有4个整型的数组的指针。

6、类型别名简化多维数组指针
(1)例:

int ia[3][4];
using int_array=int[4];
//等价于上面的声明: typedef int int_array[4];
for(int_array *p=ia;p!=ia+3;++p)
    {
    for(int *q=*p;q!=*q+4;++q)
        cout<<*q<<' ';
    cout<<endl;
    }


3.6 节练习
练习3.43:编写3个不同版本的程序,令其均能输出ia的元素。版本1使用范围for语句管理迭代过程;版本2和版本3都是用普通的for语句,其中版本2要求使用下标运算符,版本3要求用指针。此外,在所有3个版本的程序中都要直接写出数据类型,而不能使用类型别名、auto关键字或decltype关键字:

int ia[3][4];
size_t cnt = 0;
int j = 0;
for (auto & row : ia)
{
    int i = 0;
    for (auto &col : row)
    {

        col = cnt;
        ++cnt;
        cout<<"ia["<<j<<"]"<<"["<<i<<"]="
            <<col<<" ";
        ++i;
    }
    ++j;
    cout << endl;
}

//初始化二维数组

版本1for (int(&p)[4] : ia)
{
    for (const int(&q) : p)
        cout << q << ' ';
cout << endl;
}

版本2for (size_t i = 0;i < 3;++i)
{
    for (size_t j = 0;j < 4;++j)
        cout << ia[i][j] << ' ';
    cout << endl;
}

版本3for (int(*p)[4] = ia;p != end(ia);++p)
{
    for (int *q = *p;q != end(*p);++q)
        cout << *q << ' ';
    cout << endl;
}

术语表总结

1、缓冲区溢出(buffer over flow):一种严重的程序故障,主要原因是视图通过一个越界的索引访问容器内容。

2、容器(container)是一种类型,其对象容纳了一组给定类型的对象。vector是一种容器类型。

3、difference_type:由string和vector定义的一种带符号整数类型,表示迭代器之间的距离。

4、实例化(instantiation):编译器生成一个指定的模版类或函数的过程。

5、ptr_diff_t:是cstddef头文件中定义的一种与机器实现有关的带符号整数类型,它的空间足够大,能够表示数组中任意两个指针之间的距离。

6、size_t:是cstddef头文件中定义的一种与极其实现有关的无符号整数类型,它的空间足够大,能够表示任意数组的大小

7、size_type:是string和vector定义的类型的名字,能存放任意string对象或vector对象的大小。在标准库中,size_type被定义为无符号类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值