文章目录
- 第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)一般情况下,指针的值只能赋给相同类