C++ 基础知识点整理
一、变量类型及其存储特性
【记忆口诀】三变量,作用域不同;生命周期各有别,存储位置要分清
1.1 静态局部变量、全局变量、局部变量的特点与使用场景
定义与特性:
- 静态局部变量:函数内定义,static修饰;生命周期为整个程序,仅初始化一次,存储在数据段
- 全局变量:函数外定义;生命周期为整个程序运行区间,程序中任何地方可访问,存储在数据段或BSS段
- 局部变量:函数内定义;作用域和生命周期仅限于函数体内,每次调用重新创建,存储在栈上
表格对比:
分类 | 局部变量 | 静态局部变量 | 全局变量 |
---|---|---|---|
作用域 | 当前函数或者代码块内 | 当前函数内部(外部不能访问) | 整个程序内 |
生命周期 | 每次进入函数创建,用完就没 | 程序一运行就存在,一直到程序结束 | 程序启动时创建,程序结束才销毁 |
初始化行为 | (不赋值则值为随机值,取决于栈区残留数据) | (仅初始化一次,基本类型默认值为0,存储在数据段) | (基本类型默认初始化为0,存储在数据段或BSS段) |
存储位置 | 栈区(stack),速度快但不持久 | 数据段或BSS段(非栈),可长期保存 | 数据段或BSS段,生命周期长 |
适合场景 | 做临时运算,比如循环里的变量、临时数组等 | 想在函数里"记住之前的值",如统计次数、递归深度控制等 | 整个程序都需要共享的变量,比如配置信息、缓冲区等 |
代码示例:
1 | // 静态局部变量示例 |
二、指针与引用
【记忆口诀】指针是地址变量,引用是变量别名;指针可变可空,引用必须初始化且不可变
2.1 指针和引用的区别
指针特性:
- 存储另一个变量的内存地址,使用时需解引用(*)访问目标值
- 可重新赋值指向其他对象,支持指针算术(如++)
- 可为空(nullptr),有独立内存空间(通常4或8字节)
- 需要手动管理动态内存
引用特性:
- 变量的别名,绑定后不能修改指向,且不能为空
- 使用时无需解引用,语法上更简洁
- 无独立内存空间,与目标变量共享内存
- 无需手动管理内存
核心区别:不存在指向空值的引用,但存在指向空值的指针
const修饰对比:
- 指针:
const int* p
(指向常量的指针)、int* const p
(指针常量)、const int* const p
(指向常量的指针常量) - 引用:没有const引用,但可以绑定到const对象(
const int&
)
使用场景:
- 指针:处理可能为NULL的情况(如可选参数)、需要改变指向的对象(如遍历链表)、动态内存分配、多级间接访问
- 引用:函数参数传递(避免拷贝开销)、必须绑定到有效对象的场景、链式调用、运算符重载
三、数据类型
【记忆口诀】整型长度有规范,short 16 int自然,long 32 long long 64
3.1 整型数据长度标准
C++整型数据长度规范:
- short 至少 16 位
- int 至少与 short 一样长
- long 至少 32 位,且至少与 int 一样长
- long long 至少 64 位,且至少与 long 一样长
在大多数系统中:
- short 为 16 位(2字节)
- int 为 32 位(4字节)
- long 为 32 位(4字节)
- long long 为 64 位(8字节)
无符号类型:
- 不存储负数值的整型,可以增大变量能够存储的最大值,数据长度不变
- int 被设置为自然长度,即计算机处理起来效率最高的长度
四、关键字解析
【记忆口诀】const只读,static控作用域,extern跨文件,define预处理替换
4.1 const关键字
主要作用:指定变量、指针、引用、成员函数等的不可修改性质
常见用法:
- 常量变量:声明常量,使变量的值不能被修改
- 常量指针(
const int* p
):指向常量的指针,不能通过指针修改对象的值 - 指针常量(
int* const p
):指针本身是常量,不能改变指针指向 - 常量成员函数:函数不会修改对象的成员变量(除mutable修饰的成员)
- 常量引用参数:函数不会修改传入的参数
4.2 static关键字
主要作用:控制变量和函数的生命周期、作用域以及访问权限
不同场景下的作用:
- 静态局部变量:生命周期延长至程序结束,作用域限于函数内部,仅首次初始化
- 静态全局变量/函数:限制作用域为当前文件,避免命名冲突
- 静态成员变量:属于类而非对象实例,所有对象共享同一份数据,必须在类外初始化
- 静态成员函数:不依赖于对象实例,无this指针,只能访问类的静态成员
4.3 extern关键字
主要作用:声明全局变量或函数,实现跨文件共享
常见用法:
- 变量声明:
extern int x;
(声明不分配内存,定义需在其他文件中) - C++与C混合编程:
extern "C" { /* C函数声明 */ }
- 防止重复定义:在头文件中用extern声明变量,源文件中定义
4.4 define和typedef的区别
define:
- 简单的字符串替换,无类型检查
- 编译预处理阶段起作用
- 不分配内存,多次使用多次替换
typedef:
- 有对应的数据类型,进行类型检查
- 编译、运行时起作用
- 在静态存储区分配空间,程序运行过程中内存中只有一个拷贝
4.5 define和inline的区别
define:
- 预编译时处理的宏,简单字符串替换,无类型检查
inline:
- 编译时将函数体直接插入调用处,减少函数调用开销
- 是一种特殊函数,进行类型检查
- 对编译器的请求,可能被拒绝
C++中inline编译限制:
- 不能存在任何形式的循环语句
- 不能存在过多的条件判断语句
- 函数体不能过于庞大
- 内联函数声明必须在调用语句之前
4.6 constexpr和const
const:表示"只读"的语义,可定义编译期常量或运行期常量
constexpr:表示"常量"的语义,只能定义编译期常量
关系:标记为constexpr的变量或成员函数自动成为const,但反之不成立
constexpr变量:必须使用常量初始化,如constexpr int MOD = 1000000007;
constexpr函数:函数的返回类型和所有形参类型都是字面值类型,C++14起允许函数体包含多条语句
五、内存管理
【记忆口诀】new/delete是运算符,调用构造与析构;malloc/free是函数,仅管内存不处理对象
5.1 new和malloc的区别
- new内存分配失败时抛出bac_alloc异常,不返回NULL;malloc失败时返回NULL
- new无需指定内存块大小;malloc需要显式指出所需内存尺寸
- operator new/operator delete可重载;malloc/free不可重载
- new/delete会调用对象的构造函数/析构函数;malloc不会
- malloc/free是C++/C标准库函数;new/delete是C++运算符
- new从自由存储区分配内存;malloc从堆上分配内存
5.2 std::atomic
作用:提供原子操作,解决多线程中的数据竞争问题
背景:看似原子的操作(如a++、int a = b)在汇编级别实际上是多条指令,在多线程环境下可能导致不确定结果
使用示例:
1 | std::atomic<int> value; |
注意:C++11起,局部静态变量的初始化是线程安全的
六、函数与操作符
【记忆口诀】函数指针指向函数,指针函数返回指针;前置++效率高,后置++需临时对象
6.1 函数指针
定义:指向函数的指针变量,用于存储函数的地址,允许在运行时动态选择调用的函数
声明形式:返回类型 (*指针变量名)(参数列表)
使用场景:
- 回调函数
- 函数指针数组(实现状态机)
- 动态加载库
- 多态实现
- 函数指针作为参数
示例:
1 | int add(int a, int b) { return a + b; } |
6.2 函数指针和指针函数的区别
函数指针:指向函数的指针变量,如int (*ptr)(int, int)
指针函数:返回指针类型的函数,如int* getPointer()
6.3 前置++与后置++
前置++实现:返回引用,效率更高
1 | self &operator++() { |
后置++实现:返回临时对象,开销较大
1 | const self operator++(int) { |
后置++特点:
- 通过添加无意义的int占位参数区分
- 返回对象而非引用(因为临时对象会被销毁)
- 通常加const修饰防止连续调用(如i++++)
七、类与结构体
【记忆口诀】struct默认公有,class默认私有;继承方式也不同,struct默认公有继承
7.1 struct和Class的区别
相同点:
- 如果没有定义任何构造函数,编译器都会生成默认的无参数构造函数
不同点:
- struct成员默认是公有的(public);class成员默认是私有的(private)
- struct继承时默认使用公有继承;class继承时默认使用私有继承
- struct通常用于表示一组相关的数据;class通常用于表示封装了数据和操作的对象
八、类型转换
【记忆口诀】四种转换各不同,static_cast最常用;dynamic_cast带检查,reinterpret_cast最危险;const_cast去常量
8.1 C++强制类型转换
static_cast:
- 没有运行时类型检查,安全性取决于转换的合理性
- 用于基本数据类型之间的转换,上行转换(派生类→基类)安全
- 下行转换(基类→派生类)不安全
dynamic_cast:
- 进行下行转换时具有类型检查功能,更安全
- 要求转换类型是类的指针、引用或void*,基类必须有虚函数
- 转换失败时返回nullptr(指针)或抛出异常(引用)
reinterpret_cast:
- 可将指针转换为其他类型的指针,整型与指针互转
- 转换行为依赖平台实现,移植性差
const_cast:
- 将常量指针转换为非常量指针,常量引用转换为非常量引用
- 去掉类型的const或volatile属性
九、其他重要概念
【记忆口诀】nullptr是C++11关键字,类型安全指针空值;sizeof编译期计算,获取大小要记清
9.1 nullptr与NULL的区别
nullptr:
- C++11引入的关键字,表示特殊的空指针类型(
std::nullptr_t
) - 可隐式转换为任意指针类型,但不能转换为整数类型
- 类型安全,推荐使用
NULL:
- 宏定义,通常定义为
0
或(void*)0
- 本质是整数常量,可隐式转换为指针类型,但可能引发歧义
- 不推荐使用
9.2 sizeof操作符
作用:获取变量或类型所占用的字节数,编译时计算
语法:
sizeof(type)
:获取类型大小sizeof(expression)
:获取表达式结果类型的大小sizeof var
:获取变量大小(括号可选)
重要特性:
- 不执行表达式,仅分析类型
- 数组名作为参数时返回整个数组的大小,但在函数参数中数组退化为指针
- 空类大小为1字节(C++要求每个对象有唯一地址)
- 包含虚函数的类大小通常包含虚函数表指针
常见用途:
- 内存分配和释放
- 数组遍历
- 跨平台兼容性处理
- 模板元编程中的类型特征判断