文章目录
- 第6章 数组、指针与字符串
- 6.1 数组
- 6.2 指针
- 6.3 动态内存分配
- 6.4 用vector创建数组对象
- 6.5 深复制与浅复制
- 6.6 字符串
- 6.6.1 用字符数组存储和处理字符串
- 6.6.2 string类
- 6.6.2.1 初始化string对象
- 6.6.2.2 读取和输出string对象
- 6.6.2.3 getline读取一整行
- 6.6.2.4 范围for语句
- 6.6.2.5 empty函数判断string对象是否为空
- 6.6.2.6 size/length函数返回string对象的长度
- 6.6.2.7 erase函数删除子串
- 6.6.2.8 substr函数提取子串
- 6.6.2.9 find和rfind函数查找子串
- 6.6.2.10 insert函数插入字符串
- 6.6.2.11 replace函数代替子串
- 6.6.2.12 swap函数调换两个字符串
- 6.6.2.13 c_str函数转换成C风格字符串
- 6.6.2.14 string类的操作符
第6章 数组、指针与字符串
C++从C继承来的一个重要特性就是可以直接使用地址来访问内存,指针变量时实现这一特征的重要数据类型。应用指针,可以方便地处理连续存放的大量数据,以较低的代价实现函数间的大量数据共享,灵活地实现动态内存分配。
字符数据可以用来表示字符串,这是从C语言继承的有效方法,但是从面向对象的观点和安全性的角度来看,用字符数据表示的字符串有不足之处,因此标准C++类库中提供了string类,这是通过类库来扩展数据类型的一个很好的范例。
6.1 数组
数组是具有一定顺序关系的若干对象的集合体,组成数组的对象称为该数组的元素。
6.1.1 数组的声明与使用
数组类型声明的一般形式:
数据类型 标识符[常量表达式1][常量表达式2]...; //数组类型声明的一般形式
数组中同类型数据的类型由“数据类型
”给出,这个“数据类型
”可以是整形、浮点型等基本类型,也可以是结构体、类等用户自定义类型。
"标识符
"指定数组名称。
“[常量表达式1][常量表达式2]...
”称为数组的界,必须是在编译时就可求出的常量表达式,其值必须为正整数。有n个下标的数组称为n维数组。数组的下标的个数称为数组的维数。二维数组被当作一维数组的数组,更高维数组也如此。
如果发生了数组越界(使用数组时下标值超过了n),运行时有时会得到提示,有时会没有任何提示!
6.1.2 数组的存储与初始化
数组元素在内存中是顺序、连续存储的。
数组元素的初始化(声明数组时对部分或全部元素赋初值):
int a[3]={1, 1, 1};
int a[]={1, 1, 1}; //与上一行等价
float b[5]={1.0, 2.0, 3.0}; //部分初始化,不能间隔赋初值,元素个数必须给出
int c[][3]={1, 2, 3, 4, 5, 6}; //多维数组若给出全部初值,第一维可省略
int c[2][3]={{1, 2, 3}, {4, 5, 6}}; //与上一行等价
const float d[5]={1.0, 2.0 3.0}; //声明为常量的数组必须给定初值
6.1.3 数组作为函数参数
使用数组名传递参数时,传递的是地址。
实参数组的个数不应少于形参数组的个数。
如果在被调函数中对形参数组元素值进行改变,主调函数中实参数组的相应元素值也会改变。
6.1.4 对象数组
对象数组的元素是类的对象,具有数据成员和函数成员。
声明一个一维对象数组:
类名 数组名[常量表达式];
数组对象的初始化过程,实际上就是调用构造函数来对每一个元素对象进行初始化。如果在声明数组时给每一个数组元素指定初始值,在数组初始化过程中就会调用与形参类型相匹配的构造函数。例如Location a[2]={Location(1,2), Location(3,4)};
在执行时会调用与形参类型相匹配的构造函数分别初始化a[0]
和a[1]
。如果没有指定数组元素的初始值,就会调用默认构造函数,例如Location a[2]={Location(1,2)};
在执行时首先调用带形参的构造函数初始化a[0]
,然后调用默认构造函数初始化a[1]
。
如果需要建立某个类的对象数组,在设计类的构造函数时就要充分考虑到数组元素初始化时的需要:当各元素对象的初值要求为相同的值时,应该在类中定义默认构造函数;当各元素对象的初值要求为不同的值时,需要定义带形参(无默认值)的构造函数。
当一个数组中的元素对象被删除时,系统会调用析构函数来完成扫尾工作。
通过对象数组中元素对象访问公有成员:
数组名[下标表达式].成员名
6.2 指针
指针是C++从C中继承过来的重要数据类型,提供了一种较为直接的地址操作手段。动态内存分配和管理离不开指针。
6.2.1 内存空间的访问方式
具有静态生存期的变量在程序开始运行之前就被分配了内存空间。
具有动态生存期的变量是在程序运行时,遇到变量声明语句时被分配内存空间的。
有时使用变量名访问内存空间不够方便,或者根本没有变量名可用,这时就需要直接用地址来访问内存单元。
6.2.2 指针变量的声明
指针是一种数据类型。
指针变量用于存放内存单元地址。
声明指针的语法形式:
数据类型 *标识符 //声明指针
比如:
int *ptr; //定义一个指向int类型数据的指针变量
指针可以指向各种类型,包括基本类型、数组(数据元素)、函数、对象、指针。
6.2.3 与地址相关的运算“*
”和“&
”
“*
”称为指针运算符,也称解析,表示获取指针所指的变量的值,是一元操作符。
“&
”称为取地址运算符,用于得到一个对象的(存储单元)地址,是一元操作符。
“*
”和“&
”出现在声明语句中和执行语句中,其含义是不同的。作为一元运算符和二元运算符时含义也不同。
(1)在声明语句中的一元运算符“*
”和“&
”
一元运算符“*
”出现在声明语句中,在被声明的变量之前时,表示声明的是指针。
一元运算符“&
”出现在声明语句中,在被声明的变量之前时,表示声明的是引用。
(2)在非声明语句中的一元运算符“*
”和“&
”
一元运算符“*
”出现在执行语句中,或在声明语句的初始化表达式中时,表示访问指针所指对象的内容。
一元运算符“&
”出现在执行语句中,或在给变量赋值时出现在等号右边时,表示取对象的地址。
6.2.4 指针的赋值
定义指针后必须先赋值,然后才能引用。
两种赋值方法:
(1)定义指针的同时进行初始化赋值:
存储类型 数据类型 *指针名=初始地址; //定义指针的同时进行初始化赋值
(2)定义指针后,单独使用赋值语句:
指针名=地址; //定义指针后,单独使用的赋值语句
如果使用对象的地址作为指针的初值,或在赋值语句中将对象地址赋给指针变量,该对象必须在赋值之前就声明过,而且这个对象的类型应该和指针类型一致。
多个指针可指向同一个变量。
一个数组,可以用它的名称来直接表示它的起始地址。**数组名实际上就是一个不能被赋值的指针,即指针常量。**如:
int a[10]; //定义int型数组
int *ptr=a; //定义并初始化int指针
关于指针的类型,还应注意:
(1)可以声明指向常量的指针,此时所指对象的值不变,指针本身可变(可指向另外的对象):
int a;
const int *p1=&a; //p1是指向常量的指针
(2)可以声明指针类型的常量(指针常量),这时指针本身(的值,为地址)不能被改变:
int *const p2=&a; //p2是指针常量
(3)一般情况下,指针的值只能赋给相同类型的指针。但void类型的指针,可以存储任何类型的对象地址,任何类型的指针都可以赋值给void类型的指针变量。经过使用类型显示转换,通过void类型的指针便可以访问任何类型的数据。
void指针一般只在指针所指向的数据类型不确定时使用。
#include<iosteam>
using namespace std;
int main(){
//void voidObject; 错误,不能声明void类型的变量
void* pv; //对,可以声明void类型的指针
int i=5;
pv=&i; //void类型指针指向整型变量
int* pint=static_cast<int*>(pv); //void类型指针赋值给int类型指针
cout<<"*pint"<<*pint<<endl; //运行结果为*pint=5
return 0;
}
6.2.5 指针运算
指针与整数加减、指针自增自减都表示移动指针所指位置。
*(p1+n1)
可写作p1[n1]
,都表示p1
当前所指位置后方第n1
个数的内容。类似也有*(p1-n1)
,可写作p1[-n1]
。
慎用指针的算术运算:对指针进行算术运算时,一定要确保运算结果所指向的地址是程序中分配使用的地址。
两个相同类型的指针相等,表示指向的是同一地址。
不同类型的指针之间或指针与非0整数之间的关系运算是无意义的。
但指针变量可以和整数0作比较,0专用于表示空指针,即一个不指向任何有效地址的指针。
给一个指针变量赋值为0,表示该指针是一个空指针,不指向任何地址。
int *p; //声明一个int类型的指针p
p=0; //将p设置为空指针,不指向任何地址
除0以外,赋给指针变量的值不能是其他整数,必须是地址常量(如数组名)或地址变量。
空指针也可以用NULL
来表示。
int *p=NULL; //将int型的指针初始化为空指针
NULL是一个在很多头文件中都有定义的宏,被定义为0。
如果不便于用一个有效地址给一个指针变量赋初值,那么应当用0作为它的初值,从而避免指向不确定地址的指针出现。
6.2.6 用指针处理数组元素
比如对于:
int array[5]; //声明一个存放了5个int类型数的一维数组
数组名array
就是数组的首地址(第一个元素的地址),即array
和&array[0]
相同。数组中下表为i
的元素就是*(数组名+i)
,例如*array
就是数组元素array[0]
,*(array+3)
就是数组元素array[3]
。
把数组作为函数的形参,等价于把指向数组元素类型的指针作为形参。例如,下面三种写法出现在形参列表中是等价的:
void f(int p[]);
void f(int p[3]);
void f(int *p);
6.2.7 指针数组
指针数组:如果一个数组的每个元素都是指针变量,这个数组就是指针数组。指针数组的每一个元素都必须是同一类型的指针。
声明一维指针数组:
数据类型 *数组名[下标表达式]; //声明一维指针数组,指针数组的每个元素都是指针变量
其中,数组名是指针数组的名称,同时也是这个数组的首地址。
由于指针数组的每个元素都是一个指针,必须先赋值后引用,因此声明指针数组必须赋初值。
#include<iostream>
using namespace std;
int main(){
int line1[]={1, 0, 0};
int line2[]={0, 1, 0};
int line3[]={0, 0, 1};
//定义整型指针数组
int *pLine[3]={line1, line2, line3};
for(int i=0; i!=3; ++i){
for(int j=0; j!=3; ++j){
cout<<pLine[i][j]<<" ";
//上行中等价于*(pLine[i]+j)
}
cout<<endl;
}
return 0;
}
上例输出结果为矩阵:
1 0 0
0 1 0
0 0 1
6.2.8 用数组作为函数参数
如果以指针作为函数的形参,在调用时实参将值传递给形参,也就是使实参和形参指针变量指向同一个内存地址。这样在子函数运行过程中,通过形参指针对数据值的改变会影响实参指针所指向的数据值。
在C语言中,用指针作为函数的形参有三个作用:
(1)使形参和实参指向相同的内存空间,以达到参数双向传递的目的,即通过在被调函数中直接处理主调函数中的数据,来将函数的处理结果返回其调用者。C++中用引用实现这一功能(见第3章)。
(2)减少函数调用时数据传递的开销。C++中有时可用引用实现这一功能,有时还是需要用指针。
(3)通过指向函数的指针传递函数代码的首地址。
注意:如果函数体中不需要通过指针改变指针所指对象的内容,应在参数表中将其声明为指向常量的指针,这样使得常对象被取地址后也可作为该函数的参数。
在设计程序时,当某个函数中以指针或引用作为形参都可以达到同样的目的时,使用引用可以使程序的可读性更好些。
6.2.9 指针型函数
指针可以作为函数的返回值。
指针型函数:返回值为指针类型的函数。
使用指针型函数,最主要的目的是在函数结束时把大量的数据从被调函数返回到主调函数中。而通常非指针型函数调用结束后,只能返回一个变量或对象。
指针型函数的定义:
数据类型 *函数名(参数表){ //定义指针型函数
函数体 //返回值为指针类型
}
6.2.10 指向函数的指针(函数指针)
函数指针:专门用来存放函数代码首地址的变量。
调用函数的通常形式“函数名(参数表)
”的实质就是“函数代码首地址(参数表)
”。一旦函数指针指向了某个函数,它与函数名便具有同样的作用。可以像使用函数名一样使用指向函数的指针来调用函数。
声明函数指针:
数据类型 (*函数指针名)(形参表) //声明函数指针
函数指针在使用前要先赋值,使指针指向一个已经存在的函数代码的起始地址:
函数指针名=函数名;
6.2.11 对象指针
6.2.11.1 对象指针的一般概念
对象指针:用于存放对象地址的变量。
对象所占据的内存空间只是用于存放数据成员的,函数成员不在每一个对象中存储副本。
声明对象指针:
类名 *对象指针名;
例如:
Point *pointPtr; //声明Point类的对象指针变量pointPtr
Point p1; //声明Point类的对象p1
pointPtr=&p1; //将对象p1的地址赋给对象指针pointPtr
可以通过对象指针访问对象的成员,语法形式为:
对象指针名->成员名 //通过对象指针访问对象的成员
(*对象指针名).成员名 //上一行的等价形式
6.2.11.2 this指针
this指针是一个隐含于每一个**类的非静态成员函数(包括构造函数和析构函数)**中的特殊指针。
类的静态成员函数中没有this指针。
this指针用于指向正在被成员函数操作的对象。
this指针实际上是类成员函数的一个隐含参数。在调用类的成员函数时,目的对象的地址会自动作为this指针的值,传递给被调用的成员函数,这样被调函数就能够通过this指针来访问目的对象的数据成员。
this是一个指针常量,对于常成员函数,this同时又是一个指向常量的指针。
在成员函数中,可以使用*this
来标识正在调用该函数的对象。
当局部作用域中声明了与类成员同名的标识符时,对该标识符的直接引用代表的是局部作用域中所声明的标识符,这时,为了访问该类的成员,可以通过this指针(比如this->x
)。
6.2.11.3 指向类的非静态成员的指针
可将类的成员(变量、函数、对象等)的地址存放到一个指针变量中,这样,可以通过这些指针直接访问对象的成员。
声明指向类的成员的指针:
类型说明符 类名::*指针名; //声明指向数据成员的指针
类型说明符 (类名::*指针名)(参数表); //声明指向函数成员的指针
对指向类的成员的指针赋值:
指针名=&类名::数据成员名; //对数据成员指针赋值
指针名=&类名::函数成员名; //对函数成员指针赋值
通过指向类的成员的指针(以及对象指针)访问成员:
对象名.*类数据成员指针名 //访问类的数据成员
对象指针名->*类数据成员指针名 //访问类的数据成员
(对象名.*类成员函数指针名)(参数表) //访问类的函数成员
(对象指针名->*类成员函数指针名)(参数表) //访问类的函数成员
例子:
int main(){
Point a(4,5);
Point *p1=&a; //定义对象指针并初始化
//定义成员函数指针并初始化
int (Point::*funcPtr)()const=&Point::getX;
//使用成员函数指针和对象名访问成员函数
cout<<(a.*funcPtr)()<<endl;
//使用成员函数指针和对象指针访问成员函数
cout<<(p1->*funcPtr)()<<endl;
//使用对象名访问成员函数
cout<<a.getX<<endl;
//使用对象指针访问成员函数
cout<<p1->getX()<<endl;
return 0;
}
6.2.11.4 指向类的静态成员的指针
对类的静态成员的访问是不依赖于对象的。因此,可以用普通的指针来指向和访问静态成员。
6.3 动态内存分配
动态内存分配可以使程序在运行过程中按照实际需要申请适量的内存,使用结束后还可以释放。申请和释放过程一般称为建立和删除。
堆对象:在程序运行过程申请和释放的存储单元。
建立堆对象:运算符new,其功能是动态内存分配,或者称为动态创建堆对象。
new 数据类型(初始化参数列表); //用new动态创建堆对象
上述语句在程序运行过程中申请分配用于存放指定类型数据的内存空间,并根据初始化参数列表中给出的值进行初始化。
如果内存申请成功,new运算便返回一个指向新分配内存首地址的类型的指针,可以通过这个指针访问堆对象;如果申请失败,会抛出异常(见第12章)。
用new创建对象或对象数组时要调用相应的构造函数。
删除堆对象:运算符delete,其功能是删除由new建立的对象,释放指针所指向的内存空间。
delete 指针名; //用delete删除由new建立的对象
用delete删除动态对象时,系统首先为该动态对象调用析构函数,再释放其内存。
注意:用new分配的内存,必须用delete加以释放,否则会导致动态分配的内存无法回收,使得程序占据的内存越来越大,这叫做“内存泄露”。
new运算符动态创建一维数组:
new 类型名[数组长度]; //用new动态创建一维数组
如果是用new建立的数组,用delete删除时在指针名前面要加“[]
”:
delete[] 指针名; //用delete加“[]”删除由new创建的一维数组
6.4 用vector创建数组对象
无论是静态数组,还是用new创建的动态数组,都难以检测下标越界的额错误。
C++标准库提供了被封装的动态数组——vector,这种被封装的数组可以具有各种类型。
vector是一个类模板,不是类。
用vector定义动态数组的形式:
vector<元素类型>数组对象名(数组长度); //用vector定义动态数组
尖括号中的类型名表示数组元素的类型。数组长度是一个表达式,表达式中可以包含变量。比如:
int x=10;
vector<int>arr(x); //定义一个大小为10的int型动态数组对象arr
用vector定义的数组对象的所有元素都会被初始化。另外,初值也可以自行指定,但只能为所有元素指定相同的初值:
vector<元素类型>数组对象名(数组长度,元素初值);
访问vector数组对象的元素:
数组对象名[下标表达式] //访问vector数组对象的元素
vector数组对象的名字表示的就是一个数组对象,而非数组的首地址,因为数组对象不是数组,而是封装了数组的对象。
vector的详细特性将在第10章介绍。
6.5 深复制与浅复制
隐含的复制构造函数完成的只是浅复制。
默认的复制构造函数将两个对象的对应数据简单复制后,pointsArray1的成员points和pointsArray2的成员points具有相同的值,也就是说两个指针指向的是同一内存地址,表面上好像完成了复制,但是并没有形成真正的副本。因此,当程序中移动pointsArray1中的点时,也影响到了pointsArray2。这种效果就是“浅复制”。
浅复制还有更大的弊病,在程序结束之前pointsArray1和pointsArray2的析构函数会自动被调用,动态分配的内存空间会被释放。由于两个对象共用了同一块内存空间,因此两次调用析构函数,将该内存空间释放了两次,于是导致运行错误。
解决“浅复制”问题的方法是编写复制构造函数,实现“深复制”。
6.6 字符串
C语言中用字符型数组来存放字符串,C++中依然可以沿用这一方法。不仅如此,C++标准库还预定义了string类。
6.6.1 用字符数组存储和处理字符串
字符串常量是用一对双引号括起来的字符序列。比如“abcd”,“This is a string.”。在内存中按串中字符的排列次序顺序存放,每个字符占一个字节,并在末尾添加空字符(null character)’\0’作为结尾标记。这实际上是一个隐含创建的类型为char的数组,一个字符串常量就表示这样一个数组的首地址。因此,可以把字符串常量赋给字符串指针,由于常量值是不能修改的,所以应将字符串常量赋给指向常量的指针。比如:
const char* STRING1="This is a string.";
这时,可以直接对STRING1进行输出:
cout<<STRING1;
如果创建一个char数组,每个元素存放字符串的一个字符,在末尾放置一个’\0’,便构成了C++字符串。它的存储方式与字符串常量无异,但由于是程序员创建的数组,因此可以改变其内容,因而这就是字符串变量了。
对字符数组进行初始化赋值时,初值的形式可以是以逗号分隔的ASCII码或字符常量,也可以是整体的字符串常量(这时末尾的’\0’是隐含的)。下面三种创建字符串变量的写法等价:
char str[8]={'p', 'r', 'o', 'g', 'r', 'a', 'm', '\0'}; //C风格字符串
char str[8]="program"; //字符串字面值,结尾不显示地加'\0'
char str[]="program"; //让编译器自动计数
**字符常量用单引号括起;字符串常量用双引号括起,用双引号括起的字符串隐式地包含结尾的空字符。**字符常量(如’S’)是字符串编码的简写表示,而”S”不是字符常量,它表示字符’S’和’\0’组成的字符串。
然而,用字符数组表示字符串后,执行很多字符串操作比较麻烦,需要借助cstring头文件中的字符串处理函数,比如strcpy函数(将一个字符串的内容复制到另一个字符串)、strcat函数(将两个字符串连接起来)、strcmp函数(按辞典顺序比较两个字符串的大小)。另外,当字符串长度很不确定时,需要用new来动态创建字符数组,最后还要用delete释放,这些都相当繁琐。
C++对这些繁琐的操作进行了封装,形成了string类,可以更加方便地操作字符串。
6.6.2 string类
使用string类必须包含头文件string(#include<string>
)。string定义在命名空间std中。
严格地说,string并非一个独立的类,而是类模板basic_string的一个特定化实例。不过对使用者来说,其特点与一个类无异。
(注:以下内容大多参考《C++ Primer(第5版)》及《面向对象程序设计——C++语言描述(原书第2版)》)
6.6.2.1 初始化string对象
string s1;
string s2(s1); //等价于string s2=s1;
string s3("value"); //s3是字面值"value"的副本,不用加'\0'
string s4(n, 'c'); //用连续n个字符c组成的字符串初始化s4
6.6.2.2 读取和输出string对象
用操作符>>
来输入string类型字符串会自动忽略开头的空格,并从第一个真正的字符开始读起,直到遇见下一处空格为止(不存储任何空格)。
操作符<<
可用来输出字符串。
比如:
string s;
cin>>s; //若输入是“ Hello World!”
cout<<s<<endl; //则输出是“Hello”
读取未知数量的string对象:
string word;
while(cin>>word) //反复读取,没遇到文件结束标记或非法输入
cout<<word<<endl; //逐个输出单词,每个单词后面紧跟一个换行
6.6.2.3 getline读取一整行
如果希望从键盘读入字符串,直到行末为止,不以中间的空格作为输入的分隔符,可以使用头文件string中定义的getline:
getline(cin, s2); //从键盘读入字符串并赋给s2,不以空格作为输入的分隔符
getline函数从输入流中读入字符,然后将它们存储到string变量中,直到出现以下情况为止:
(1)读入了文件结束标志;
(2)读到一个新行,该新行将从流中移除,但没有存储到变量中;
(3)到达字符串的最大允许长度(一般为4294967295)。
如果getline没有读入字符,它将返回false
,该条件可用于判断文件是否结束,以终止应用程序。
getline还允许在输入字符串时增加其他分隔符,方法是把作为分隔符的字符作为第3个参数传递给getline:
getline(cin, s2, ',');
6.6.2.4 范围for语句
范围for语句遍历给定序列中的每个元素并对序列中每个值执行操作。
for(declaration:expression)
statement
比如:
string str("some string");
for(auto c:str)
cout<< c <<endl; //对于str中每个字符,依次输出并紧跟一个换行符
将范围for语句中的循环变量定义为引用类型来改变string对象中字符的值:
string str("some string");
for(auto &c:str)
c=toupper(c); //若是小写,变为大写
cout<< c <<endl; //输出"SOME STRING"
6.6.2.5 empty函数判断string对象是否为空
根据string对象是否为空返回一个对应的布尔值。
比如,只输出非空的行:
while(getline(cin,line))
if(!line.empty()) //不为空,则执行
cout<<line<<endl;
6.6.2.6 size/length函数返回string对象的长度
size函数(或length函数)返回string对象的长度(字符的个数),返回的是一个无符号整型数。
比如,只输出超过80个字符的行:
while(getline(cin, line))
if(line.size>80)
cout<<line<<endl;
6.6.2.7 erase函数删除子串
第一个参数是需要删除的子串开始的位置,第二个参数是子串的长度。字符串第一个字符的索引为0。
如果不指明第二个参数,erase函数将删除从索引的指定开始位置到字符串末尾的所有字符。
如果两个参数都不指定,erase函数将删除所有的字符,最终得到一个空串。
string s="Ray Dennis Steckler";
s.erase(4,7);
cout<<s<<endl; //输出为"Ray Steckler"
6.6.2.8 substr函数提取子串
substr函数返回一个子串,第一个参数是子串首位字符的索引,第二个参数是子串的长度。
string s1="Ray Dannis Steckler";
string s2;
s2=s1.substr(4,6);
cout<<s1<<endl; //输出为"Ray Dannis Steckler"
cout<<s2<<endl; //输出为"Dannis"
6.6.2.9 find和rfind函数查找子串
find函数:
s1.find(s2, ind)
如果s2在s1中的位置大于或等于ind,则find返回大于或等于ind的索引值。如果没有找到s2,则find将返回无穷大(通常为429496795)。如果第二个参数没有给出,则其默认值为0。参数s2可以是一个string字符串、一个C风格字符串或一个char字符。
rfind函数:
rfind函数类似于find函数,只不过是返回小于或等于ind的索引值或为无穷大。若参数s2未给出,则默认为无穷大。
6.6.2.10 insert函数插入字符串
insert函数在某个string字符串的指定位置插入另一个字符串,第一个参数给出插入位置,第二个参数给出要插入的字符串。
6.6.2.11 replace函数代替子串
replace函数用一个指定的字符串来代替一个字串,第一个参数给出要代替的子串的首位字符的位置,第二个参数说明子串有多少个字符,第三个参数是要代替子串的字符串,第三个参数可以是string字符串或C风格的字符串。
如果第一个参数在范围内,用来替换的字符串长度是下述两个值中的最小值:第二个实参的值,字符串长度减去第一个参数的值。
6.6.2.12 swap函数调换两个字符串
s1.swap(s2); //实现字符串s1和s2互换
6.6.2.13 c_str函数转换成C风格字符串
C风格字符串:以NULL结束的char类型数组。
c_str()函数返回一个const的NULL结尾的char类型数组的指针。
string filename="infile.text";
ifstream infile;
infile.open(filename.ctr());
6.6.2.14 string类的操作符
注意:string类不支持“-
”操作符。
操作符 | 示例 | 注释 |
---|---|---|
+ | s+t | 连接成新串 |
= | s=t | 用t更新s |
+= | s+=t | 等价于s=s+t |
== | s==t | 判断s与t是否相等 |
!= | s!=t | 判断s与t是否不相等 |
< | s<t | 判断s是否小于t |
<= | s<=t | 判断s是否小于或等于t |
> | s>t | 判断s是否大于t |
>= | s>=t | 判断s是否大于或等于t |
[] | s[i] | 访问串中下标为i的字符串 |
注意:这里所说的对两个字符串的大小的比较,是根据字典顺序的比较(大小写敏感)。假设有两个字符串s1与s2,二者大小的比较规则如下:
(1)若两个string长度不同,但较短的和较长的对应位上字符相同,则短<长;
(2)若两个string某位置上字符开始相异,则实际比较第一个相异的字符。
之所以能通过上面的操作符来操作string对象,是因为string类对这些操作符进行了重载(第8章)。