1. 数据类型
1.1 整型
整型变量分为:
- short(2字节)
- int(4字节)
- long(window:4字节, linux 4 字节(32位)8字节(64位))
- long long (8字节)
它们之间的区别在于占用的内存空间不同,表示的有效数字范围不同
1.2 sizeof关键字
作用:统计数据类型所占内存大小
用法: sizeof(数据类型/变量)
1.3 实型(浮点型)
浮点型分为两种:
- 单精度 float
- 双精度 double
两者的区别在于表示的有效数字范围不同。
| 数据类型 | 占用空间 | 有效数字范围 |
|---|---|---|
| float | 4字节 | 7位有效数字 |
| double | 8字节 | 15-16位有效数字 |
1 | float a = 3.131592654f; |
1.4 字符型
作用:字符型变量用于显示单个字符
语法:char ch = 'a';
注意:字符型变量要用单引号
注意:单引号内只能有一个字符
- C 和 C++ 中字符类型只占用1个字节
- 字符型变量并不是把字符本身存在内存中,而是将该字符的ASCII 编码放到存储单元中。
1.5 字符串型
作用:用于表示一串字符
两种风格:
C风格字符串:
char 变量名[] = "字符串值"实例:
1
2
3
4int main(){
char str1[] = "hello world";
return 0;
}C++ 风格字符串:
string 变量名 = "字符串值"实例:
1
2
3
4int main(){
string str = "hello world";
return 0;
}注意:C++ 风格字符串,需要加入头文件#include<string>
1.6 布尔类型bool
作用:代表真或假
C++ 中,非0即为真
类型大小:占用1字节大小
1.4 数据的输入
作用:用于从键盘获取数据
关键字:cin
语法:cin >> 变量
实例:
1 | int main(){ |
2. 程序流程结构
C/C++ 支持最基本的三种程序运行结构:顺序结构,选择结构,循环结构。
- 顺序结构:程序按顺序执行,不发生跳转
- 选择结构:依据条件是否满足,有选择的执行相应功能
- 循环结构:依据条件是否满足,循环多次执行某段代码
3. 数组
3.1 概述
所谓数组,就是一个集合,里面存放了相同类型的数据元素
特点:
- 数组中的每个数据元素都是相同的数据类型
- 数组是由连续的内存位置组成的
3.2 一维数组
3.2.1 一维数组定义方式
一维数组定义的三种方式:
数据类型 数组名[长度];数据类型 数组名[长度] = {值1, 值2 ……}数据类型 数组名[] = {值1, 值2 ……}
3.2.2 一维数组数组名
作用:
- 可以统计整个数组在内存中的长度
- 可以获取数组在内存中的首地址
3.3 二维数组
3.3.1 二维数组定义方式
二维数组定义的四种方式:
数据类型 数组名[行数][列数];数据类型 数组名[行数][列数] = { {数据1, 数据2}, {数据3, 数据4} };数据类型 数组名[行数][列数] = {数据1, 数据2, 数据3, 数据4};数据类型 数组名[][列数] = {数据1, 数据2, 数据3, 数据4};
3.3.2 二维数组数组名
作用:
- 查看二维数组所占内存空间
- 获取二维数组首地址
4. 函数
4.1 概述
作用:将一段经常使用的代码封装起来,减少代码重复性。
4.2 函数的定义
步骤:
- 返回值类型
- 函数名
- 参数列表
- 函数体
- return 表达式
语法:
1 | 返回值类型 函数名(参数列表) |
4.3 函数的调用
功能:使用定义好的函数
语法:函数名(参数)
4.4 值传递
- 所谓值传递,就是函数调用时实参将值传入给形参
- 值传递时,如果形参发生改变,并不会影响实参。
4.5 函数的常见样式
- 无参无返
- 有参无返
- 有参有返
- 无参有返
4.6 函数的声明
作用:告诉编译器函数名称以及如何调用函数。函数的实际主体可以单独定义。
注意:函数的声明可以多次,但函数的定义只能有一次。
4.7 函数的分文件编写
作用:让代码结构更加清晰
函数分文件编写一般分为4个步骤:
- 创建后缀名为 .h 的头文件
- 创建后缀名为 .cpp 的源文件
- 在头文件中编写函数的声明
- 在源文件中编写函数的定义
5. 指针
5.1 指针的基本概念
指针的作用:可以通过指针间接访问内存
可以利用指针变量来保存地址
5.2 指针变量的定义与使用
指针变量定义:数据类型 * 变量名
示例:
1 | int a = 10; |
5.3 指针所占内存空间
指针实际只是用来存放地址,指针类型变量所占的内存空间跟操作系统有关:
- 32位系统,无论什么类型的指针变量,都占4个字节
- 63位系统,无论什么类型的指针变量,都占8个字节
5.4 空指针和野指针
空指针:指针变量指向内存中编号为0的空间
用途:初始化指针变量
注意:空指针指向的内存时不可访问的(0-255之间的内存编号时系统占用的,因此不可以访问)
示例:空指针
1 | int main(){ |
野指针:指针变量指向非法的内存空间
示例:野指针
1 | int main(){ |
总结:空指针和野指针都不是我们申请的内存空间,不要访问。
5.5 const 修饰指针
- const 修饰指针:常量指针
- const 修饰常量:指针常量
- cont 修饰指针和常量
常量指针:
特点:指针的指向可以修改,但指针指向的值不可以修改
指针常量:
特点:指针的指向不能修改,但指针指向的值可以修改
cont 修饰指针和常量
特点:指针的指向不能修改,指针指向的值也不可以修改
示例:
1 | int main() { |
5.6 指针和数组
作用:利用指针访问数组中元素
1 | int main() { |
5.7 指针和函数
作用:利用指针作为函数参数,可以修改实参的值
6. 结构体
6.1 结构体基本概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
6.2 结构的定义和使用
语法:struct 结构体名{结构体成员列表};
通过结构体创建变量的方式有三种:
- struct 结构体名 变量名
- struct 结构体名 变量名 = {成员1值, 成员2值}
- 定义结构体时顺便创建变量
总结:
- 定义结构体时的关键字是struct,不可省略
- 创建结构体变量时,关键字struct 可以省略
- 结构体变量利用操作符 “.” 来访问成员
6.3 结构体数组
作用:将自定义的结构体放到数组中,方便维护
语法:struct 结构体名 数组名[] = { {}, {}, {},……}
6.4 结构体指针
作用:通过指针访问结构体中的成员
- 利用操作符
->可以通过结构体指针访问结构体属性
示例:
1 | struct student{ |
6.5 结构体嵌套结构体
特征:结构体中的成员可以是另一个结构体
6.6 结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式有两种:
- 值传递(不修改实参数据)
- 地址传递(可修改实参数据)
6.7 结构体中const使用场景
作用:用const来防止误操作
示例:
1 |
|
7. 内存分区模型
C++程序执行时,将内存大方向划分为4个区域
- 代码区:存放函数体的二进制代码,由操作系统进行管理
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
内存四区意义:
不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程
7.1 程序运行前
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
代码区:
存放CPU执行的机器指令
代码区时共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区:
全局变量和静态变量存放在此
全局区还包含了常量区,字符串常量和其他常量也存放在此
该区域的数据在程序结束后由操作系统释放
7.2 程序运行后
栈区:
由编译器自动分配释放,存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
堆区:
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在C++中主要利用new在堆区开辟内存
7.3 new操作符
C++中利用 new 操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 delete
语法:new 数据类型
利用 new 创建的数据,会返回该数据对应的类型的指针
示例1:
1 | int* a = new int(10) |
示例2:开辟数组
1 | int* arr = new int[10] |
8. 引用
8.1 引用的基本使用
作用:给变量起别名
语法:数据类型 &别名 = 原名
8.2 引用注意事项
- 引用必须初始化
- 引用初始化后,不可以改变
实例:
1 | int a = 10; |
8.3 引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
实例:
1 |
|
8.4 引用的本质
本质:引用的本质在C++内部的实现是一个指针常量
实例:
1 |
|
8.5 常量引用
作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,繁殖形参改变实参
9. 函数的提高
9.1 函数默认参数
在C++中,函数的形参列表中的形参可以有默认值。
语法:返回值类型 函数名 (参数 = 默认值){}
注意:
- 一旦参数有默认参数,后面的参数都必须有默认参数
- 函数的声明与实现,只能有一个有默认参数
9.2 函数占位参数
C++中函数的形参列表中可以有占位参数,用来占位,调用函数时必须填补该位置
语法:返回值类型 函数名(数据类型){}
9.3 函数重载
9.3.1 函数重载概述
作用:函数名相同,提高复用性
函数重载满足条件:
- 同一作用域下
- 函数名称相同
- 函数参数类型不同 或者 个数不同 或者 顺序不同
注意:仅仅返回值类型不同,没办法作为函数重载的条件
10. 类和对象
C++面向对象的三大特性:封装,继承,多态
10.1 封装
10.1.1 封装的意义
封装是C++面向对象三大特性之一
意义:
- 将属性和行为作为一个整体,表现生活中的事物
- 将属性和行为加以权限控制
类在封装时,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:
- public 公告权限
- protected 保护权限
- private 私有权限
10.1.2 struct 和class区别
在C++中 struct 和 class 唯一的区别在于默认的访问权限不同
区别:
- struct 默认权限为公共
- class 默认权限为私有
示例:
1 |
|
10.1.3 成员属性设置为私有
优点:
- 将所有成员属性设置为私有,可以自己控制读写权限
- 对于写权限,我们可以检测数据的有效性
10.2 对象的初始化和清理
10.2.1 构造函数和析构函数
对象的初始化和清理是两个非常重要的安全问题
- 一个对象或者变量没有初始状态,对其使用后果是未知的
- 同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
C++利用了构造函数和析构函数解决上述问题,这个函数将会被编译器自动调用,完成对象初始化和清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,因此,如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数,但是空实现。
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法:类名(){}
- 构造函数,没有返回值,也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数语法:~类名(){}
- 析构函数,没有返回值也不写void
- 函数名称与类名相同,在名称前加上符号~
- 析构函数不能有参数,所以不能发生重载
- 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
10.2.2 构造函数的分类及调用
两种分类方式:
- 按参数分类:有参构造和无参构造
- 按类型分类:普通构造,拷贝构造
三种调用方式:
- 括号法
- 显示法
- 隐式转换法
1 |
|
10.2.3 拷贝构造函数调用时机
C++中拷贝构造函数调用时机通常有三种情况
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递方式给函数参数传值(将对象作为参数传入函数时,实际上拷贝了一份,作为形参)
- 以值方式返回局部对象(将对象作为返回值return时,外部实际接收到的是拷贝后的数据)
10.2.4 构造函数调用规则
默认情况下,C++编译器至少给一个类添加3个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
- 如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造
- 如果用户定义拷贝构造,C++不会再提供其他构造函数
10.2.5 深拷贝和浅拷贝
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
示例:
1 |
|
10.2.6 初始化列表
作用:C++提供了初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1), 属性2(值2)... {}
示例:
1 |
|
10.2.7 类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
示例:
1 | class A{} |
注意:
- 构造顺序:当其他类作为本类的成员时,构造时会先构造成员类,再构造本身
- 析构顺序:先析构自身,再析构成员
例如:上面例子中,构造B之前,会先构造A;析构时,先析构B,再析构A。
10.2.8 静态成员
静态成员就是在成员变量和成员函数前加上关键字 static,称为静态成员
静态成员分为:
- 静态成员变量
- 所有对象共享同一份数据
- 在编译阶段分配内存
- 类内声明,类外初始化
- 静态成员函数
- 所有成员共享同一个函数
- 静态成员函数只能访问静态成员变量
10.3 C++对象模型和this指针
10.3.1 成员变量和成员函数分开存储
在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
10.3.2 this 指针概念
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
问题:这一块代码如何区分哪个对象调用自己呢?
C++通过提供特殊的对象指针,this指针,解决上述问题。
this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针,
this指针不需要定义,直接使用即可
this指针的用途
- 当形参和成员变量同名时,可用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用 return *this
10.3.3 空指针访问成员函数
C++中空指针也是可以调用成员函数的,但也要注意有没有用到this指针。
如果用到 this 指针,需要加以判断,保证代码的健壮性
10.3.4 const 修饰成员函数
常函数:
- 成员函数后加 const ,我们称这个函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字 mutable 后,在常函数中依旧可以修改
常对象:
- 声明对象前加 const 后,称该对象为常对象
- 常对象只能调用常函数
10.4 友元
在程序中,有一些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的关键字friend
友元的三种实现方式
- 全局函数做友元
- 类做友元
- 成员函数做友元
10.4.1 全局函数做友元
示例:
1 |
|
10.4.2 类做友元
1 | class Person{ |
10.4.3 成员函数做友元
1 | class Person{ |
10.5 运算符重载
运算符重载概念:对已有的运算符重新进行定义,赋予另一种功能,以适应不同的数据类型
10.5.1 加号运算符重载
1 |
|
10.6 继承
继承是面向对象三大特性之一
继承的好处:减少重复的代码
10.6.1 继承的基本语法
class A:public B{}
A类称为 子类,或者 派生类
B类称为 父类,或者 基类
派生类中的成员,包含:
从基类继承过来的,还有自己增加的成员
从基类继承过来的表现其共性,新增的成员体现其个性。
10.6.2 继承方式
继承的语法:class 子类:继承方式 父类
继承的方式:
- 公共继承 public
- 父类中 public属性 在子类中还是 public
- 父类中 protected属性,在子类中还是 protected
- 父类中 private属性,子类无法访问
- 保护继承 protected
- 父类中 public 属性,在子类中 变为 protected
- 父类中 protected 属性, 在子类中 还是protected
- 父类中 private属性,子类无法访问
- 私有继承 private
- 父类中 public 属性,在子类中 变为 private
- 父类中 protected 属性, 在子类中 变为 private
- 父类中 private属性,子类无法访问
10.6.3 继承中的对象模型
子类继承父类,父类的属性子类都会继承,但是私有属性无法访问罢了,子类还是会占用相应的内存空间。
例如:父类有一个 private 属性的 int m_A, 子类无法访问该属性,但是子类依旧需要开辟4个字节的空间来存储这个属性。
10.6.4 继承中构造和析构顺序
继承中,先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反。
10.6.5 继承同名成员处理方式
在子类与父类出现同名的成员,如何通过子类对象,访问子类或者父类中那个同名的数据呢?
- 访问子类同名成员,直接访问即可
- 访问父类同名成员,需加作用域
示例:
1 |
|
总结:
- 子类对象可以直接访问到子类中同名成员
- 子类对象加作用域可以访问到父类同名成员
- 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名的成员函数,加作用域可以访问到父类中同名函数
10.6.6 继承同名静态成员处理方式
静态成员和非静态成员出现同名,处理方式一致
- 访问子类同名成员, 直接访问即可
- 访问父类同名成员,需要加作用域
10.6.7 多继承语法
C++允许一个类继承多个类
语法:class 子类 : 继承方式 父类A, 继承方式 父类B...
多继承可能会引发父类中有同名成员出现,需加作用域区分
C++实际开发中不建议使用多继承
10.6.8 菱形继承
概念:
A,B两个派生类继承同一个基类
C类同时继承A,B两个派生类
这种继承被称为菱形继承,或者钻石继承
总结:
- 菱形继承带来的问题:子类继承两份相同的数据,导致资源浪费
- 利用虚继承(virtual)可以解决菱形继承问题
10.7 多态
10.7.1 多态的基本概念
多态是C++面向对象的三大特性之一
多态分为两类:
- 静态多态:函数重载 和 运算符重载 属于静态多态,复用函数名
- 动态多态:派生类 和 虚函数实现运行时多态
静态多态和动态多态区别:
- 静态多态的函数地址早绑定 – 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 – 运行阶段确定函数地址
总结:
多态满足条件
- 有继承关系
- 子类重写父类中的虚函数
多态使用条件
- 父类指针或引用指向子类对象
重写:函数返回值类型 函数名 参数列表 完全一致 称为重写
10.7.2 纯虚函数和抽象类
在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
语法:virtual 返回值类型 函数名(参数列表) = 0
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:
- 无法实例化对象
- 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
10.7.3 虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那父类指针在释放时无法调用到子类的析构代码
解决方法:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
- 可以解决父类指针释放子类对象
- 都需要有具体的函数实现
区别:
- 如果时纯虚构,该类属于抽象类,无法实例化对象
虚析构语法:
·virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0
类名::~类名(){}
11. 文件操作
程序运行时的数据都是临时数据,一旦运行结束都会被释放。
文件可以将数据持久化
C++中队文件操作需要包含头文件
文件分类:
- 文本文件:文件以文本的ASCII码形式存储在计算机中
- 二进制文件:文件以文本的二进制形式存储在计算机中,用户无法直接阅读
操作文件:
- ofstream:写操作
- ifstream:读操作
- fstream:读写操作
11.1 文本文件
11.1.1写文件
写文件步骤:
- 包含头文件
- 创建流对象
- 打开文件
- 写数据
- 关闭文件
1 |
|
文件打开方式:
| 打开方式 | 解释 |
|---|---|
| ios::in | 为读文件而打开文件 |
| ios::out | 为写文件而打开文件 |
| ios: ate | 初始位置:文件尾 |
| ios::app | 追加方式写文件 |
| ios::trunc | 如果文件存在先删除,再创建 |
| ios::binary | 二进制方式 |
文件打开方式可以配合使用,利用|操作符
例如ios::binary |ios::out
利用二进制方式写文件
示例:
1 |
|
11.1.2 读文件
读文件步骤:
- 包含头文件
- 创建流对象 :
ifstream ifs; - 打开文件,并判断文件是否打开成功
- 读数据
- 关闭文件
利用is_open函数可以判断文件是否打开成功
11.2 二进制文件
以二进制的方式对文件进行读写操作
打开方式要指定为ios::binary
10.2.1 写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型:ostream& write(const char * buffer, int len)
参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数
10.2.2 读文件
文件输入流对象 可以通过read函数, 以二进制方式读数据
12. 模板
12.1 模板的概念
模板就是建立通用磨具,大大提高复用性
12.2 函数模板
- C++另一种编程思想称为泛型编程,主要利用的技术就是模板
- C++提供两种模板机制:函数模板 和 类模板
12.2.1 函数模板语法
函数模板作用:
建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代表。
语法:
1 | template<typename T> |
解释:
template – 声明创建模板
typename – 表面其后面的符号是一种数据类型,可以用class代替
T – 通用的数据类型,名称可以代替换,通常为大写字母
1 | template<typename T> |
总结:
- 函数模板利用关键字 template
- 使用函数模板有两种方式:自动类型推导、显式指定类型
- 模板的目的是为了提高复用性,将类型参数化
12.2.2 函数模板注意事项
- 自动类型推导,必须推导出一致的数据类型 T ,才可以使用
- 模板必须要确定出 T 的数据类型,才可以使用
12.2.3 普通函数与函数模板的区别
普通函数与函数模板区别:
- 普通函数调用时可以发生自动类型转换(隐式类型转换)
- 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
- 如果利用显式指定类型的方式,可以发生隐式类型转换
12.2.4 普通函数与函数模板的调用规则
调用规则如下:
- 如果函数模板和普通函数都可以实现,优先调用普通函数
- 可以通过空模板函数参数列表来强制调用函数模板
- 函数模板也可以发生重载
- 如果函数模板可以产生更好的匹配,一下调用函数模板
12.3 类模板
12.3.1 类模板语法
类模板作用:
- 建立一个通用类,类中的成员 数据类型可以不具体指定,用一个虚拟的类型来代表。
语法:
1 | template<typename T> |
解释:
template – 声明创建模板
typename – 表明其后面的符号是一种数据类型,可以用calss代替
T — 通用的数据类型,名称可以替换,通常为大写字母
12.3.2 类模板与函数模板区别
类模板与函数模板区别主要有两点:
- 类模板没有自动类型推导的使用方式
- 类模板在模板参数列表中可以有默认参数
12.3.3 类模板中成员函数创建时机
类模板中成员函数和普通类中成员函数创建时机有区别:
- 普通类中成员函数一开始就可以创建
- 类模板中的成员函数在调用时才创建
13. STL初识
- 大多数情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
- 为了建立数据结构和算法的一套标准,诞生了 STL
13.2 STL 基本概念
- STL(standard Template Library,标准模板库)
- STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)
- 容器和算法之间通过迭代器进行无缝连接
- STL几乎所有的代码都采用模板类或者模板函数
13.3 STL六大组件
- 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
- 算法:各种常用算法,如sort,find、copy、for_each等
- 迭代器:扮演容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
- 空间适配器:负责空间的配置与管理
13.4 STL中容器、算法、迭代器
容器:
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组、链表、树、栈队列、集合、映射表等
这些容器分为序列式容器和关联式容器两种:
- 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
- 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系
算法:
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法
算法分为质变算法和非质变算法
- 质变算法:指运算过程中会更改区间内的元素内容。例如拷贝,替换,删除等
- 非质变算法:指运算过程中不会更改区间内的元素内容,例如:查找、计数、遍历、找极值等
迭代器:
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器
迭代器种类:
| 种类 | 功能 | 支持运算 |
|---|---|---|
| 输入迭代器 | 对数据的只读访问 | 只读,支持++、==、!= |
| 输出迭代器 | 对数据的只写访问 | 只写,支持++ |
| 前向迭代器 | 读写操作,并能向前推进迭代器 | 读写,支持++、==、!= |
| 双向迭代器 | 读写操作,并能向前和向后操作 | 读写,支持++、– |
| 随机访问迭代器 | 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 | 读写,支持++、–、[n]、-n、<、<=、>、>= |
常用:双向迭代器,随机访问迭代器
13.5 容器算法迭代器
STL中最常用的容器为Vertor,可以理解为数组
13.5.1 vector存放内置数据类型
容器:vector
算法:for_each
迭代器:vector<int>::iterator
示例:
1 |
|
14. STL- 常用容器
14.1 string容器
14.1.1 string 基本概念
本质:
string 是C++风格的字符串,而string本质上是一个类
string 和 char* 区别
- char* 是一个指针
- string 是一个类,类内部封装了 char*,管理这个字符串,是一个char* 型容器
特点:
string 类内部封装了很多成员方法
例如:查找find,拷贝copy,删除delete、替换replace、插入insert
string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
14.1.2 string构造函数
构造函数原型:
string();//创建一个空字符串string(const char* s);//使用字符串s初始化string(const string& str);//使用一个string对象初始化另一个string对象string(int n, char c);//使用n个字符c初始化