基础知识
- C++ Standard Library headers − C++ 标准库
- C++ Programming − 这本书涵盖了 C++ 语言编程、软件交互设计、C++ 语言的现实生活应用
- C++ FAQ − C++ 常见问题
数据类型
基本数据类型
基本数据类型:布尔型bool、字符型char、整型int、浮点型float、双浮点型double、无类型void、宽字符型wchar_t(实际为short int)
一些基本类型可以使用一个或多个类型修饰符进行修饰
| 修饰符 | 描述 | 示例 |
|---|---|---|
signed |
有符号类型(默认) | signed int x = -10; |
unsigned |
无符号类型 | unsigned int y = 10; |
short |
短整型 | short int z = 100; |
long |
长整型 | long int a = 100000; |
const |
常量,值不可修改 | const int b = 5; |
volatile |
变量可能被意外修改(硬件、其他线程),禁止编译器优化 | volatile int c = 10; |
mutable |
类成员可以在 const 对象中修改 |
mutable int counter; |
- 默认情况下,int、short、long 都是带符号的,即 signed
- long int 8 个字节,int 都是 4 个字节
| 数据类型 | 描述 | 大小(字节) | 范围/取值示例 |
|---|---|---|---|
bool |
布尔类型,表示真或假 | 1 | true 或 false |
char |
字符类型,通常用于存储 ASCII 字符 | 1 | -128 到 127 或 0 到 255 |
signed char |
有符号字符类型 | 1 | -128 到 127 |
unsigned char |
无符号字符类型 | 1 | 0 到 255 |
wchar_t |
宽字符类型,用于存储 Unicode 字符 | 2 或 4 | 取决于平台 |
char16_t |
16 位 Unicode 字符类型(C++11 引入) | 2 | 0 到 65,535 |
char32_t |
32 位 Unicode 字符类型(C++11 引入) | 4 | 0 到 4,294,967,295 |
short |
短整型 | 2 | -32,768 到 32,767 |
unsigned short |
无符号短整型 | 2 | 0 到 65,535 |
int |
整型 | 4 | -2,147,483,648 到 2,147,483,647 |
unsigned int |
无符号整型 | 4 | 0 到 4,294,967,295 |
long |
长整型 | 4 或 8 | 取决于平台 |
unsigned long |
无符号长整型 | 4 或 8 | 取决于平台 |
long long |
长长整型(C++11 引入) | 8 | -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 |
unsigned long long |
无符号长长整型(C++11 引入) | 8 | 0 到 18,446,744,073,709,551,615 |
float |
单精度浮点数 | 4 | 约 ±3.4e±38(6-7 位有效数字) |
double |
双精度浮点数 | 8 | 约 ±1.7e±308(15 位有效数字) |
long double |
扩展精度浮点数 | 8、12 或 16 | 取决于平台 |
C++11 新增类型
| 数据类型 | 描述 | 示例 |
|---|---|---|
auto |
自动类型推断 | auto x = 10; |
decltype |
获取表达式的类型 | decltype(x) y = 20; |
nullptr |
空指针常量 | int* ptr = nullptr; |
std::initializer_list |
初始化列表类型 | std::initializer_list<int> list = {1, 2, 3}; |
std::tuple |
元组类型,可以存储多个不同类型的值 | std::tuple<int, float, char> t(1, 2.0, 'a'); |
派生数据类型
| 数据类型 | 描述 | 示例 |
|---|---|---|
数组 |
相同类型元素的集合 | int arr[5] = {1, 2, 3, 4, 5}; |
指针 |
存储变量内存地址的类型 | int* ptr = &x; |
引用 |
变量的别名 | int& ref = x; |
函数 |
函数类型,表示函数的签名 | int func(int a, int b); |
结构体 |
用户定义的数据类型,可以包含多个不同类型的成员 | struct Point { int x; int y; }; |
类 |
用户定义的数据类型,支持封装、继承和多态 | class MyClass { ... }; |
联合体 |
多个成员共享同一块内存 | union Data { int i; float f; }; |
枚举 |
用户定义的整数常量集合 | enum Color { RED, GREEN, BLUE }; |
类型别名
| 别名 | 描述 | 示例 |
|---|---|---|
typedef |
为现有类型定义别名 | typedef int MyInt; |
using |
为现有类型定义别名(C++11 引入) | using MyInt = int; |
标准库类型
| 数据类型 | 描述 | 示例 |
|---|---|---|
std::string |
字符串类型 | std::string s = "Hello"; |
std::vector |
动态数组 | std::vector<int> v = {1, 2, 3}; |
std::array |
固定大小数组(C++11 引入) | std::array<int, 3> a = {1, 2, 3}; |
std::pair |
存储两个值的容器 | std::pair<int, float> p(1, 2.0); |
std::map |
键值对容器 | std::map<int, std::string> m; |
std::set |
唯一值集合 | std::set<int> s = {1, 2, 3}; |
类型转换
类型转换是将一个数据类型的值转换为另一种数据类型的值
C++ 中有四种类型转换:静态转换、动态转换、常量转换和重新解释转换
静态转换(Static Cast)
- 静态转换是将一种数据类型的值强制转换为另一种数据类型的值
- 静态转换通常用于比较类型相似的对象之间的转换,例如将 int 类型转换为 float 类型
- 静态转换不进行任何运行时类型检查,因此可能会导致运行时错误
|
|
动态转换(Dynamic Cast)
- 动态转换(dynamic_cast)是 C++ 中用于在继承层次结构中进行向下转换(downcasting)的一种机制
- 动态转换通常用于将一个基类指针或引用转换为派生类指针或引用
- 动态转换在运行时进行类型检查。如果转换失败,对于指针类型会返回 nullptr,对于引用类型则会抛出 std::bad_cast 异常
|
|
- 目标类型:必须是指针或引用类型
- 表达式:需要转换的基类指针或引用
常量转换(Const Cast)
- 常量转换用于将 const 类型的对象转换为非 const 类型的对象
- 常量转换只能用于转换掉 const 属性,不能改变对象的类型
|
|
重新解释转换(Reinterpret Cast)
- 重新解释转换是将一个数据类型的值重新解释为另一个数据类型的值,通常用于在不同的数据类型之间进行转换
- 重新解释转换不进行任何类型检查,因此可能会导致未定义的行为
|
|
静态转换与重新解释转换核心区别
静态转换 (static_cast) |
重新解释转换 (reinterpret_cast) |
|
|---|---|---|
| 本质 | 编译器的语义转换(有规律的类型互转) | 底层二进制位的直接重新解释(“强行”解释) |
| 安全性 | 相对安全,编译期会做类型检查,若完全无关会报错 | 不安全,完全依赖程序员保证正确性,编译器不检查语义 |
| 常见用途 | 基本数据类型转换、有继承关系的指针/引用转换 | 无关类型指针转换、指针与整数之间的相互转换 |
| 是否改变数值 | 会改变底层数值(如 int 转 float 会重新计算存储形式) |
不改变二进制位,只是换个视角看待这串内存 |
变量
- 带有静态存储持续时间的变量(全局变量和静态变量)会被隐式初始化为 NULL(所有字节的值都是 0)
- 其他所有变量的初始值是未定义的(不确定的随机值)
|
|
extern 是一个关键字,用于在多个文件之间共享全局变量或函数。例如:在一个文件 a.cpp中定义了全局变量 int count = 5;,在另一个文件 b.cpp中想用它,不能直接写 int count;(那会变成重复定义),而要写 extern int count; 告诉编译器去别处找
常量
- 常量是固定值,在程序执行期间不会改变。这些固定的值,又叫做字面量
- 常量可以是任何的基本数据类型,可分为整型数字、浮点数字、字符、字符串和布尔值
- 常量就像是常规的变量,只不过常量的值在定义后不能进行修改
在 C++ 中,有两种方式定义常量:使用 #define 预处理器、使用 const 关键字
|
|
函数
|
|
- 单返回值:但可以通过结构体、
std::tuple、std::vector、引用传参实现多返回值效果 - C++ 默认使用值传递,函数内的代码不能改变用于调用函数的参数
- 引用传递和指针传递会改变实际参数
函数声明
函数声明会告诉编译器函数名称及如何调用函数
|
|
数组
|
|
- 默认值和上面变量一样
字符串
C++ 标准库提供了 string 类
指针
指针是一个变量,其值为另一个变量的内存地址(十六进制数)
|
|
引用
- 引用是变量的别名,是某个已存在变量的另一个名字
- 一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量
- 引用必须在定义时初始化,并且一旦绑定到一个变量后,就不能再绑定到其他变量
|
|
输入输出
C++ 的 I/O 发生在流中,流是字节序列
I/O 库头文件
| 头文件 | 描述 |
|---|---|
<iostream> |
标准输入流 cin、标准输出流 cout、…… |
<iomanip> |
参数化流 |
<fstream> |
文件流 |
cout
- cout 是 iostream 类的一个实例
- cout 对象"连接"到标准输出设备,通常是显示屏
- cout 是与流插入运算符
<<结合使用的
|
|
- C++ 编译器根据输出变量的数据类型,选择合适的流插入运算符来显示值
<<运算符被重载来输出内置类型(整型、浮点型、double 型、字符串和指针)的数据项- 流插入运算符
<<在一个语句中可以多次使用 - endl 用于在行末添加一个换行符
cin
- cin 是 iostream 类的一个实例
- cin 对象附属到标准输入设备,通常是键盘
- cin 是与流提取运算符
>>结合使用的
|
|
- C++ 编译器根据要输入值的数据类型,选择合适的流提取运算符来提取值,并把它存储在给定的变量中
- 流提取运算符
>>在一个语句中可以多次使用
结构体
- 结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起
- 与类(class)类似,结构体允许定义成员变量、成员函数、访问权限(默认 public)
|
|
- 最后一个分号之前,可以指定一个或多个结构体变量,这是可选的
- 访问结构体的成员使用成员访问运算符
. - 结构体作为函数参数时要写
struct关键字 - struct 和 class 唯一的区别在于默认的访问权限,struct => public、class => private
异常处理
异常是程序在执行期间产生的问题
|
|
动态内存
C++ 程序中的内存分为两个部分:
- 栈:函数内部声明的所有变量都在栈上
- 堆:程序运行时动态分配
new 运算符可以分配堆内的内存,返回所分配的内存地址
delete 运算符用于删除 new 分配的内存
|
|
命名空间
命名空间用于区分不同库中相同名称的函数、类、变量等
- 调用带有命名空间的函数或变量,需要在前面加上命名空间的名称
- 使用 using namespace 就可以不用加命名空间的名称,该指令会告诉编译器后续的代码将使用指定的命名空间
|
|
using 指令也可以用来指定命名空间中的特定项目。例如,只使用 std 命名空间中的 cout 部分
|
|
.和::
| 符号 | 名字 | 左边是什么 | 能不能访问实例成员 | 能不能访问 static 静态 |
|---|---|---|---|---|
. |
点运算符 | 对象 / 实例 / 引用 | ✅实例成员 | ✅语法允许,但不推荐 |
:: |
作用域解析 | 类名、命名空间、全局 | ❌不能访问普通实例成员 | ✅静态成员、类外函数定义 |
预处理器
- 预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理
- 所有的预处理器指令都是以井号
#开头 - 预处理指令不以分号
;结尾
#define
#define 预处理指令用于创建符号常量,该符号常量通常称为宏
|
|
可以使用 #define 来定义一个带有参数的宏
|
|
条件编译
条件编译是有选择地对部分程序源代码进行编译
|
|
预定义宏
C++ 提供了下表所示的一些预定义宏:
| 宏 | 描述 |
|---|---|
__LINE__ |
这会在程序编译时包含当前行号。 |
__FILE__ |
这会在程序编译时包含当前文件名。 |
__DATE__ |
这会包含一个形式为 month/day/year 的字符串,它表示把源文件转换为目标代码的日期。 |
__TIME__ |
这会包含一个形式为 hour:minute:second 的字符串,它表示程序被编译的时间。 |
#include
#include指令告诉编译器在编译时将指定的头文件内容插入到当前文件中- 头文件通常包含函数声明、类定义、宏定义等内容
STL
C++ 标准模板库(Standard Template Library,STL)是一套 C++ 模板类和函数的集合,它提供了一系列通用的、可复用的算法和数据结构
<vector>向量⭐
vector是最常用的标准模板库(Standard Template Library,STL)容器之一vector本质上是一个可自动扩容的动态数组
|
|
<list>双向链表⭐
|
|
<forward_list> 是单向链表
<stack>栈
| 函数名称 | 功能描述 |
|---|---|
push() |
在栈顶添加一个元素 |
pop() |
移除栈顶元素 |
top() |
返回栈顶元素的引用,但不移除它 |
empty() |
检查栈是否为空 |
size() |
返回栈中元素的数量 |
<deque>双端队列⭐
| 函数名称 | 功能描述 |
|---|---|
deque() |
默认构造函数,创建一个空的 deque 容器 |
deque(size_type n) |
创建一个包含 n 个默认值元素的 deque 容器 |
deque(size_type n, const T& value) |
创建一个包含 n 个值为 value 的 deque 容器 |
deque(initializer_list<T> il) |
使用初始化列表 il 构造 deque 容器 |
operator= |
赋值操作符,赋值给 deque 容器 |
assign() |
用新值替换 deque 容器中的所有元素 |
at(size_type pos) |
返回 pos 位置的元素,并进行范围检查 |
operator[](size_type pos) |
返回 pos 位置的元素,不进行范围检查 |
front() |
返回第一个元素的引用 |
back() |
返回最后一个元素的引用 |
begin() |
返回指向第一个元素的迭代器 |
end() |
返回指向末尾元素后一位置的迭代器 |
rbegin() |
返回指向最后一个元素的逆向迭代器 |
rend() |
返回指向第一个元素之前位置的逆向迭代器 |
empty() |
检查容器是否为空 |
size() |
返回容器中的元素个数 |
max_size() |
返回容器可容纳的最大元素个数 |
clear() |
清除容器中的所有元素 |
insert(iterator pos, const T& value) |
在 pos 位置插入 value 元素 |
erase(iterator pos) |
移除 pos 位置的元素 |
push_back(const T& value) |
在容器末尾添加 value 元素 |
pop_back() |
移除容器末尾的元素 |
push_front(const T& value) |
在容器前端添加 value 元素 |
pop_front() |
移除容器前端的元素 |
resize(size_type count) |
调整容器大小为 count,多出部分用默认值填充 |
swap(deque& other) |
交换两个 deque 容器的内容 |
get_allocator() |
返回一个用于构造双端队列的分配器对象的副本 |
<queue> 单向队列
<priority_queue>优先队列⭐
默认是大顶堆,根元素值最大
|
|
| 函数名称 | 功能描述 |
|---|---|
push() |
向队列添加一个元素 |
pop() |
移除队列顶部的元素 |
top() |
返回队列顶部的元素,但不移除它 |
empty() |
检查栈是否为空 |
size() |
返回队列中的元素数量 |
<map>字典⭐
key 唯一且有序,按「键 key 的大小升序」,不是插入顺序;底层红黑树
|
|
<unordered_map>:key 唯一但无序
|
|
<set> 集合⭐
元素唯一且有序,按「键 key 的大小排序」,不是插入顺序;底层红黑树
| 函数名称 | 功能描述 |
|---|---|
insert(元素值) |
插入一个元素 |
erase(元素值) |
删除一个元素 |
find(元素值) |
查找一个元素 |
empty() |
检查容器是否为空 |
size() |
返回容器中元素的数量 |
<unordered_set>无序集合:元素唯一但无序,基于哈希表
<bitset>二进制位
|
|
<bitset>不支持动态调整大小,如果需要动态位集,可以考虑vector<bool>
<algorithm>
- C++ 标准库中的
<algorithm>头文件提供了一组用于操作容器(如数组、向量、列表等)的算法 - 这些算法包括排序、搜索、复制、比较等
<algorithm>头文件定义了一组模板函数,这些函数可以应用于任何类型的容器,只要容器支持迭代器- 这些算法通常接受两个或更多的迭代器作为参数,表示操作的起始和结束位置
|
|