第四章:复合类型
本章的学习,涵盖指针、引用、数组、结构体、联合、枚举六大类c++基础知识,允许用户以类的形式定义自己的类型,以便描述实际应用中的各种对象。
标准库类型string
字符串
标准库类型vector
动态数组
标准文件流和文件读写
fstream和stringstream
(一)指针
内存地址:
程序运行时,代码和需要的数据都被存储在内存中
内存是有序的字节序列,每个字节都有唯一的地址,使用该地址可以确定字节的位置,用以存储和获取数据
直接访问和间接访问:
通过变量的名字直接访问为程序中定义的变量分配的内存单元,存取变量的值
使用变量的内存地址找到存放数据的单元,间接访问其中的内容
指针:
指针持有一个对象的地址,称为指针“指向”该对象
通过指针可以间接操纵它指向的对象
代码如下:
int ival = 120;
int pi = &ival;
// pi存放int变量ival的地址
// 或者说pi指向ival
char ch = ‘a’, pc = &ch;
// pc指向字符型变量ch
特别注意:
1、指向一个对象的指针有两个存储单元与之相关
2、一个是指针自己的存储单元,里面存放着所指对象的地址;另一个就是指针指向的对象的存储单元,里面存放该对象的值。
本章也引入了某个特殊类型的指针:
void指针
含义:可以持有任何类型的地址值,即通用指针
特点:
1、相关的值是个地址,但是该地址保存的对象类型不知道
2、不能操纵void指针指向的对象,只能传送该地址值或者和其他地址值进行比较
3、不允许void指针到其他类型指针的直接赋值
(二)动态内存管理
动态内存管理方法主要分为new和delete
new运算符
①new表达式的三种形式:
1、分配单个对象:new 类型 或者 new 类型(初始值)
2、分配多个连续存储的对象:new 类型[数组大小]
3、定位new,在指定位置分配空间:new (指针) 类型;
②new类型:
1、在堆上动态分配空间,创建对象,并返回对象的地址
2、一般将new返回的地址保存在指针变量中,以便间接访问堆上的对象。
int ip1 = new int;
//在堆上分配一个int类型的对象,返回它的地址
ip1 = 512;
//堆上分配的这个对象只能通过指针间接操作
int ip2 = new int(100);
//在堆上分配一个int对象,
//初始化为100,返回其地址
③new 类型[数组大小]:
1、在堆上分配指定类型和大小的数组(连续内存空间),返回数组首地址
2、不能对数组进行显式的初始化
3、数组大小不必是常量,是数组元素的个数,不是字节数
④new (指针) 类型;
1、定位new在指针指向的空间中创建一个指定类型的对象
2、程序员可以预先分配大量的内存,以后通过定位new表达式在这段内存中创建对象
3、使用定位new,必须包含标准库头文件
delete运算符:
堆上的空间在使用后必须释放,否则会造成内存泄漏,即动态内存空间使用后未回收,导致一直被占据。
①释放new分配的单个对象 delete 指针;
int* ip = new int;
… //不再使用这个int对象时,释放内存
delete ip;
//释放指针指向的int对象,将空间归还给动态存储区
②释放new分配的数组 delete[] 指针;
int* pa = new int[100];
… //不再使用这个数组时,释放内存
delete[] pa;
//释放指针pa指向的数组,将空间归还给动态存储区
特别注意:()
1、执行delete运算后,指针ip指向的空间被释放,不能再使用ip指向的内存,但是ip这个指针变量自己的存储空间不受影响
2、delete后的ip不是空指针,而是“空悬指针”,即指向不确定的单元
3、delete之后,继续通过ip间接使用这个单元是非法的,会引起不可预料的运行错误
(三)引用(左值引用)
“引用”在C++11中被改称为“左值引用”,因为C++11新增加了“右值引用”的概念
在不引起歧义的情况下,一般仍使用“引用”来指“左值引用”。
定义:引用由类型标识符和一个取地址符(&)来定义
type& refVariable = leftValue;
初始化:引用必须被初始化,初始值是一个有内存地址
(四)指向const对象的指针(非const )
const type cp; 或者type const cp;
cp是指向常量的指针,它所指向的内存中的内容不可以改变,即cp的值不能改变
指向非const对象的const指针
type const cp = initAddressValue;
cp是常量指针,初始化后值不能改变,指向固定的单元
(五)结构体
结构体把一组来自不同类型的数据组合在一起构成复合类型,其中的每个数据都是结构体的成员。
结构体由关键字struct定义,语法形式:
struct 结构体类型名{
成员声明;
};
结构体的成员不能独立使用,必须由结构体类型的变量通过成员选择运算符“.”来选择,或者由结构体类型的指针通过“->”运算符选择。
//定义结构体类型X
struct X {
char c;
int i;
float f;
double d;
};
(六)枚举
枚举类型定义了一组命名的整数常量,以提高代码的可读性
enum TrafficLight { red, green, yellow };
TrafficLight枚举类型定义了3个常量:0,1,2 分别和名字red,green以及yellow关联。
TrafficLight是一个枚举类型,可以用来定义枚举变量,变量的值只能是枚举成员。
另外补充的第七个要点–标准库类型string
string 表示可变长度的字符序列
字符串是对象
string 类支持字符串对象的各种操作
各种初始化方式
字符串之间的复制、比较、连接
查询字符串长度和判断字符串是否为空
访问字符串中的单个字符
使用string 类要包含头文件
第五章:函数
(一)函数基础
定义:函数可以被看作是一个由用户定义的操作,一般用一个名字表示,即函数名
,其操作数称为参数,由参数列表指定。
函数的结果称为返回值,其类型称为函数返回类型
函数执行的动作在函数体中指定
函数定义的语法形式为
返回类型 函数名(参数列表) { 函数体 }
例子:
//算法:n! = 1 * 2 * … * (n-1) * n
//函数名fact,返回类型int,一个int类型的形参n
int fact(int n)
{ //函数体开始
int ret = 1;
while ( n > 1 ) //求阶乘
ret *= n–;
return ret; //返回结果
} //函数体结束
函数的参数列表不能省略
如果函数没有任何参数,可以用空参数表或void参数表表示
参数列表由逗号分隔的形参类型列表构成,每个形参类型后跟一个可选的形参名字
int fact(int n)
{
int ret = 1;
while ( n > 1 )ret *= n–;
return ret;
}
//没有参数的函数
int foo(){…}
int foo(void){…}
int print(int value, int base)
{ … }
int exchange(int v1, int){ … }
//第二个参数没名字不能在函数体中使用
(二)参数传递
定义:参数传递是指用函数调用的实参来初始化函数形参存储区的过程
函数的形参是局部对象,仅在函数的作用域内可见
每次调用函数时,会创建形参变量,并用传入的实参初始化形参
形参的类型决定了实参初始化形参的方式
如果形参是引用类型,形参将绑定到对应的实参上;否则,将实参的值复制后赋给形参
传值,按值传递参数;当实参的值被复制给形参时,形参和实参是两个独立的对象,实参被称为按值传递,或传值调用函数
C++的默认参数传递方式
传指针:使用指针参数是传地址值
(三)作用域与存储类别
1、对象的生存期:是指程序执行过程中对象存在的时间
对象的生存期与对象的作用域和存储类别密切相关
名字的作用域:程序的一段区域,名字的作用域指的是该名字可以在哪些程序文本区使用。
对象的存储类别:创建对象时分配内存空间的方式和内存空间的类型
2、全局变量和全局函数:在全局作用域中可以定义函数和变量
在程序整个执行过程中都存在,可以在整个程序中使用
全局的内置类型变量,不指定初始值时被初始化为0值
全局变量和非inline 全局函数在程序中只能定义一次,在其他地方使用时需要声明
关键字extern 用来声明全局对象,可以在同一文件中或同一程序的不同文件中多次出现
(四)函数重载
如果要定义一组函数,对不同类型的数据执行同样的一般性动作,表达相同的概念,应该如何命名:
void print_int_range(const int *first, const int *last);
void print_int_array(const int ia[], size_t size);
void print_chars(const char *cp);
C++函数重载机制
多个函数可以共享同一个函数名,针对不同的参数类型提供不同的操作
重载函数
如果同一个作用域内的几个函数名字相同但形参列表不同,则它们是重载函数