乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

内存管理最佳实践

定义与作用

现代 C++(C++11 及以后)的内存管理范式已经从"手动 new/delete"转变为"RAII + 智能指针优先":

// ❌ C++03 风格
int* p = new int(42);
// ... 忘记 delete → 泄漏

// ✅ 现代 C++ 风格
auto p = std::make_unique<int>(42);
// 离开作用域自动释放

核心原则

完整示例

示例一:从原始指针到智能指针的迁移

#include <iostream>
#include <memory>
#include <vector>
#include <string>

// ====== C++03 风格(危险) ======
struct StudentCpp03 {
    std::string name;
    int* scores;        // 裸指针——谁负责释放?
    int count;

    StudentCpp03(const std::string& n, int c)
        : name(n), scores(new int[c]), count(c) {}

    // 拷贝构造?拷贝赋值?析构?Rule of Three 常被忘记
    ~StudentCpp03() { delete[] scores; }
};

// ====== 现代 C++ 风格(安全) ======
struct StudentModern {
    std::string name;
    std::vector<int> scores;  // vector 自动管理内存

    StudentModern(std::string n, std::vector<int> s)
        : name(std::move(n)), scores(std::move(s)) {}

    // 零规则(Rule of Zero):编译器生成的拷贝/移动/析构全部正确
};

// ====== 需要动态分配时 ======
class StudentDatabase {
public:
    void add_student(std::string name, std::vector<int> scores) {
        students_.push_back(
            std::make_unique<StudentModern>(std::move(name), std::move(scores)));
    }

    void print_all() const {
        for (const auto& s : students_) {
            std::cout << "  " << s->name << ": ";
            for (int sc : s->scores) std::cout << sc << " ";
            std::cout << std::endl;
        }
    }

private:
    std::vector<std::unique_ptr<StudentModern>> students_;
};

int main() {
    StudentDatabase db;
    db.add_student("张三", {85, 92, 78});
    db.add_student("李四", {90, 95, 88});
    db.print_all();
    return 0;
}

预期输出:

  张三: 85 92 78 
  李四: 90 95 88 

示例二:选择决策流程

#include <iostream>
#include <memory>
#include <vector>

// 场景 1:独占所有权 → unique_ptr
std::unique_ptr<int> create_unique() {
    return std::make_unique<int>(42);
}

// 场景 2:共享所有权 → shared_ptr
void share_between_modules() {
    auto sp = std::make_shared<int>(100);
    std::vector<std::shared_ptr<int>> modules;
    modules.push_back(sp);  // 引用计数 = 2
    modules.push_back(sp);  // 引用计数 = 3
    std::cout << "shared count: " << sp.use_count() << std::endl;
}

// 场景 3:不拥有 → 裸指针
void print_value(const int* ptr) {
    if (ptr) std::cout << "value: " << *ptr << std::endl;
}

int main() {
    auto u = create_unique();
    print_value(u.get());  // 传递裸指针——不转移所有权

    share_between_modules();

    // 场景 4:容器代替手动数组
    // ❌ int* arr = new int[100];
    // ✅ std::vector<int> arr(100);
    std::vector<int> arr(100);
    std::cout << "vector size: " << arr.size() << std::endl;

    return 0;
}

预期输出:

value: 42
shared count: 3
vector size: 100

最佳实践速查表

建议说明
优先值语义能用栈对象不用堆对象
unique_ptr 是默认选择独占、零开销
避免裸 new/delete用 make_unique/make_shared
用容器代替手动数组std::vector、std::array
不拥有 → 裸指针或引用函数参数、观察者
Rule of Zero让编译器生成特殊成员函数

易错场景与面试考点

常见问题参考答案
何时用 unique_ptr 而非 shared_ptr?默认用 unique_ptr,只在真正需要共享所有权时用 shared_ptr
为什么避免 new?make_unique 异常安全、代码更简洁
Rule of Zero 是什么?如果不需要自定义析构/拷贝/移动,就不要写——交给编译器

小结

  • 现代 C++ 内存管理:RAII + 智能指针 + 容器
  • 默认选择:栈对象 > unique_ptr > shared_ptr
  • 永远不要写裸 new/delete
  • Rule of Zero:让编译器做事,减少出错
上一页
shared_ptr 与 weak_ptr