当前位置: 首页 > news >正文

【学习笔记06】内存管理与智能指针学习总结

📝 说明:本文为C++学习笔记,AI辅助系统整理
📅 整理时间:2025年9-10月
🎯 课时:课时05 - 内存管理与智能指针
💡 目的:掌握C++内存管理核心技术和智能指针使用


📚 本文内容

  1. new/delete vs malloc/free
  2. 内存泄漏
  3. unique_ptr
  4. shared_ptr
  5. weak_ptr
  6. make_shared
  7. RAII
  8. 野指针和悬挂指针

本篇整理的知识点

课时05学习了C++内存管理的8个核心知识点:

  1. new/delete与malloc/free的区别
  2. 内存泄漏识别与预防
  3. unique_ptr独占指针
  4. shared_ptr共享指针
  5. weak_ptr弱引用
  6. make_shared的优点
  7. RAII资源管理模式
  8. 野指针和悬挂指针

知识点1:new/delete vs malloc/free

本质区别

典型示例

// malloc/free:C语言函数,只分配/释放字节
int* p1 = (int*)malloc(sizeof(int));
*p1 = 42;
free(p1);// new/delete:C++运算符,分配内存+调用构造/析构
int* p2 = new int(42);
delete p2;

对象创建对比

典型示例

class Student {
public:string name;int age;Student(string n, int a) : name(n), age(a) {cout << "构造函数被调用: " << name << endl;}~Student() {cout << "析构函数被调用: " << name << endl;}
};// new/delete(推荐)
Student* s1 = new Student("张三", 20);  // 自动调用构造函数
delete s1;                              // 自动调用析构函数// malloc/free(危险)
Student* s2 = (Student*)malloc(sizeof(Student));
// s2->name 崩溃!对象未构造
free(s2);  // 不会调用析构函数

六大关键区别

特性new/deletemalloc/free
本质C++运算符C库函数
构造/析构✅ 自动调用❌ 不调用
返回类型具体类型指针void* 需要强转
失败处理抛出bad_alloc返回NULL
大小计算自动计算需要手动sizeof
重载✅ 可以重载❌ 不能重载

常见陷阱

// 陷阱1:混用导致崩溃
int* p = new int(10);
free(p);  // ❌ 错误!new配delete// 陷阱2:数组分配释放不匹配
int* arr = new int[10];
delete arr;     // ❌ 错误!应该用delete[]
delete[] arr;   // ✅ 正确// 陷阱3:malloc分配对象后不构造
Student* s = (Student*)malloc(sizeof(Student));
s->name = "李四";  // ❌ 崩溃!对象未构造

我的理解:C++里必须用new/delete,不要用malloc/free。new不仅分配内存,还会调用构造函数初始化对象。


知识点2:内存泄漏

五种常见泄漏场景

// 场景1:忘记释放
void leak1() {int* p = new int(10);// 忘记delete p;
}// 场景2:异常路径没释放
void leak2() {int* p = new int(10);if (条件) throw exception();  // 异常抛出,delete不会执行delete p;
}// 场景3:容器存指针不释放
vector<int*> vec;
vec.push_back(new int(10));  // 析构时只删vector,不删指针内存// 场景4:循环引用(智能指针)
struct A { shared_ptr<B> b; };
struct B { shared_ptr<A> a; };  // 互相引用,计数永不为0// 场景5:重复赋值前未释放
int* p = new int(10);
p = new int(20);  // 前一个内存泄漏

预防方法

// 1. 使用智能指针
unique_ptr<int> p1 = make_unique<int>(42);  // 自动释放// 2. RAII原则
class FileGuard {FILE* fp;
public:FileGuard(const char* path) { fp = fopen(path, "r"); }~FileGuard() { if (fp) fclose(fp); }  // 自动关闭
};// 3. 工具检测
// Valgrind、AddressSanitizer

知识点3:unique_ptr

核心特性:独占所有权

典型示例

#include <memory>// 创建
unique_ptr<int> p1 = make_unique<int>(42);// ❌ 不能复制
unique_ptr<int> p2 = p1;  // 编译错误// ✅ 可以移动
unique_ptr<int> p3 = std::move(p1);  // p1变nullptrcout << *p3 << endl;  // 42
// 自动释放

