高级核心
指针的深度理解
函数指针
含义:函数指针是一个指针变量,他存的不是数值,而是这个函数的地址
- 程序运行时,函数会加载到内存,有地址空间
- 函数指针就直指向这个地址的指针
- 函数指针可以把函数当作参数传递,动态调用函数
函数指针的定义格式
1.普通的使用
返回值类型 (*指针名)(参数列表);
int add(int a, int b) { return a+b; }
// 函数指针定义
int (*p)(int, int);
p = add; // 让指针 p 指向 add 函数
int res = p(1,2); // 等价 add(1,2)
2.函数指针作为参数使用
void print(int x) { cout << x << endl; }
void doSomething(int a, void (*cb)(int)) {
cb(a); // 调用传入的函数
}
int main() {
doSomething(100, print);
}
3.C++11的简化写法
using Func = int(*)(int,int);
//等同于
typedef int(*Func)(int,int);
4.lambda表达式
- 可以随时定义、就地使用的匿名函数,不用单独写函数名
[捕获列表] (参数) -> 返回值 { 函数体 }; 捕获列表 [=]:值捕获,外部变量只读 [&]:引用捕获,外部变量可修改 [x, &y]:x 值捕获,y 引用捕获 [this]:在类里捕获当前对象 auto add = [](int a, int b) { return a + b; }; cout << add(1,2); // 输出3
- lambda表达式的本质是仿函数 类里面重写了()运算符重载 因此可以向函数指针一样使用。
5.std::function可调用对象包装器
一个可调用对象包装器,能装:函数、函数指针、lambda、成员函数。
#include <functional> std::function<int(int, int)> func; func = [](int a,int b){return a+b;}; cout << func(1,2);
6.std::bind参数绑定器
用来绑定函数 + 参数,生成一个新的可调用对象。
占位符:std::placeholders::_1 表示第一个参数 绑定普通函数 void add(int a,int b) { cout<<a+b; } auto f = std::bind(add, 10, std::placeholders::_1); f(20); // 输出 30 绑定成员函数: std::bind(&类::函数, 对象指针/引用, 参数...);
指针数组
含义:一个数组,数组里的每个元素都是指针。
- 本质是数组
- 数组里存的不是普通值,是地址(指针)
int* arr[5]; 这就是: *一个大小为 5 的数组,每个元素都是 int 类型的指针。**
和数组指针区分:
指针数组:
int *arr[10];数组,里面是指针数组指针:
int (*arr)[10];指针,指向一个数组口诀: 括号优先是指针,无括号是数组。
二级指针
含义:二级指针就是:用来存放 一级指针地址 的指针。
- 一级指针:
int *p→ 存放变量地址- 二级指针:
int **pp→ 存放一级指针的地址
void*
定义:void *是可以指向任意类型数据的通用指针
内存四区
栈
含义:由系统自动管理,自动分配、自动释放
存放:局部变量、函数参数、函数调用
堆
手动申请、手动释放
存放 :
new/malloc出来的动态数据
数据段
存放全局数据、静态数据****程序整个运行期间都存在
分为:
已初始化数据:全局变量、static 变量
未初始化数据(BSS):未赋值的全局变量
代码段
存放程序代码、二进制指令
只读,不能修改
函数名、代码地址都在这里
结构体、内存对齐、位域
结构体
定义:结构体是用户自定义的复合数据类型,把多个不同类型的变量打包在一起。
struct Student {
int id;
char name[10];
float score;
};
- 可以存不同类型数据
- 默认权限 public
- 占用内存 = 所有成员总和 + 内存对齐填充
内存对齐
目的:为了让 CPU 读取更快,提高访问效率。
三大对齐规则:
第一个成员从偏移 0 开始
每个成员的起始地址 = 自身大小的整数倍
结构体总大小 = 最大成员大小的整数倍
举一个例子:
struct A { char a; // 1字节 int b; // 4字节 short c; // 2字节 }; 计算: char a:1 字节 → 偏移 0 填充 3 字节(让 int 从 4 的倍数开始) int b:4 字节 → 偏移 4~7 short c:2 字节 → 偏移 8~9 总大小:10 → 补齐到最大成员 int (4) 的倍数 → 12 字节 答案:sizeof (A) = 12
位域
定义:把一个变量按 "二进制位" 来分配空间,节省内存。
格式:
struct Test {
int a : 3; // 占3位
int b : 4; // 占4位
int c : 5; // 占5位
};
特点 节省内存 单位是位(bit) 不能取地址 & 只能用于整型(char/short/int)
用途 网络协议包 硬件寄存器 存储空间极小的配置项
编译流程
预编译
处理以 # 开头的指令 展开 #include 头文件 处理条件编译 #if #ifdef #endif 删除注释
.c/.cpp ->.i
编译
把预处理后的代码 → 汇编代码 语法检查 词法分析 语义分析
.i -> .s
汇编
把汇编代码 → 二进制机器码 生成目标文件只翻译,不合并
.s -> .o
链接
把多个目标文件 + 库文件 → 可执行程序 合并代码段、数据段 解析函数地址 链接静态库 / 动态库 生成最终可执行文件
.o/.obj -> .exe
面向对象的深度理解
构造 / 析构函数、拷贝构造、移动构造(C++11)
1. 构造函数
- 作用 :创建对象时初始化成员变量
- 特点:与类同名,无返回值,可重载
- 分类:默认构造、有参构造、拷贝构造、移动构造
2. 析构函数
- 作用 :对象销毁时释放资源(如 new 的内存)
- 格式 :
~类名(){} - 特点:无参无返回值,一个类只有一个
- 重点 :有动态分配内存时,必须写析构!
3. 拷贝构造函数(深拷贝 / 浅拷贝必考)
- 作用 :用一个已有对象初始化新对象
- 格式 :
类名(const 类名& other) - 浅拷贝 :只拷贝地址,会导致重复释放内存
- 深拷贝:重新开辟内存,拷贝内容,安全!
4. 移动构造函数(C++11)
-
作用 :转移临时对象(右值)资源,避免拷贝,提高效率
-
格式 :
类名(类名&& other) -
特点:窃取资源,不拷贝,效率高
#include
class A {
public:
//构造函数
A(int a1,int* c1,int&d1):a(a1),d(d1){
c = new int(c1);
std::cout << "A 构造函数调用" << std::endl;
}
//析构函数
~A(){
if (c != nullptr) {
delete c;
std::cout << "A 析构函数调用" << std::endl;
}
}
//拷贝构造
A(const A& other):a(other.a),d(other.d) {
c = new int((other.c));
std::cout << "A 拷贝构造函数调用" << std::endl;
}
//赋值运算符重载
A& operator=(const A& other) {
if (&other != this) {
*c = other.c;
}
std::cout << "赋值运算符重载函数调用" << std::endl;
}
//移动构造函数
A(A&& other):a(other.a), d(other.d) {
delete c;
c = other.c;
other.c = nullptr;
std::cout << "/移动构造函数调用" << std::endl;
}
private:
const int a;
static int b;
int c = nullptr;
int& d;
};
int A::b = 0;
RAII 机制(C++ 核心思想)
RAll机制:资源获取即初始化 在创建对象前进行构造,销毁对象时进行析构。
核心思想:
- 利用栈对象自动销毁的特性,管理动态资源
- 在构造时获取资源
- 在析构时自动释放资源
- 保证不泄漏资源、不重复释放、异常安全
经典应用
unique_ptr
核心特点
独占所有权,同一时间只能有一个指针指向对象
禁止拷贝,不能赋值给另一个 unique_ptr
可以移动(move)
效率极高,和裸指针一样快
离开作用域自动 delete
#include<iostream>
template<typename T>
class my_unique {
T* _ptr;
// 禁止拷贝(重点)
my_unique(const my_unique&) = delete;
my_unique& operator=(const my_unique&) = delete;
public:
my_unique(T* p =nullptr):_ptr(p){}
my_unique() { delete _ptr; }
// 支持移动
my_unique(my_unique&& other) {
_ptr = other._ptr;
other._ptr = nullptr;
}
T* operator->() { return _ptr; }
T& operator*() { return *_ptr; }
};
shared_ptr
核心特点
共享所有权,多个指针指向同一个对象
内部有引用计数(reference count)
拷贝一次,计数 +1
销毁一次,计数 -1
计数变为 0 才真正释放对象
template<typename T>
class my_shared {
T* _ptr;
int* c;
public:
// 构造
my_shared(T* p = nullptr) : _ptr(p) {
if (_ptr) {
_count = new int(1);
}
}
// 拷贝构造(计数+1)
my_shared(const my_shared& other) {
_ptr = other._ptr;
_count = other._count;
if (_count) (*_count)++;
}
// 析构(计数-1,为0释放)
~my_shared() {
if (_count) {
(*_count)--;
if (*_count == 0) {
delete _ptr;
delete _count;
}
}
}
T* operator->() { return _ptr; }
T& operator*() { return *_ptr; }
int use_count() { return *_count; }
};
weak_ptr
核心特点
- 指向 shared_ptr 管理的对象
- 不增加引用计数
- 不能直接使用
*、->- 必须调用
lock()转为 shared_ptr 才能用- 解决 shared_ptr 循环引用导致内存泄漏
template <typename T>
class weak_ptr {
private:
T* _ptr;
int* _count;
public:
weak_ptr() : _ptr(nullptr), _count(nullptr) {}
// 从 shared_ptr 构造
weak_ptr(shared_ptr<T>& other) {
_ptr = other._ptr;
_count = other._count;
// 重点:不增加引用计数!
}
// 提升为 shared_ptr
shared_ptr<T> lock() {
return shared_ptr<T>(*this);
}
bool expired() {
return (_count == nullptr || *_count == 0);
}
};