自定义删除器

// 与C接口协作
struct FileCloser {void operator()(FILE* f) const {if (f) fclose(f);}
};using UniqueFile = unique_ptr<FILE, FileCloser>;UniqueFile openFile(const char* path) {return UniqueFile(fopen(path, "rb"));
}

适用场景

  • 文件句柄管理
  • Socket连接管理
  • 独占资源管理
  • 工厂函数返回值

知识点4:shared_ptr

引用计数机制

shared_ptr
对象指针
控制块指针
引用计数
弱引用计数
删除器
拷贝: 计数+1
销毁: 计数-1
计数=0?
释放对象
保留对象

引用计数机制

典型示例

shared_ptr<int> p1 = make_shared<int>(42);
cout << p1.use_count() << endl;  // 1shared_ptr<int> p2 = p1;  // 引用计数+1
cout << p1.use_count() << endl;  // 2p1.reset();  // 计数-1
cout << p2.use_count() << endl;  // 1
// p2离开作用域,计数归0,自动释放

线程安全性

// ✅ 引用计数操作是线程安全的(原子操作)
shared_ptr<int> sp1 = make_shared<int>(42);
shared_ptr<int> sp2 = sp1;  // 计数+1,线程安全// ❌ 对象本身的读写不是线程安全的
*sp1 = 100;  // 修改对象,不是线程安全!需要加锁

控制块结构

shared_ptr包含两部分:

  1. 指向对象的指针
  2. 指向控制块的指针
    • 引用计数
    • 弱引用计数
    • 删除器
    • 分配器

知识点5:weak_ptr

解决循环引用

shared_ptr
shared_ptr
对象A
对象B
引用计数A=2
引用计数B=2
永不释放!

解决方案:用weak_ptr打破循环

shared_ptr
weak_ptr
对象A
对象B
引用计数A=1
引用计数B=1
正常释放!

循环引用问题

循环引用问题

struct B;
struct A {shared_ptr<B> b;~A() { cout << "A析构" << endl; }
};
struct B {shared_ptr<A> a;  // ❌ 循环引用~B() { cout << "B析构" << endl; }
};shared_ptr<A> pa = make_shared<A>();
shared_ptr<B> pb = make_shared<B>();
pa->b = pb;  // A持有B
pb->a = pa;  // B持有A,形成环
// 永不析构!

解决方案

struct B {weak_ptr<A> a;  // ✅ 用weak_ptr打破循环~B() { cout << "B析构" << endl; }
};

weak_ptr使用

weak_ptr<int> wp = sp;  // 不增加引用计数if (auto p = wp.lock()) {  // 转为shared_ptrcout << *p << endl;  // 使用对象
}if (wp.expired()) {  // 检查对象是否还存在cout << "对象已销毁" << endl;
}

知识点6:make_shared

为什么用make_shared?

原因1:性能优化(一次内存分配)

// 分两次分配
shared_ptr<int> p1(new int(42));
// 第1次:new int(42) 分配对象
// 第2次:shared_ptr分配控制块// 只分配一次
shared_ptr<int> p2 = make_shared<int>(42);
// 对象和控制块一起分配,更快

原因2:异常安全

// ❌ 可能泄漏
func(shared_ptr<int>(new int(42)), other_func());
// 如果other_func()抛异常,new int(42)可能泄漏// ✅ 安全
func(make_shared<int>(42), other_func());

知识点7:RAII

资源获取即初始化

RAII核心思想:

  • 构造时获取资源
  • 析构时释放资源
  • 利用对象生命周期自动管理

典型示例

class FileHandle {
private:FILE* file;
public:FileHandle(const char* path) {file = fopen(path, "r");  // 构造时打开}~FileHandle() {if (file) fclose(file);  // 析构时关闭}
};void process() {FileHandle fh("data.txt");// 使用文件...if (error) {return;  // 即使提前返回,也会调用析构关闭文件}
}  // 离开作用域,自动关闭

我的理解:RAII让资源管理变得自动化,不需要手动释放,也不怕异常导致泄漏。智能指针就是RAII的典型应用。


知识点8:野指针和悬挂指针

野指针

未初始化的指针,指向随机地址。

int* p;  // 野指针,未初始化
*p = 42;  // 崩溃!随机地址

解决

int* p = nullptr;  // 安全

悬挂指针

指向已释放内存的指针。

int* p = new int(42);
delete p;  // 释放内存
*p = 100;  // 悬挂指针,崩溃!

解决

delete p;
p = nullptr;  // delete后立即置nullptr

学习中的疑问与解答

Q1: new和malloc能混用吗?

A: 不能!new必须配delete,malloc必须配free。混用会导致崩溃或内存泄漏。

Q2: unique_ptr和shared_ptr怎么选?

A:

  • 资源只有一个所有者 → unique_ptr
  • 资源需要多个对象共享 → shared_ptr
  • unique_ptr性能更好,优先考虑

Q3: shared_ptr的线程安全是什么意思?

A:

  • ✅ 多个线程同时拷贝shared_ptr是安全的(引用计数是原子的)
  • ❌ 多个线程同时修改对象是不安全的(需要加锁)

Q4: weak_ptr什么时候用?

A: 主要用于解决shared_ptr的循环引用问题,比如父子关系、观察者模式。

Q5: 为什么推荐make_shared?

A:

  1. 性能更好(一次内存分配)
  2. 异常安全
  3. 代码更简洁

学习收获

  1. C++里必须用new/delete,不要用malloc/free
  2. 智能指针能自动管理内存,避免泄漏
  3. unique_ptr优先,shared_ptr按需,weak_ptr打破循环
  4. make_shared比new + shared_ptr更好
  5. RAII是C++资源管理的核心思想
  6. 指针用完要置nullptr,避免野指针和悬挂指针

内存管理是C++的核心,智能指针是现代C++的标准做法,必须熟练掌握。


💡 下一步:继续学习课时06(并发编程基础),掌握多线程编程。


📝 本篇整理完成 - 内存管理与智能指针知识体系

http://www.dtcms.com/a/456884.html

相关文章:

  • 0、FreeRTOS编码和命名规则
  • 无锡专业制作网站wordpress 手风琴
  • 通过camel AI创建多agent进行写作
  • qt常用控件
  • 离散化模板
  • linphone + minisipserver 下载和配置
  • 网站建设登录界面代码wordpress 按钮美化
  • 吴恩达机器学习课程(PyTorch 适配)学习笔记:3.4 强化学习
  • jEasyUI 自定义窗口工具栏
  • Spring Boot 和 MyBatis 环境下实现动态数据源切换
  • 2025 年度国产大模型「开源 vs. 闭源」深度评测与实战指南
  • 网页设计与网站建设电话香洲网站建设
  • 移动论坛网站模板免费下载revolution slider wordpress
  • TPM(Total Productive Maintenance)现场管理
  • 从 0 到 1 搭建 Python 语言 Web UI自动化测试学习系列 11 自动化测试--框架设计--基础知识 7--各种弹窗处理
  • ubuntu terminal 设置代理
  • 《传感器与检测技术》第 2 章 电阻式传感器原理与应用
  • Linux中input子系统
  • 探索Linux:开源世界的钥匙
  • GitHub 热榜项目 - 日榜(2025-10-08)
  • 手写Function.prototype.bind:从原理到完整实现
  • 百度做网站的公司施工企业的施工现场消防安全责任人应是
  • 做电商网站报价网站开发工程师需要会写什么
  • (3)容器布局进阶:Spacer、Divider、Frame 与 Alignment
  • 墨西哥证券交易所(BMV)等多个交易所股票数据API对接文档
  • 【数据分析与可视化】2025年一季度金融业主要行业资产、负债、权益结构与增速对比
  • app网站建设阿里巴巴卓拙科技做网站吗
  • 乌苏市城乡建设局网站北京朝阳区邮政编码
  • 个人用云计算学习笔记 --18(NFS 服务器、iSCSI 服务器)
  • 智能制造——解读MES在各行业中的需求与解决方案【附全文阅读】