第11章 内存与资源管理 内存管理基础 内存区域 C++程序运行时,内存通常分为以下几个区域,每个区域都有其特定的用途、管理方式和性能特点:
代码区(Text Segment) :
存储程序的可执行机器代码,通常是只读的,以防止程序意外修改自身指令 包含函数体的二进制指令,按函数地址顺序排列 支持内存保护,防止执行非代码区域的数据 在多进程环境中,相同程序的代码区可以共享,减少内存使用 现代CPU会对代码区进行指令缓存(I-Cache)优化,提高指令读取速度 代码区的大小影响程序的启动速度和内存占用 只读数据区(RODATA) :
存储字符串字面量、const修饰的全局变量和其他常量 同样是只读的,防止运行时修改 字符串字面量会被去重,相同的字符串只存储一份 例如:const char* str = "Hello" 中的 “Hello” 存储在此区域 位于较低地址空间,与代码区相邻,便于CPU缓存 全局/静态区(Data Segment) :
存储初始化的全局变量和静态变量 程序启动时分配,结束时释放 变量在编译时就确定了地址 例如:int globalVar = 42 和 static int staticVar = 100 存储在此区域 支持数据缓存(D-Cache),但全局变量可能导致缓存行竞争 未初始化数据区(BSS Segment) :
存储未初始化的全局变量和静态变量 程序启动时会被自动初始化为0 不占用可执行文件空间,仅在运行时分配内存 例如:int uninitGlobalVar; 和 static int uninitStaticVar; 存储在此区域 大小在链接时确定,加载时由操作系统清零 堆区(Heap) :
动态分配的内存,由程序员通过new/delete或malloc/free管理 分配和释放的顺序可以任意,形成自由内存块 空间较大,是程序动态内存的主要来源 内存分配器负责管理空闲内存块,处理分配和释放请求 可能产生内存碎片,影响内存利用率 现代内存分配器(如tcmalloc、jemalloc)采用多线程本地缓存和分级分配策略 栈区(Stack) :
存储函数调用时的局部变量、函数参数、返回地址和调用帧信息 由编译器自动管理,遵循后进先出(LIFO)原则 分配和释放速度极快,仅需修改栈指针(ESP/RSP寄存器) 空间有限,通常为几MB,超出会导致栈溢出 函数调用结束时,自动释放该函数的栈帧 栈内存对齐到16字节(x86-64),确保SSE指令的正确执行 内存映射区域(Memory-Mapped Region) :
用于映射文件、共享内存和动态库 通过mmap系统调用创建(POSIX) 支持零拷贝I/O,提高文件操作性能 可以设置不同的权限(读/写/执行) 内存布局示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 高地址 ┌─────────────────────────────────┐ │ 栈区 (Stack) │ ← 向下增长 ├─────────────────────────────────┤ │ 内存映射区域 │ ├─────────────────────────────────┤ │ 堆区 (Heap) │ ← 向上增长 ├─────────────────────────────────┤ │ 未初始化数据区 (BSS Segment) │ ├─────────────────────────────────┤ │ 初始化数据区 (Data Segment) │ ├─────────────────────────────────┤ │ 只读数据区 (RODATA) │ ├─────────────────────────────────┤ │ 代码区 (Text Segment) │ └─────────────────────────────────┘ 低地址
内存区域的硬件视角 :
缓存层次 :代码区和只读数据区优先进入L1指令缓存,全局数据进入L1数据缓存TLB优化 :频繁访问的内存区域会被TLB(Translation Lookaside Buffer)缓存,减少地址转换开销页大小 :现代系统使用4KB或更大的页大小(如2MB大页),减少页表开销NUMA架构 :在多核系统中,内存分配应考虑NUMA节点亲和性,减少跨节点访问延迟内存分配方式 C++中有三种基本内存分配方式,每种方式都有其特定的适用场景和性能特点:
静态分配 :
分配时机 :编译时由编译器分配存储位置 :数据段或BSS段生命周期 :程序启动到程序结束管理方式 :编译器自动管理,无需手动释放底层实现 :初始化的全局变量存储在数据段,在可执行文件中占用空间 未初始化的全局变量存储在BSS段,在可执行文件中不占用空间 链接器在链接时确定变量地址,生成符号表条目 优点 :分配速度快,无运行时开销 地址固定,访问速度快(直接地址访问) 无需手动管理,避免内存泄漏 支持常量折叠和编译期优化 缺点 :生命周期固定,无法在运行时调整 空间有限,受限于数据段大小 全局可见,可能导致命名冲突 多线程环境下可能导致缓存行竞争 适用场景 :存储程序运行期间始终需要的数据 配置参数和全局状态 常量和只读数据 线程安全的全局对象(如单例模式) 栈分配 :
分配时机 :函数调用时由编译器自动分配存储位置 :栈区生命周期 :函数调用开始到函数返回管理方式 :编译器自动管理,函数返回时自动释放底层实现 :函数调用时,栈指针(ESP/RSP)向下移动,分配栈帧 栈帧包含返回地址、前栈指针、函数参数、局部变量 函数返回时,栈指针向上移动,自动释放栈帧 栈帧大小在编译时确定,运行时无计算开销 优点 :分配和释放速度极快,仅需修改栈指针(单条CPU指令) 无需手动管理,避免内存泄漏 作用域明确,提高代码可读性 支持RAII(资源获取即初始化) 缓存友好,局部变量通常在CPU缓存中 缺点 :空间有限,通常为几MB(可通过编译器/操作系统配置调整) 生命周期受限,函数返回后自动释放 不适合存储大型数据结构(可能导致栈溢出) 递归调用深度受栈大小限制 适用场景 :存储函数局部变量和参数 临时数据和中间计算结果 生命周期与函数调用一致的资源 小型对象和基本数据类型 堆分配 :
分配时机 :运行时通过内存分配器手动分配存储位置 :堆区生命周期 :从分配到手动释放管理方式 :程序员手动管理,需要配对使用分配和释放函数底层实现 :内存分配器维护空闲内存块链表或位图 分配时查找合适大小的块(首次适应、最佳适应、最坏适应算法) 释放时将块放回空闲链表,可能合并相邻空闲块 现代分配器(如ptmalloc、tcmalloc、jemalloc)采用多线程本地缓存和分级分配 优点 :空间较大,适合存储大型数据结构 生命周期灵活,可由程序员控制 支持动态调整大小 适合存储生命周期跨函数调用的数据 缺点 :分配和释放速度较慢(需要查找空闲块) 需要手动管理,容易导致内存泄漏 可能产生内存碎片(内部碎片和外部碎片) 地址不固定,访问速度相对较慢(间接地址访问) 多线程环境下可能存在锁竞争 适用场景 :存储大小不确定的数据 生命周期与函数调用无关的数据 大型数据结构和容器 需要在运行时动态创建的对象 内存分配性能对比 :
分配方式 分配速度 空间大小 灵活性 安全性 访问速度 线程安全 静态分配 极快 中 低 高 极快 低(可能竞争) 栈分配 极快 小 低 高 极快 高(线程私有) 堆分配 慢 大 高 低 中 中(需同步)
内存分配的高级技术 :
内存池 :预分配大块内存,然后分割成小块分配,减少分配开销和内存碎片对象池 :为特定类型对象预分配内存,避免频繁构造/析构区域分配器 :为特定生命周期的对象分配内存,一次性释放整个区域线程本地存储 :为每个线程分配独立的内存池,减少线程竞争内存对齐 :确保内存分配满足特定对齐要求,提高访问速度内存映射 :使用mmap分配大内存,支持按需分页和文件映射内存分配的底层实现 :
栈分配 :
通过修改栈指针(ESP/RSP寄存器)实现 分配时栈指针减少(向低地址方向移动) 释放时栈指针增加(向高地址方向移动) 栈帧结构包含:返回地址、前栈指针、函数参数、局部变量 栈帧大小在编译时确定,运行时无计算开销 堆分配 :
通过内存分配器(如ptmalloc、tcmalloc、jemalloc)管理 维护空闲内存块链表或位图 分配时查找合适大小的块(首次适应、最佳适应、最坏适应算法) 释放时将块放回空闲链表,可能合并相邻空闲块 处理内存碎片,提高内存利用率 静态分配 :
在程序加载时由操作系统加载器分配 位于数据段或BSS段 变量地址在编译时确定,存储在可执行文件的符号表中 初始化的全局变量存储在数据段,未初始化的存储在BSS段 内存分配的高级特性 :
内存对齐 :
变量或对象在内存中的起始地址必须是某个值的倍数 提高内存访问速度,减少CPU读取次数 某些硬件和指令集要求数据必须对齐 使用alignas关键字和std::aligned_alloc函数控制 内存屏障 :
确保内存操作的顺序性,防止编译器和CPU重排序 用于多线程编程中的同步 使用std::atomic和内存序控制 内存映射 :
将文件或设备映射到进程的虚拟内存空间 提供高效的文件I/O操作 使用mmap系统调用(POSIX)或等效API 虚拟内存 :
提供逻辑地址到物理地址的映射 支持内存保护和分页管理 允许程序使用比物理内存更大的地址空间 内存管理的挑战 内存泄漏 :
定义 :动态分配的内存未释放,导致内存使用量不断增加常见原因 :忘记释放内存(最常见) 异常导致的提前返回(资源获取后异常,未执行释放代码) 循环引用(尤其是使用shared_ptr时) 全局对象持有资源引用 检测工具 :Valgrind Memcheck(Linux) AddressSanitizer(跨平台) Dr. Memory(Windows) Visual Studio Memory Profiler(Windows) 解决方案 :使用智能指针(unique_ptr、shared_ptr) 实现RAII(资源获取即初始化) 使用内存分析工具定期检测 采用静态分析工具(如Clang Static Analyzer) 内存碎片 :
定义 :频繁分配和释放小块内存导致的内存空间不连续类型 :内部碎片:分配块大于请求大小(如对齐导致的浪费) 外部碎片:空闲块分散,无法满足大内存请求 影响 :内存利用率下降 分配速度变慢 程序内存使用量增加 可能导致内存分配失败(即使总空闲内存足够) 解决方案 :使用内存池(固定大小块) 使用对象池(特定类型对象) 使用区域分配器(同生命周期对象) 采用伙伴系统分配器(如jemalloc) 合理设置内存分配策略(如使用大页) 悬空指针 :
定义 :指向已释放内存的指针风险 :解引用悬空指针会导致未定义行为(崩溃、数据损坏、安全漏洞)常见场景 :释放后未置空指针 多线程环境下的指针竞争 函数返回局部变量的指针 解决方案 :释放后将指针置为nullptr 使用智能指针(自动管理生命周期) 采用引用而非指针(避免悬空) 使用AddressSanitizer检测 野指针 :
定义 :未初始化或指向无效内存的指针风险 :解引用野指针会导致未定义行为常见场景 :解决方案 :始终初始化指针(nullptr或有效地址) 使用智能指针 避免裸指针的使用 启用编译器警告(-Wuninitialized) 内存溢出 :
类型 :栈溢出:递归过深或局部变量过大 堆溢出:分配的内存超过可用堆空间 缓冲区溢出:写入超过缓冲区大小的数据 风险 :解决方案 :合理设计递归(设置深度限制或改用迭代) 使用动态内存存储大型数据 监控内存使用(设置内存上限) 使用std::vector等安全容器 启用地址 sanitizer 和边界检查 内存竞争 :
定义 :多线程环境下对共享内存的并发访问导致的数据不一致风险 :解决方案 :使用互斥锁(std::mutex) 使用原子操作(std::atomic) 使用线程本地存储(thread_local) 采用无锁数据结构 内存管理的最佳实践 :
优先使用栈分配 :
对于小对象和临时数据,优先使用栈分配 利用栈分配的速度优势和自动管理特性 栈分配的对象支持RAII,确保异常安全 合理使用静态分配 :
对于全局常量和配置数据,使用静态分配 避免过度使用全局变量,减少命名冲突 考虑使用inline变量(C++17+)替代全局变量 谨慎使用堆分配 :
对于大型数据结构和动态大小的数据,使用堆分配 始终使用RAII和智能指针管理堆内存 优先使用std::unique_ptr,仅在需要共享所有权时使用std::shared_ptr 监控内存使用 :
使用内存分析工具(如Valgrind、AddressSanitizer)检测内存问题 定期检查内存使用情况,避免内存泄漏 在生产环境中设置内存监控和告警 优化内存分配 :
减少动态内存分配次数,使用批量分配 对于频繁分配的小对象,使用内存池 合理设计数据结构,减少内存占用 使用reserve()预分配容器空间 使用现代C++特性 :
使用智能指针替代裸指针 使用std::optional避免空指针检查 使用std::variant减少联合类型的内存占用 利用C++17 PMR(多态内存资源)实现灵活内存管理 内存对齐优化 :
使用alignas关键字确保内存对齐 合理安排结构体成员顺序,减少内存填充 对于性能关键数据,使用缓存行对齐 多线程内存管理 :
使用线程本地存储减少竞争 采用无锁数据结构提高并发性能 考虑NUMA架构的内存亲和性 资源管理 :
将所有资源(不仅是内存)纳入RAII管理 使用自定义删除器管理非内存资源 实现资源池减少资源创建开销 持续优化 :
使用性能分析工具识别内存瓶颈 定期审查内存使用模式 关注内存访问的局部性,提高缓存命中率 动态内存分配 new和delete运算符C++使用new和delete运算符进行动态内存分配和释放,它们在C的malloc/free基础上增加了对象的构造和析构功能,提供了更安全、更符合C++语言特性的内存管理方式。
基本用法 1 2 3 4 5 6 7 8 9 10 11 12 int * p = new int ;*p = 42 ; std::cout << *p << std::endl; delete p;int * arr = new int [10 ];for (int i = 0 ; i < 10 ; ++i) { arr[i] = i; } delete [] arr;
初始化方式 C++支持多种初始化方式,适应不同的场景需求:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int * p1 = new int (100 );Point* p2 = new Point{10 , 20 }; int * p3 = new int ;int * p4 = new int {};int * arr1 = new int [5 ]{1 , 2 , 3 , 4 , 5 };int * arr2 = new int [5 ]{};int * arr3 = new int [5 ];char buffer[sizeof (Point)];Point* p5 = new (buffer) Point{30 , 40 }; constexpr int size = 10 ;const int * p6 = new const int [size]{1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };
定位new(Placement New) 定位new允许在指定的内存位置创建对象,而不分配新的内存,是实现内存池、对象池和自定义分配器的基础。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <new> alignas (alignof (int )) char buffer[sizeof (int )];int * p = new (buffer) int (42 );std::cout << *p << std::endl; new (buffer) int (100 );std::cout << *p << std::endl;
定位new的高级应用 :
内存池 :预分配大块内存,然后在其中构造对象,减少内存分配开销自定义分配器 :实现特定的内存分配策略,如线程本地存储、对齐分配等对象重用 :在同一内存位置重复构造不同类型的对象,提高内存利用率内存布局控制 :精确控制对象在内存中的位置,用于硬件交互或性能优化异常安全 :在复杂的初始化过程中,确保内存分配和对象构造的原子性内存对齐 :确保对象在内存中按特定字节对齐,提高访问速度new和delete的底层实现new运算符的工作过程:
调用operator new分配原始内存 在分配的内存上调用构造函数 返回指向新对象的指针 delete运算符的工作过程:
调用对象的析构函数 调用operator delete释放内存 深度实现分析 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 template <typename T, typename ... Args>T* new_impl (Args&&... args) { void * memory = operator new (sizeof (T)); try { new (memory) T (std::forward<Args>(args)...); return static_cast <T*>(memory); } catch (...) { operator delete (memory) ; throw ; } } template <typename T>void delete_impl (T* ptr) { if (ptr) { ptr->~T (); operator delete (ptr) ; } } template <typename T>T* new_array_impl (size_t size) { void * memory = operator new [](size * sizeof (T) + sizeof (size_t )); *static_cast <size_t *>(memory) = size; try { T* objects = static_cast <T*>(static_cast <char *>(memory) + sizeof (size_t )); for (size_t i = 0 ; i < size; ++i) { new (objects + i) T (); } return objects; } catch (...) { operator delete [](memory); throw ; } } template <typename T>typename std::enable_if<!std::is_trivially_destructible<T>::value>::type delete_array_impl (T* ptr) { if (ptr) { size_t size = *static_cast <size_t *>(static_cast <char *>(ptr) - sizeof (size_t )); for (size_t i = size; i > 0 ; --i) { (ptr + i - 1 )->~T (); } operator delete [](static_cast <char *>(ptr) - sizeof (size_t )); } } template <typename T>typename std::enable_if<std::is_trivially_destructible<T>::value>::type delete_array_impl (T* ptr) { if (ptr) { operator delete [](static_cast <char *>(ptr) - sizeof (size_t )); } }
重载new和delete运算符 C++允许重载new和delete运算符,以实现自定义的内存分配策略,满足特定场景的需求。
全局重载 :影响整个程序的内存分配1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 void * operator new (size_t size) { std::cout << "Custom new called for size " << size << std::endl; void * p = std::malloc (size); if (!p) { throw std::bad_alloc{}; } return p; } void operator delete (void * p) noexcept { std::cout << "Custom delete called" << std::endl; std::free (p); } void * operator new [](size_t size) { std::cout << "Custom new[] called for size " << size << std::endl; return operator new (size); } void operator delete [](void * p) noexcept { std::cout << "Custom delete[] called" << std::endl; operator delete (p) ; } void * operator new (size_t size, const std::nothrow_t &) noexcept { try { return operator new (size); } catch (...) { return nullptr ; } } void * operator new (size_t size, std::align_val_t align) { std::cout << "Custom aligned new called for size " << size << " and alignment " << static_cast <size_t >(align) << std::endl; void * p = std::aligned_alloc (static_cast <size_t >(align), size); if (!p) { throw std::bad_alloc{}; } return p; } void operator delete (void * p, std::align_val_t align) noexcept { std::cout << "Custom aligned delete called" << std::endl; std::free (p); }
类级重载 :仅影响特定类的对象分配1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class MyClass {public : void * operator new (size_t size) { std::cout << "MyClass new called for size " << size << std::endl; void * p = memoryPool.allocate (size); if (!p) { throw std::bad_alloc{}; } return p; } void operator delete (void * p) noexcept { std::cout << "MyClass delete called" << std::endl; memoryPool.deallocate (p); } void * operator new [](size_t size) { std::cout << "MyClass new[] called for size " << size << std::endl; return memoryPool.allocate (size); } void operator delete [](void * p) noexcept { std::cout << "MyClass delete[] called" << std::endl; memoryPool.deallocate (p); } void * operator new (size_t size, const std::nothrow_t &) noexcept { try { return operator new (size); } catch (...) { return nullptr ; } } void operator delete (void * p, const std::nothrow_t &) noexcept { operator delete (p) ; } private : static MemoryPool memoryPool; }; MemoryPool MyClass::memoryPool (sizeof (MyClass), 100 ) ;
放置版本重载 :自定义定位new的行为1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void * operator new (size_t size, const char * file, int line) { std::cout << "Placement new called from " << file << ":" << line << std::endl; return operator new (size); } void operator delete (void * p, const char * file, int line) noexcept { std::cout << "Placement delete called from " << file << ":" << line << std::endl; operator delete (p) ; } void * operator new (size_t size, alignas (16 ) char * buffer) { std::cout << "Aligned placement new called" << std::endl; return buffer; } void operator delete (void * p, alignas (16 ) char * buffer) noexcept { std::cout << "Aligned placement delete called" << std::endl; } #define DEBUG_NEW new(__FILE__, __LINE__) MyClass* p = DEBUG_NEW MyClass (); delete p;alignas (16 ) char buffer[sizeof (MyClass)];MyClass* p2 = new (buffer) MyClass (); p2->~MyClass ();
内存分配策略(高级实现) 内存池 :
设计原理 :预分配大块内存,然后分割成固定大小的块进行管理实现要点 :使用空闲链表管理可用块 支持块大小对齐 实现线程安全版本 提供内存使用统计 性能优势 :分配/释放操作O(1)时间复杂度 减少内存碎片 避免系统调用开销 对象池 :
设计原理 :为特定类型对象预分配内存,管理对象的构造和析构实现要点 :模板化设计,支持任意类型 分离内存分配和对象构造 支持对象复用 线程安全选项 适用场景 :对象创建成本高(如数据库连接) 频繁创建和销毁的对象 需要控制对象数量的场景 区域分配器 :
设计原理 :为特定生命周期的对象分配内存,一次性释放整个区域实现要点 :线性分配内存(类似栈) 支持多个分配区域 提供作用域管理 适用场景 :单次请求中创建多个对象 事件处理中的临时对象 游戏引擎中的帧内对象 缓存分配器 :
设计原理 :缓存最近释放的内存块,加速后续分配实现要点 :适用场景 :对齐分配器 :
设计原理 :确保内存分配满足特定的对齐要求实现要点 :性能优势 :内存分配策略的选择指南 :
场景 推荐策略 优势 注意事项 频繁分配小对象 内存池 速度快,无碎片 块大小选择至关重要 特定类型对象 对象池 避免构造/析构开销 需考虑对象生命周期 批量创建临时对象 区域分配器 一次性释放,简单高效 内存使用峰值较高 多线程环境 线程本地内存池 无锁竞争 内存使用可能增加 高性能计算 对齐分配器 提高内存访问速度 内存开销略大
内存池实现示例(高级版本) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 class MemoryPool {public : MemoryPool (size_t blockSize, size_t blockCount, bool threadSafe = false ) : blockSize (alignBlockSize (blockSize)), blockCount (blockCount), pool (nullptr ), freeList (nullptr ), allocatedBlocks (0 ), threadSafe (threadSafe) { pool = static_cast <char *>(std::malloc (blockSize * blockCount)); if (!pool) { throw std::bad_alloc{}; } initializeFreeList (); } ~MemoryPool () { if (pool) { std::free (pool); } } void * allocate () { if (threadSafe) { std::lock_guard<std::mutex> lock (mutex) ; return allocateImpl (); } return allocateImpl (); } void deallocate (void * ptr) { if (!ptr) return ; if (threadSafe) { std::lock_guard<std::mutex> lock (mutex) ; deallocateImpl (ptr); } else { deallocateImpl (ptr); } } size_t getBlockSize () const { return blockSize; } size_t getBlockCount () const { return blockCount; } size_t getAllocatedBlocks () const { if (threadSafe) { std::lock_guard<std::mutex> lock (mutex) ; return allocatedBlocks; } return allocatedBlocks; } size_t getFreeBlocks () const { return blockCount - getAllocatedBlocks (); } bool isFull () const { return getFreeBlocks () == 0 ; } bool isEmpty () const { return getAllocatedBlocks () == 0 ; } size_t getTotalMemory () const { return blockSize * blockCount; } size_t getUsedMemory () const { return blockSize * getAllocatedBlocks (); } double getMemoryUsage () const { return static_cast <double >(getUsedMemory ()) / getTotalMemory () * 100.0 ; } void reset () { if (threadSafe) { std::lock_guard<std::mutex> lock (mutex) ; resetImpl (); } else { resetImpl (); } } MemoryPool (const MemoryPool&) = delete ; MemoryPool& operator =(const MemoryPool&) = delete ; MemoryPool (MemoryPool&&) = delete ; MemoryPool& operator =(MemoryPool&&) = delete ; private : size_t blockSize; size_t blockCount; char * pool; char * freeList; size_t allocatedBlocks; bool threadSafe; mutable std::mutex mutex; size_t alignBlockSize (size_t size) { size_t minSize = sizeof (void *); if (size < minSize) { size = minSize; } size_t alignment = sizeof (void *); return (size + alignment - 1 ) & ~(alignment - 1 ); } void initializeFreeList () { freeList = nullptr ; for (size_t i = 0 ; i < blockCount; ++i) { char * block = pool + i * blockSize; *reinterpret_cast <char **>(block) = freeList; freeList = block; } allocatedBlocks = 0 ; } void * allocateImpl () { if (!freeList) { throw std::bad_alloc{}; } void * block = freeList; freeList = *reinterpret_cast <char **>(freeList); allocatedBlocks++; return block; } void deallocateImpl (void * ptr) { if (ptr < pool || ptr >= pool + blockSize * blockCount) { throw std::invalid_argument ("Pointer not from this memory pool" ); } size_t offset = static_cast <char *>(ptr) - pool; if (offset % blockSize != 0 ) { throw std::invalid_argument ("Pointer not properly aligned" ); } *reinterpret_cast <char **>(ptr) = freeList; freeList = static_cast <char *>(ptr); allocatedBlocks--; } void resetImpl () { initializeFreeList (); } }; void memoryPoolExample () { MemoryPool pool (sizeof (int ), 1000 , true ) ; std::vector<void *> pointers; pointers.reserve (500 ); for (int i = 0 ; i < 500 ; ++i) { void * p = pool.allocate (); *static_cast <int *>(p) = i; pointers.push_back (p); } std::cout << "Block size: " << pool.getBlockSize () << std::endl; std::cout << "Total blocks: " << pool.getBlockCount () << std::endl; std::cout << "Allocated blocks: " << pool.getAllocatedBlocks () << std::endl; std::cout << "Free blocks: " << pool.getFreeBlocks () << std::endl; std::cout << "Memory usage: " << pool.getMemoryUsage () << "%" << std::endl; std::cout << "Total memory: " << pool.getTotalMemory () << " bytes" << std::endl; for (void * p : pointers) { pool.deallocate (p); } std::cout << "After deallocation:" << std::endl; std::cout << "Allocated blocks: " << pool.getAllocatedBlocks () << std::endl; std::cout << "Free blocks: " << pool.getFreeBlocks () << std::endl; std::cout << "Memory usage: " << pool.getMemoryUsage () << "%" << std::endl; pool.reset (); std::cout << "After reset:" << std::endl; std::cout << "Allocated blocks: " << pool.getAllocatedBlocks () << std::endl; std::cout << "Free blocks: " << pool.getFreeBlocks () << std::endl; } void multiThreadedMemoryPoolTest () { MemoryPool pool (sizeof (int ), 10000 , true ) ; std::atomic<int > sum (0 ) ; std::vector<std::thread> threads; for (int i = 0 ; i < 10 ; ++i) { threads.emplace_back ([&pool, &sum, i] { std::vector<void *> pointers; pointers.reserve (1000 ); for (int j = 0 ; j < 1000 ; ++j) { void * p = pool.allocate (); *static_cast <int *>(p) = i * 1000 + j; pointers.push_back (p); } int localSum = 0 ; for (void * p : pointers) { localSum += *static_cast <int *>(p); } sum += localSum; for (void * p : pointers) { pool.deallocate (p); } }); } for (auto & thread : threads) { thread.join (); } std::cout << "Multi-threaded test completed." << std::endl; std::cout << "Sum: " << sum << std::endl; std::cout << "Allocated blocks: " << pool.getAllocatedBlocks () << std::endl; std::cout << "Free blocks: " << pool.getFreeBlocks () << std::endl; }
对象池实现示例(高级版本) :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 template <typename T, bool ThreadSafe = false >class ObjectPool {public : ObjectPool (size_t capacity) : capacity (capacity), size (0 ) { size_t alignment = alignof (T); size_t totalSize = sizeof (T) * capacity; pool = static_cast <T*>(std::aligned_alloc (alignment, totalSize)); if (!pool) { throw std::bad_alloc{}; } for (size_t i = 0 ; i < capacity; ++i) { T* obj = pool + i; freeObjects.push_back (obj); } } ~ObjectPool () { for (T* obj : allocatedObjects) { obj->~T (); } std::free (pool); } template <typename ... Args> T* allocate (Args&&... args) { if constexpr (ThreadSafe) { std::lock_guard<std::mutex> lock (mutex) ; return allocateImpl (std::forward<Args>(args)...); } else { return allocateImpl (std::forward<Args>(args)...); } } void deallocate (T* obj) { if (!obj) return ; if constexpr (ThreadSafe) { std::lock_guard<std::mutex> lock (mutex) ; deallocateImpl (obj); } else { deallocateImpl (obj); } } template <typename ... Args> std::vector<T*> allocateBatch (size_t count, Args&&... args) { std::vector<T*> objects; objects.reserve (count); for (size_t i = 0 ; i < count; ++i) { objects.push_back (allocate (std::forward<Args>(args)...)); } return objects; } void deallocateBatch (const std::vector<T*>& objects) { for (T* obj : objects) { deallocate (obj); } } size_t getCapacity () const { if constexpr (ThreadSafe) { std::lock_guard<std::mutex> lock (mutex) ; return capacity; } else { return capacity; } } size_t getSize () const { if constexpr (ThreadSafe) { std::lock_guard<std::mutex> lock (mutex) ; return size; } else { return size; } } size_t getFreeCount () const { if constexpr (ThreadSafe) { std::lock_guard<std::mutex> lock (mutex) ; return freeObjects.size (); } else { return freeObjects.size (); } } bool isFull () const { return getFreeCount () == 0 ; } bool isEmpty () const { return getSize () == 0 ; } void reset () { if constexpr (ThreadSafe) { std::lock_guard<std::mutex> lock (mutex) ; resetImpl (); } else { resetImpl (); } } ObjectPool (const ObjectPool&) = delete ; ObjectPool& operator =(const ObjectPool&) = delete ; ObjectPool (ObjectPool&&) = delete ; ObjectPool& operator =(ObjectPool&&) = delete ; private : size_t capacity; size_t size; T* pool; std::vector<T*> freeObjects; std::vector<T*> allocatedObjects; mutable std::mutex mutex; template <typename ... Args> T* allocateImpl (Args&&... args) { if (freeObjects.empty ()) { throw std::bad_alloc{}; } T* obj = freeObjects.back (); freeObjects.pop_back (); try { new (obj) T (std::forward<Args>(args)...); } catch (...) { freeObjects.push_back (obj); throw ; } allocatedObjects.push_back (obj); size++; return obj; } void deallocateImpl (T* obj) { if (obj < pool || obj >= pool + capacity) { throw std::invalid_argument ("Object not from this pool" ); } auto it = std::find (allocatedObjects.begin (), allocatedObjects.end (), obj); if (it == allocatedObjects.end ()) { throw std::invalid_argument ("Object not allocated from this pool" ); } obj->~T (); allocatedObjects.erase (it); freeObjects.push_back (obj); size--; } void resetImpl () { for (T* obj : allocatedObjects) { obj->~T (); freeObjects.push_back (obj); } allocatedObjects.clear (); size = 0 ; } }; class MyObject {public : MyObject (int v) : value (v) { std::this_thread::sleep_for (std::chrono::microseconds (10 )); std::cout << "MyObject constructed with value " << value << std::endl; } ~MyObject () { std::this_thread::sleep_for (std::chrono::microseconds (5 )); std::cout << "MyObject destructed with value " << value << std::endl; } int getValue () const { return value; } void setValue (int v) { value = v; } private : int value; }; void singleThreadObjectPoolExample () { ObjectPool<MyObject, false > objPool (10 ) ; std::cout << "=== Single-threaded Object Pool Example ===" << std::endl; std::cout << "Initial capacity: " << objPool.getCapacity () << std::endl; std::cout << "Initial free objects: " << objPool.getFreeCount () << std::endl; MyObject* obj1 = objPool.allocate (10 ); MyObject* obj2 = objPool.allocate (20 ); std::cout << "After allocation:" << std::endl; std::cout << "Allocated objects: " << objPool.getSize () << std::endl; std::cout << "Free objects: " << objPool.getFreeCount () << std::endl; std::cout << "obj1 value: " << obj1->getValue () << std::endl; std::cout << "obj2 value: " << obj2->getValue () << std::endl; obj1->setValue (100 ); obj2->setValue (200 ); std::cout << "After modification:" << std::endl; std::cout << "obj1 value: " << obj1->getValue () << std::endl; std::cout << "obj2 value: " << obj2->getValue () << std::endl; objPool.deallocate (obj1); objPool.deallocate (obj2); std::cout << "After deallocation:" << std::endl; std::cout << "Allocated objects: " << objPool.getSize () << std::endl; std::cout << "Free objects: " << objPool.getFreeCount () << std::endl; MyObject* obj3 = objPool.allocate (30 ); std::cout << "Reused object value: " << obj3->getValue () << std::endl; objPool.deallocate (obj3); std::cout << "\n=== Batch Operations ===" << std::endl; auto objects = objPool.allocateBatch (5 , 42 ); std::cout << "Allocated batch of " << objects.size () << " objects" << std::endl; objPool.deallocateBatch (objects); std::cout << "Deallocated batch of objects" << std::endl; objPool.reset (); std::cout << "After reset:" << std::endl; std::cout << "Allocated objects: " << objPool.getSize () << std::endl; std::cout << "Free objects: " << objPool.getFreeCount () << std::endl; } void multiThreadedObjectPoolExample () { ObjectPool<MyObject, true > objPool (100 ) ; std::atomic<int > totalValue (0 ) ; std::vector<std::thread> threads; std::cout << "\n=== Multi-threaded Object Pool Example ===" << std::endl; for (int i = 0 ; i < 10 ; ++i) { threads.emplace_back ([&objPool, &totalValue, i] { std::vector<MyObject*> objects; objects.reserve (10 ); for (int j = 0 ; j < 10 ; ++j) { int value = i * 100 + j; MyObject* obj = objPool.allocate (value); objects.push_back (obj); } int localSum = 0 ; for (MyObject* obj : objects) { localSum += obj->getValue (); } totalValue += localSum; for (MyObject* obj : objects) { objPool.deallocate (obj); } }); } for (auto & thread : threads) { thread.join (); } std::cout << "Multi-threaded test completed." << std::endl; std::cout << "Total value: " << totalValue << std::endl; std::cout << "Allocated objects: " << objPool.getSize () << std::endl; std::cout << "Free objects: " << objPool.getFreeCount () << std::endl; } void objectPoolPerformanceTest () { const size_t ObjectCount = 10000 ; std::cout << "\n=== Object Pool Performance Test ===" << std::endl; { ObjectPool<MyObject, false > objPool (ObjectCount) ; auto start = std::chrono::high_resolution_clock::now (); for (size_t i = 0 ; i < ObjectCount; ++i) { MyObject* obj = objPool.allocate (i); objPool.deallocate (obj); } auto end = std::chrono::high_resolution_clock::now (); auto duration = std::chrono::duration_cast <std::chrono::milliseconds>(end - start); std::cout << "Object pool: " << duration.count () << " ms" << std::endl; } { auto start = std::chrono::high_resolution_clock::now (); for (size_t i = 0 ; i < ObjectCount; ++i) { MyObject* obj = new MyObject (i); delete obj; } auto end = std::chrono::high_resolution_clock::now (); auto duration = std::chrono::duration_cast <std::chrono::milliseconds>(end - start); std::cout << "Direct new/delete: " << duration.count () << " ms" << std::endl; } }
智能指针 C++11引入了智能指针,用于自动管理动态内存,避免内存泄漏。智能指针通过RAII(资源获取即初始化)机制,在对象生命周期结束时自动释放所管理的资源。现代C++中,智能指针已成为内存管理的首选方案,取代了传统的裸指针和手动内存管理。
std::unique_ptrstd::unique_ptr是一种独占所有权的智能指针,同一时间只能有一个unique_ptr指向同一个对象,所有权可以转移但不能复制。它是最常用的智能指针类型,适用于大多数场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <memory> #include <iostream> std::unique_ptr<int > p1 (new int (42 )) ;auto p2 = std::make_unique <int >(100 );std::unique_ptr<int > p3 = std::move (p1); std::cout << *p2 << std::endl; std::cout << *p3 << std::endl; if (p1) { std::cout << *p1 << std::endl; } else { std::cout << "p1 is null" << std::endl; } p2. reset (); p3. reset (new int (200 )); int * rawPtr = p3. get ();std::cout << *rawPtr << std::endl; int * releasedPtr = p3. release ();delete releasedPtr;
std::unique_ptr的高级特性 :
零开销抽象 :
大小与原始指针相同(通常为8字节) 无引用计数开销 移动操作开销极小(仅指针赋值) 自定义删除器 :
支持函数对象作为删除器 支持lambda表达式作为删除器 删除器类型成为模板参数的一部分 数组特化 :
自动使用delete[]释放数组 支持operator[]访问数组元素 C++14+支持make_unique<T[]>创建数组 派生类到基类的转换 :
支持从派生类unique_ptr到基类unique_ptr的隐式转换 确保正确的多态删除 std::unique_ptr与数组 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 std::unique_ptr<int []> arr (new int [10 ]) ;for (int i = 0 ; i < 10 ; ++i) { arr[i] = i; } auto arr2 = std::make_unique <int []>(10 );auto arrayDeleter = [](int * p) { std::cout << "Deleting array" << std::endl; delete [] p; }; std::unique_ptr<int , decltype (arrayDeleter) > arr3 (new int [5 ], arrayDeleter) ;
std::shared_ptrstd::shared_ptr是一种共享所有权的智能指针,多个shared_ptr可以指向同一个对象,使用引用计数来管理内存。当最后一个shared_ptr被销毁时,对象才会被释放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <memory> #include <iostream> std::shared_ptr<int > p1 (new int (42 )) ;auto p2 = std::make_shared <int >(100 );std::shared_ptr<int > p3 = p1; std::cout << *p1 << std::endl; std::cout << *p2 << std::endl; std::cout << *p3 << std::endl; std::cout << "p1 use_count: " << p1. use_count () << std::endl; p1. reset (); std::cout << "p3 use_count after p1.reset(): " << p3. use_count () << std::endl; std::shared_ptr<int > p4 = std::make_shared <int >(300 ); p3. swap (p4); std::cout << *p3 << std::endl; std::cout << *p4 << std::endl;
std::shared_ptr的内部实现 :
控制块 :
存储引用计数(强引用) 存储弱引用计数 存储自定义删除器 存储分配器 内存布局 :
当使用make_shared时,控制块和对象在同一块内存中分配,减少内存开销和内存碎片 当使用shared_ptr(new T())时,会分配两次内存(一次用于对象,一次用于控制块) 线程安全性 :
引用计数操作是原子的,线程安全 多个线程可以同时修改引用计数 但对象本身的访问不是线程安全的,需要额外同步 std::shared_ptr的性能考量 :
大小为两个指针(通常为16字节) 引用计数操作是原子的,有一定的线程安全开销 避免使用shared_ptr(new T()),优先使用make_shared<T>() 对于大对象,考虑使用std::allocate_shared自定义分配器 std::weak_ptrstd::weak_ptr是一种不增加引用计数的智能指针,用于解决shared_ptr的循环引用问题,也可以用于观察对象是否存在。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <memory> #include <iostream> class B ;class A {public : std::shared_ptr<B> b; ~A () { std::cout << "A destroyed" << std::endl; } }; class B {public : std::weak_ptr<A> a; ~B () { std::cout << "B destroyed" << std::endl; } }; int main () { auto a = std::make_shared <A>(); auto b = std::make_shared <B>(); a->b = b; b->a = a; std::cout << "a use_count: " << a.use_count () << std::endl; std::cout << "b use_count: " << b.use_count () << std::endl; if (auto locked = b->a.lock ()) { std::cout << "A still exists" << std::endl; } else { std::cout << "A has been destroyed" << std::endl; } if (b->a.expired ()) { std::cout << "A has expired" << std::endl; } else { std::cout << "A is still alive" << std::endl; } return 0 ; }
std::weak_ptr的高级应用 :
解决循环引用 :
在双向引用中,一方使用shared_ptr,另一方使用weak_ptr 打破引用计数循环,确保对象能被正确销毁 观察者模式 :
观察者持有被观察对象的weak_ptr 避免观察者延长被观察对象的生命周期 当被观察对象销毁时,观察者不会持有悬空指针 缓存实现 :
缓存对象的weak_ptr 当对象不再被其他地方使用时,缓存自动失效 避免缓存导致对象无法释放 延迟初始化 :
结合weak_ptr和shared_ptr实现对象的延迟初始化 当需要时通过lock()获取对象 智能指针的最佳实践 优先使用std::unique_ptr :
对于独占所有权的场景,unique_ptr是最佳选择 无引用计数开销,性能最优 明确表达所有权语义 仅在需要共享所有权时使用std::shared_ptr :
避免过度使用shared_ptr,因为它有引用计数开销 优先使用make_shared创建shared_ptr 对于大对象,考虑使用std::allocate_shared 使用std::weak_ptr解决循环引用 :
在双向引用中,一方使用shared_ptr,另一方使用weak_ptr 用于实现观察者模式和缓存 避免裸指针与智能指针混用 :
不要将智能指针管理的内存地址赋给裸指针 避免手动释放智能指针管理的内存 使用get()获取原始指针时要谨慎 自定义删除器的使用 :
用于管理非堆内存资源(如文件句柄、网络连接) 用于实现特殊的资源释放逻辑 优先使用lambda表达式作为删除器,简洁明了 智能指针与异常安全 :
智能指针确保即使发生异常,资源也能被正确释放 避免使用shared_ptr(new T()),因为可能导致内存泄漏 使用make_shared或std::unique_ptr配合std::move 智能指针的性能对比 :
智能指针类型 大小 引用计数 线程安全 开销 适用场景 unique_ptr8字节 无 不适用 极低 独占所有权 shared_ptr16字节 有 是 中等 共享所有权 weak_ptr8字节 无(依赖shared_ptr) 不适用 低 观察对象
智能指针的高级实现技巧 自定义删除器 :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <memory> #include <fstream> #include <iostream> struct FileDeleter { void operator () (std::FILE* fp) { if (fp) { std::fclose (fp); std::cout << "File closed" << std::endl; } } }; std::unique_ptr<std::FILE, FileDeleter> filePtr (std::fopen("test.txt" , "w" )) ;auto deleter = [](int * p) { std::cout << "Custom deleter called" << std::endl; delete p; }; std::unique_ptr<int , decltype (deleter) > p (new int (42 ), deleter) ;std::shared_ptr<int > sp (new int (100 ), [](int * p) { std::cout << "Shared deleter called" << std::endl; delete p; }) ;std::shared_ptr<int > arrSp (new int [10 ], [](int * p) { delete [] p; }) ;std::unique_ptr<FILE, int (*) (FILE*) > filePtr2 (fopen("test2.txt" , "w" ), fclose) ;
智能指针与工厂模式 :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <memory> class Base {public : virtual ~Base () = default ; virtual void doSomething () = 0 ; }; class Derived1 : public Base {public : void doSomething () override { } }; class Derived2 : public Base {public : void doSomething () override { } }; enum class DerivedType { Type1, Type2 };std::unique_ptr<Base> createBase (DerivedType type) { switch (type) { case DerivedType::Type1: return std::make_unique <Derived1>(); case DerivedType::Type2: return std::make_unique <Derived2>(); default : return nullptr ; } } int main () { auto obj1 = createBase (DerivedType::Type1); auto obj2 = createBase (DerivedType::Type2); obj1->doSomething (); obj2->doSomething (); return 0 ; }
智能指针与容器 :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <memory> #include <vector> #include <iostream> int main () { std::vector<std::unique_ptr<int >> uniquePtrs; uniquePtrs.push_back (std::make_unique <int >(42 )); uniquePtrs.emplace_back (std::make_unique <int >(100 )); for (const auto & ptr : uniquePtrs) { std::cout << *ptr << std::endl; } std::vector<std::shared_ptr<int >> sharedPtrs; auto sp = std::make_shared <int >(200 ); sharedPtrs.push_back (sp); sharedPtrs.push_back (sp); std::cout << "sp use_count: " << sp.use_count () << std::endl; return 0 ; }
智能指针的未来发展 :
C++20引入了std::shared_ptr的owner_before方法,用于自定义排序;C++23进一步增强了智能指针的功能,包括std::make_shared_for_overwrite等优化。智能指针已经成为现代C++内存管理的基石,是每个C++开发者必须掌握的核心技术之一。
内存分配器 C++标准库提供了内存分配器接口,用于自定义容器的内存管理。通过实现自定义分配器,可以为特定场景优化内存分配策略,如内存池、对象池、线程本地存储等。
标准分配器接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 template <typename T>class Allocator {public : using value_type = T; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using size_type = std::size_t ; using difference_type = std::ptrdiff_t ; Allocator () noexcept = default ; template <typename U> Allocator (const Allocator<U>&) noexcept {} T* allocate (size_type n) { if (n > max_size ()) { throw std::bad_alloc (); } void * ptr = std::malloc (n * sizeof (T)); if (!ptr) { throw std::bad_alloc (); } return static_cast <T*>(ptr); } void deallocate (T* p, size_type) noexcept { std::free (p); } size_type max_size () const noexcept { return std::numeric_limits<size_type>::max () / sizeof (T); } template <typename U, typename ... Args> void construct (U* p, Args&&... args) { ::new (static_cast <void *>(p)) U (std::forward<Args>(args)...); } template <typename U> void destroy (U* p) { p->~U (); } template <typename U> struct rebind { using other = Allocator<U>; }; }; template <typename T, typename U>bool operator ==(const Allocator<T>&, const Allocator<U>&) noexcept { return true ; } template <typename T, typename U>bool operator !=(const Allocator<T>&, const Allocator<U>&) noexcept { return false ; }
内存池分配器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 template <typename T>class PoolAllocator {private : struct Block { Block* next; char data[1 ]; }; Block* freeList; size_t blockSize; size_t alignment; Block* allocateBlock () { size_t actualSize = sizeof (Block) + blockSize - 1 ; void * raw = std::malloc (actualSize); if (!raw) { throw std::bad_alloc (); } void * aligned = raw; if (alignment > 1 ) { uintptr_t addr = reinterpret_cast <uintptr_t >(raw); uintptr_t alignedAddr = (addr + alignment - 1 ) & ~(alignment - 1 ); aligned = reinterpret_cast <void *>(alignedAddr); } Block* block = reinterpret_cast <Block*>(aligned); block->next = nullptr ; return block; } public : using value_type = T; PoolAllocator (size_t blockSize = sizeof (T), size_t alignment = alignof (T)) : freeList (nullptr ), blockSize (blockSize), alignment (alignment) { if (blockSize < sizeof (void *)) { blockSize = sizeof (void *); } } template <typename U> PoolAllocator (const PoolAllocator<U>& other) noexcept : freeList (nullptr ), blockSize (other.blockSize), alignment (other.alignment) { } ~PoolAllocator () { while (freeList) { Block* next = freeList->next; std::free (freeList); freeList = next; } } T* allocate (size_type n) { if (n != 1 ) { throw std::bad_alloc (); } if (!freeList) { freeList = allocateBlock (); } void * ptr = freeList->data; freeList = freeList->next; return static_cast <T*>(ptr); } void deallocate (T* p, size_type) noexcept { if (!p) return ; Block* block = reinterpret_cast <Block*>( reinterpret_cast <char *>(p) - offsetof (Block, data) ); block->next = freeList; freeList = block; } size_type max_size () const noexcept { return 1 ; } template <typename U, typename ... Args> void construct (U* p, Args&&... args) { ::new (static_cast <void *>(p)) U (std::forward<Args>(args)...); } template <typename U> void destroy (U* p) { p->~U (); } template <typename U> struct rebind { using other = PoolAllocator<U>; }; size_t getBlockSize () const noexcept { return blockSize; } size_t getAlignment () const noexcept { return alignment; } }; template <typename T, typename U>bool operator ==(const PoolAllocator<T>&, const PoolAllocator<U>&) noexcept { return true ; } template <typename T, typename U>bool operator !=(const PoolAllocator<T>&, const PoolAllocator<U>&) noexcept { return false ; }
多线程内存管理 线程本地存储 线程本地存储(Thread Local Storage, TLS)是一种为每个线程提供独立内存空间的机制,避免了线程间的内存竞争,提高了多线程程序的性能和安全性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 template <typename T>class ThreadLocalMemoryPool {private : thread_local static MemoryPool* pool; static MemoryPool& getPool () { if (!pool) { pool = new MemoryPool (sizeof (T), 100 ); } return *pool; } public : using value_type = T; ThreadLocalMemoryPool () noexcept = default ; template <typename U> ThreadLocalMemoryPool (const ThreadLocalMemoryPool<U>&) noexcept {} T* allocate (size_type n) { if (n != 1 ) { throw std::bad_alloc (); } return static_cast <T*>(getPool ().allocate ()); } void deallocate (T* p, size_type) noexcept { if (p) { getPool ().deallocate (p); } } template <typename U> struct rebind { using other = ThreadLocalMemoryPool<U>; }; }; template <typename T>thread_local MemoryPool* ThreadLocalMemoryPool<T>::pool = nullptr ;void multiThreadedExample () { std::vector<std::thread> threads; for (int i = 0 ; i < 4 ; ++i) { threads.emplace_back ([] { ThreadLocalMemoryPool<int > alloc; std::vector<int *, ThreadLocalMemoryPool<int >> vec; for (int j = 0 ; j < 1000 ; ++j) { int * p = alloc.allocate (1 ); *p = j; vec.push_back (p); } int sum = 0 ; for (int * p : vec) { sum += *p; } std::cout << "Thread sum: " << sum << std::endl; for (int * p : vec) { alloc.deallocate (p, 1 ); } }); } for (auto & thread : threads) { thread.join (); } }
原子操作与内存序 在多线程环境中,内存操作的顺序和可见性是至关重要的。C++11引入了原子操作和内存序,用于确保多线程间的内存操作正确同步。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 #include <atomic> #include <thread> std::atomic<int > counter (0 ) ;std::atomic<bool > ready (false ) ;void producer () { int value = 42 ; counter.store (value, std::memory_order_release); ready.store (true , std::memory_order_release); } void consumer () { while (!ready.load (std::memory_order_acquire)) { } int value = counter.load (std::memory_order_acquire); std::cout << "Value: " << value << std::endl; } void atomicExample () { std::thread t1 (producer) ; std::thread t2 (consumer) ; t1. join (); t2. join (); } class LockFreeStack {private : struct Node { int value; Node* next; Node (int v) : value (v), next (nullptr ) {} }; std::atomic<Node*> head; public : LockFreeStack () : head (nullptr ) {} void push (int value) { Node* newNode = new Node (value); newNode->next = head.load (std::memory_order_relaxed); while (!head.compare_exchange_weak ( newNode->next, newNode, std::memory_order_release, std::memory_order_relaxed)) { } } bool pop (int & value) { Node* oldHead = head.load (std::memory_order_relaxed); while (oldHead) { if (head.compare_exchange_weak ( oldHead, oldHead->next, std::memory_order_acquire, std::memory_order_relaxed)) { value = oldHead->value; delete oldHead; return true ; } } return false ; } }; void lockFreeStackExample () { LockFreeStack stack; std::vector<std::thread> threads; for (int i = 0 ; i < 2 ; ++i) { threads.emplace_back ([&stack, i] { for (int j = 0 ; j < 1000 ; ++j) { stack.push (i * 1000 + j); } }); } std::atomic<int > sum (0 ) ; for (int i = 0 ; i < 2 ; ++i) { threads.emplace_back ([&stack, &sum] { int value; int localSum = 0 ; for (int j = 0 ; j < 1000 ; ++j) { if (stack.pop (value)) { localSum += value; } } sum += localSum; }); } for (auto & thread : threads) { thread.join (); } std::cout << "Sum: " << sum << std::endl; }
内存资源管理的高级主题 C++17 多态内存资源 C++17引入了std::pmr命名空间,提供了多态内存资源(Polymorphic Memory Resources, PMR),用于统一内存分配接口,简化内存资源的管理和替换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <memory_resource> void pmrExample () { std::pmr::vector<int > v1; for (int i = 0 ; i < 10 ; ++i) { v1. push_back (i); } char buffer[1024 ]; std::pmr::monotonic_buffer_resource pool (buffer, sizeof (buffer)) ; std::pmr::vector<int > v2 (&pool) ; for (int i = 0 ; i < 100 ; ++i) { v2. push_back (i); } std::pmr::synchronized_pool_resource syncPool; std::pmr::vector<int > v3 (&syncPool) ; std::pmr::unsynchronized_pool_resource unsyncPool; std::pmr::vector<int > v4 (&unsyncPool) ; class CustomMemoryResource : public std::pmr::memory_resource { private : void * do_allocate (size_t bytes, size_t alignment) override { std::cout << "Allocating " << bytes << " bytes with alignment " << alignment << std::endl; return std::malloc (bytes); } void do_deallocate (void * p, size_t bytes, size_t alignment) override { std::cout << "Deallocating " << bytes << " bytes" << std::endl; std::free (p); } bool do_is_equal (const std::pmr::memory_resource& other) const noexcept override { return this == &other; } }; CustomMemoryResource customPool; std::pmr::vector<int > v5 (&customPool) ; for (int i = 0 ; i < 10 ; ++i) { v5. push_back (i); } } void pmrAdapterExample () { std::pmr::monotonic_buffer_resource pool; std::pmr::polymorphic_allocator<int > alloc (&pool) ; int * p = alloc.allocate (10 ); for (int i = 0 ; i < 10 ; ++i) { alloc.construct (p + i, i); } for (int i = 0 ; i < 10 ; ++i) { std::cout << p[i] << " " ; } std::cout << std::endl; for (int i = 0 ; i < 10 ; ++i) { alloc.destroy (p + i); } alloc.deallocate (p, 10 ); }
内存管理的性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 void preallocationExample () { std::vector<int > vec; vec.reserve (1000000 ); for (int i = 0 ; i < 1000000 ; ++i) { vec.push_back (i); } } struct alignas (64 ) CacheAlignedData { int data[16 ]; }; void alignmentExample () { CacheAlignedData data; } void memoryPoolOptimization () { MemoryPool pool (sizeof (int ), 1000000 ) ; std::vector<void *> pointers; pointers.reserve (1000000 ); for (int i = 0 ; i < 1000000 ; ++i) { void * p = pool.allocate (); *static_cast <int *>(p) = i; pointers.push_back (p); } int sum = 0 ; for (void * p : pointers) { sum += *static_cast <int *>(p); } for (void * p : pointers) { pool.deallocate (p); } } void cacheOptimization () { const int N = 10000 ; int ** matrix = new int *[N]; for (int i = 0 ; i < N; ++i) { matrix[i] = new int [N]; } int sum1 = 0 ; for (int i = 0 ; i < N; ++i) { for (int j = 0 ; j < N; ++j) { sum1 += matrix[i][j]; } } int sum2 = 0 ; for (int j = 0 ; j < N; ++j) { for (int i = 0 ; i < N; ++i) { sum2 += matrix[i][j]; } } for (int i = 0 ; i < N; ++i) { delete [] matrix[i]; } delete [] matrix; } void prefetchExample () { const int N = 1000000 ; std::vector<int > data (N) ; for (int i = 0 ; i < N; ++i) { data[i] = i; } int sum = 0 ; for (int i = 0 ; i < N; ++i) { if (i + 64 < N) { __builtin_prefetch(&data[i + 64 ], 0 , 0 ); } sum += data[i]; } }
内存管理最佳实践 优先使用栈分配 :对于小对象和临时数据,优先使用栈分配,利用栈的高速访问特性。
合理使用静态分配 :对于全局常量和配置数据,使用静态分配,避免动态内存开销。
谨慎使用堆分配 :对于大型数据结构和动态大小的数据,使用堆分配,并通过智能指针管理。
使用内存池 :对于频繁分配和释放的小对象,使用内存池减少内存碎片和分配开销。
使用智能指针 :优先使用std::unique_ptr,仅在需要共享所有权时使用std::shared_ptr。
使用标准容器 :使用std::vector、std::string等标准容器管理动态内存,避免手动内存管理。
内存对齐 :合理使用alignas和std::aligned_alloc确保内存对齐,提高访问速度。
内存预分配 :对于已知大小的数据结构,使用reserve等方法预分配内存,避免多次扩容。
多线程内存管理 :使用线程本地存储和无锁数据结构减少线程间的内存竞争。
内存监控 :使用内存分析工具(如Valgrind、AddressSanitizer)检测内存泄漏和使用错误。
使用C++17 PMR :对于需要灵活内存管理的场景,使用std::pmr命名空间的内存资源。
自定义分配器 :对于特定场景,实现自定义分配器优化内存分配策略。
避免内存碎片 :通过内存池、对象池等技术减少内存碎片,提高内存利用率。
异常安全 :使用RAII和智能指针确保即使发生异常也能正确释放内存。
性能分析 :使用性能分析工具识别内存瓶颈,针对性地优化内存使用。
现代C++内存管理总结 现代C++提供了丰富的内存管理工具和技术,从低级的内存分配器到高级的智能指针和内存资源,使开发者能够根据具体场景选择最合适的内存管理策略。
内存管理技术栈 技术级别 工具/技术 适用场景 性能 安全性 低级 原始指针 + new/delete 特殊内存布局需求 高 低 低级 内存池/对象池 频繁分配小对象 极高 中 中级 智能指针 自动内存管理 中 高 中级 标准容器 动态大小数据结构 中 高 高级 内存分配器 自定义内存策略 高 中 高级 PMR 多态内存管理 中 高 高级 无锁数据结构 多线程场景 高 中
内存管理决策流程 确定内存需求 :分析数据大小、生命周期和访问模式。
选择分配方式 :
小对象、短生命周期:栈分配 大对象、长生命周期:堆分配 频繁分配:内存池 多线程共享:智能指针 选择管理工具 :
独占所有权:std::unique_ptr 共享所有权:std::shared_ptr 观察对象:std::weak_ptr 动态数组:std::vector 字符串:std::string 优化内存使用 :
监控和调试 :
通过合理运用这些内存管理技术和最佳实践,开发者可以编写更高效、更安全、更可维护的C++代码,充分发挥现代C++的性能优势。 std::enable_if_t<!std::is_array_v, UniquePtr> make_unique(Args&&… args) { return UniquePtr(new T(std::forward(args)…)); }
template std::enable_if_t<std::is_array_v, UniquePtr> make_unique(std::size_t size) { using ElemType = std::remove_extent_t; return UniquePtr(new ElemTypesize ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 **`std::shared_ptr`的深度实现**: `std::shared_ptr`的核心是引用计数和控制块,下面是一个更接近标准库实现的版本,包含了弱引用计数和线程安全的引用计数操作: ```cpp template <typename T> class SharedPtr { public: // 构造函数 constexpr SharedPtr() noexcept : control_block_(nullptr) {} explicit SharedPtr(T* ptr) { if (ptr) { control_block_ = new ControlBlock(ptr); } } // 拷贝构造函数 SharedPtr(const SharedPtr& other) noexcept { if (other.control_block_) { control_block_ = other.control_block_; control_block_->add_ref(); } } // 移动构造函数 SharedPtr(SharedPtr&& other) noexcept : control_block_(other.control_block_) { other.control_block_ = nullptr; } // 从weak_ptr构造 SharedPtr(const WeakPtr<T>& other) { if (!other.expired()) { control_block_ = other.control_block_; control_block_->add_ref(); } } // 析构函数 ~SharedPtr() { if (control_block_) { if (control_block_->release_ref() == 0) { delete control_block_; } } } // 拷贝赋值运算符 SharedPtr& operator=(const SharedPtr& other) noexcept { if (this != &other) { SharedPtr temp(other); swap(temp); } return *this; } // 移动赋值运算符 SharedPtr& operator=(SharedPtr&& other) noexcept { if (this != &other) { SharedPtr temp(std::move(other)); swap(temp); } return *this; } // 指针操作 T& operator*() const noexcept { return *control_block_->ptr_; } T* operator->() const noexcept { return control_block_->ptr_; } T* get() const noexcept { return control_block_ ? control_block_->ptr_ : nullptr; } // 引用计数 long use_count() const noexcept { return control_block_ ? control_block_->use_count() : 0; } // 检查是否是唯一所有者 bool unique() const noexcept { return use_count() == 1; } // 交换 void swap(SharedPtr& other) noexcept { std::swap(control_block_, other.control_block_); } // 重置 void reset() noexcept { SharedPtr().swap(*this); } template <typename U> void reset(U* ptr) { SharedPtr(ptr).swap(*this); } private: // 控制块 struct ControlBlock { T* ptr_; std::atomic<long> use_count_; std::atomic<long> weak_count_; ControlBlock(T* ptr) : ptr_(ptr), use_count_(1), weak_count_(1) {} ~ControlBlock() { delete ptr_; } void add_ref() { ++use_count_; } long release_ref() { long count = --use_count_; if (count == 0) { // 释放资源 delete ptr_; ptr_ = nullptr; // 检查弱引用计数 if (--weak_count_ == 0) { delete this; } } return count; } void add_weak_ref() { ++weak_count_; } long release_weak_ref() { long count = --weak_count_; if (count == 0 && use_count_ == 0) { delete this; } return count; } long use_count() const { return use_count_.load(); } long weak_count() const { return weak_count_.load(); } }; ControlBlock* control_block_; // 用于weak_ptr的访问 friend class WeakPtr<T>; }; // weak_ptr的实现 template <typename T> class WeakPtr { public: // 构造函数 constexpr WeakPtr() noexcept : control_block_(nullptr) {} // 从shared_ptr构造 WeakPtr(const SharedPtr<T>& other) noexcept { if (other.control_block_) { control_block_ = other.control_block_; control_block_->add_weak_ref(); } } // 拷贝构造函数 WeakPtr(const WeakPtr& other) noexcept { if (other.control_block_) { control_block_ = other.control_block_; control_block_->add_weak_ref(); } } // 移动构造函数 WeakPtr(WeakPtr&& other) noexcept : control_block_(other.control_block_) { other.control_block_ = nullptr; } // 析构函数 ~WeakPtr() { if (control_block_) { control_block_->release_weak_ref(); } } // 赋值运算符 WeakPtr& operator=(const WeakPtr& other) noexcept { if (this != &other) { WeakPtr temp(other); swap(temp); } return *this; } WeakPtr& operator=(WeakPtr&& other) noexcept { if (this != &other) { WeakPtr temp(std::move(other)); swap(temp); } return *this; } WeakPtr& operator=(const SharedPtr<T>& other) noexcept { WeakPtr temp(other); swap(temp); return *this; } // 检查是否过期 bool expired() const noexcept { return use_count() == 0; } // 获取shared_ptr SharedPtr<T> lock() const noexcept { if (expired()) { return SharedPtr<T>(); } return SharedPtr<T>(*this); } // 引用计数 long use_count() const noexcept { return control_block_ ? control_block_->use_count() : 0; } // 交换 void swap(WeakPtr& other) noexcept { std::swap(control_block_, other.control_block_); } private: typename SharedPtr<T>::ControlBlock* control_block_; // 用于shared_ptr的访问 friend class SharedPtr<T>; }; // 辅助函数 template <typename T, typename... Args> SharedPtr<T> make_shared(Args&&... args) { // 优化:将控制块和对象在同一块内存中分配 struct ControlBlockWithObject : public SharedPtr<T>::ControlBlock { std::aligned_storage_t<sizeof(T), alignof(T)> storage; ControlBlockWithObject(Args&&... args) : ControlBlock(nullptr) { ptr_ = new (&storage) T(std::forward<Args>(args)...); } ~ControlBlockWithObject() { // 析构函数由基类调用 } }; auto cb = new ControlBlockWithObject(std::forward<Args>(args)...); SharedPtr<T> ptr; ptr.control_block_ = cb; return ptr; }
智能指针的高级特性 智能指针的类型转换 C++标准库提供了三种智能指针的类型转换函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <memory> auto basePtr = std::make_shared <Base>();auto derivedPtr = std::static_pointer_cast <Derived>(basePtr);auto basePtr2 = std::make_shared <Base>();auto derivedPtr2 = std::dynamic_pointer_cast <Derived>(basePtr2);if (derivedPtr2) { } else { } std::shared_ptr<const int > constPtr = std::make_shared <int >(42 ); std::shared_ptr<int > nonConstPtr = std::const_pointer_cast <int >(constPtr); *nonConstPtr = 100 ;
智能指针与自定义分配器 C++11及以上版本支持为shared_ptr指定自定义分配器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <memory> #include <vector> class MyAllocator {public : using value_type = char ; MyAllocator () = default ; template <typename U> MyAllocator (const MyAllocator&) noexcept {} char * allocate (std::size_t n) { std::cout << "Allocating " << n << " bytes" << std::endl; return static_cast <char *>(std::malloc (n)); } void deallocate (char * p, std::size_t n) noexcept { std::cout << "Deallocating " << n << " bytes" << std::endl; std::free (p); } }; void useCustomAllocator () { auto deleter = [](int * p) { std::cout << "Deleting int: " << *p << std::endl; delete p; }; std::allocator_arg_t arg; MyAllocator alloc; std::shared_ptr<int > p ( std::allocator_arg, alloc, new int (42 ), deleter ) ; std::cout << *p << std::endl; }
智能指针的性能优化技巧 优先使用std::make_unique和std::make_shared :
它们更安全,避免了异常安全问题 std::make_shared更高效,因为它只分配一次内存避免不必要的智能指针拷贝 :
对于shared_ptr,拷贝操作会增加引用计数,有一定开销 尽量使用引用或指针传递智能指针 合理使用移动语义 :
对于unique_ptr,使用std::move转移所有权 对于shared_ptr,移动操作比拷贝操作更高效 使用std::weak_ptr作为观察者 :
当只需要观察对象而不延长其生命周期时,使用weak_ptr 避免使用shared_ptr导致的不必要的引用计数 避免循环引用 :
使用weak_ptr打破循环引用 考虑使用所有权明确的设计,如父子关系中父对象拥有子对象 合理选择智能指针类型 :
当资源只需要一个所有者时,使用unique_ptr 当资源需要多个所有者时,使用shared_ptr 当只需要观察资源时,使用weak_ptr 智能指针的最佳实践和陷阱 最佳实践 :
始终使用智能指针管理动态内存 :
避免手动调用new和delete 减少内存泄漏的风险 使用std::make_unique和std::make_shared创建智能指针 :
优先使用std::unique_ptr :
零开销,所有权明确 当需要共享所有权时,再考虑std::shared_ptr 使用std::weak_ptr解决循环引用 :
避免裸指针与智能指针混用 :
不要让裸指针的生命周期超过智能指针的生命周期 尽量使用智能指针的get()方法获取裸指针,且只在必要时使用 使用自定义删除器管理非内存资源 :
常见陷阱 :
循环引用 :
两个或多个shared_ptr相互引用,导致引用计数永远不为0 解决方案:使用weak_ptr打破循环 裸指针赋值给多个智能指针 :
同一个裸指针被多个智能指针管理,导致重复释放 解决方案:始终使用std::make_unique或std::make_shared,或确保只有一个智能指针管理资源 智能指针的析构函数抛出异常 :
如果智能指针的删除器抛出异常,可能导致程序终止 解决方案:确保删除器不抛出异常 过度使用shared_ptr :
所有对象都使用shared_ptr管理,导致不必要的引用计数开销 解决方案:优先使用unique_ptr,只在需要共享所有权时使用shared_ptr shared_ptr的线程安全性误解 :
认为shared_ptr管理的对象也是线程安全的 解决方案:shared_ptr只保证引用计数的线程安全,对象的线程安全需要额外的同步措施 智能指针与数组的误用 :
使用std::shared_ptr<int>管理数组,导致使用delete而不是delete[] 解决方案:在C++17及以上版本使用std::shared_ptr<int[]>,在C++14及以下版本使用自定义删除器 智能指针与多线程 std::shared_ptr的线程安全性 :
引用计数的操作是线程安全的(原子操作) 多个线程可以同时读取和修改同一个shared_ptr的引用计数 但对shared_ptr所管理对象的访问不是线程安全的,需要额外的同步措施 std::unique_ptr的线程安全性 :
不提供线程安全保证 同一时间只能有一个线程访问同一个unique_ptr 可以通过移动语义在不同线程间传递所有权 多线程环境下的智能指针使用 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <memory> #include <thread> #include <mutex> #include <vector> class SharedData {public : SharedData (int value) : value_ (value) {} void increment () { std::lock_guard<std::mutex> lock (mutex_) ; ++value_; } int get () const { std::lock_guard<std::mutex> lock (mutex_) ; return value_; } private : mutable std::mutex mutex_; int value_; }; void threadFunction (std::shared_ptr<SharedData> data, int iterations) { for (int i = 0 ; i < iterations; ++i) { data->increment (); } } void processData (std::unique_ptr<int > data) { std::cout << "Processing data: " << *data << std::endl; } int main () { auto sharedData = std::make_shared <SharedData>(0 ); std::vector<std::thread> threads; for (int i = 0 ; i < 4 ; ++i) { threads.emplace_back (threadFunction, sharedData, 100000 ); } for (auto & thread : threads) { thread.join (); } std::cout << "Final value: " << sharedData->get () << std::endl; std::thread t (processData, std::make_unique<int >(42 )) ; t.join (); return 0 ; }
线程安全的智能指针包装 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <memory> #include <mutex> template <typename T>class ThreadSafeSharedPtr {public : ThreadSafeSharedPtr () = default ; template <typename ... Args> explicit ThreadSafeSharedPtr (Args&&... args) : ptr_(std::make_shared<T>(std::forward<Args>(args)...)) { } template <typename Func> auto withLock (Func&& func) { std::lock_guard<std::mutex> lock (mutex_) ; return func (*ptr_); } template <typename Func> auto withLockMut (Func&& func) { std::lock_guard<std::mutex> lock (mutex_) ; return func (*ptr_); } private : std::shared_ptr<T> ptr_; mutable std::mutex mutex_; }; void useThreadSafeSharedPtr () { ThreadSafeSharedPtr<int > value (42 ) ; int currentValue = value.withLock ([](const int & v) { return v; }); value.withLockMut ([](int & v) { v = 100 ; }); }
RAII(资源获取即初始化) RAII的核心概念与设计哲学 RAII(Resource Acquisition Is Initialization)是C++中最基本、最重要的资源管理技术,其核心思想是将资源的生命周期与对象的生命周期绑定,通过对象的构造和析构来自动管理资源。
RAII的基本原理 :
资源获取时初始化 :在构造函数中获取资源(如内存、文件句柄、网络连接等)资源在对象生命周期内保持有效 :只要对象存在,资源就有效资源在对象销毁时释放 :在析构函数中自动释放资源RAII的理论基础 :
栈展开(Stack Unwinding) :当异常发生时,C++会自动展开栈,调用所有局部对象的析构函数析构函数的保证执行 :无论函数是正常返回还是因异常退出,局部对象的析构函数都会被调用作用域约束 :RAII对象的作用域决定了资源的生命周期,提供了清晰的资源管理边界RAII的优势 :
自动资源管理 :无需手动释放资源,避免资源泄漏异常安全性 :即使发生异常,析构函数也会被调用,确保资源释放代码简洁 :将资源管理逻辑集中在构造和析构函数中,提高代码可读性作用域控制 :通过对象的作用域控制资源的生命周期,避免悬空资源可组合性 :RAII对象可以组合使用,形成更复杂的资源管理结构RAII的高级应用场景 RAII是C++中最基本的资源管理技术,几乎所有C++代码都应该使用RAII来管理资源。以下是RAII的常见高级应用场景:
内存管理 :使用智能指针(std::unique_ptr、std::shared_ptr)自动管理动态内存文件操作 :使用RAII包装器管理文件句柄,确保文件正确关闭网络连接 :使用RAII包装器管理网络套接字,确保连接正确关闭同步原语 :使用std::lock_guard、std::unique_lock等管理互斥锁,确保锁的正确释放数据库连接 :使用RAII包装器管理数据库连接,确保连接正确关闭线程管理 :使用std::thread的RAII语义管理线程,确保线程资源正确释放事务管理 :使用RAII管理数据库事务,确保事务正确提交或回滚其他系统资源 :如注册表句柄、GDI对象、COM接口等自定义RAII类的高级实现 通用RAII包装器的深度实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 template <typename Resource, typename Deleter = std::default_delete<Resource>>class RAIIWrapper {public : RAIIWrapper (Resource resource, Deleter deleter = Deleter{}) : resource_ (resource), deleter_ (std::move (deleter)), owns_ (true ) {} RAIIWrapper (RAIIWrapper&& other) noexcept : resource_ (std::move (other.resource_)), deleter_ (std::move (other.deleter_)), owns_ (other.owns_) { other.owns_ = false ; } ~RAIIWrapper () { if (owns_) { deleter_ (resource_); } } RAIIWrapper (const RAIIWrapper&) = delete ; RAIIWrapper& operator =(const RAIIWrapper&) = delete ; RAIIWrapper& operator =(RAIIWrapper&& other) noexcept { if (this != &other) { reset (); resource_ = std::move (other.resource_); deleter_ = std::move (other.deleter_); owns_ = other.owns_; other.owns_ = false ; } return *this ; } Resource get () const noexcept { return resource_; } Resource release () noexcept { owns_ = false ; return resource_; } void reset (Resource new_resource = Resource{}) noexcept { if (owns_) { deleter_ (resource_); } resource_ = new_resource; owns_ = true ; } void swap (RAIIWrapper& other) noexcept { std::swap (resource_, other.resource_); std::swap (deleter_, other.deleter_); std::swap (owns_, other.owns_); } explicit operator bool () const noexcept { return owns_; } bool owns () const noexcept { return owns_; } decltype (auto ) operator *() const { return *resource_; } decltype (auto ) operator ->() const { return resource_; } private : Resource resource_; Deleter deleter_; bool owns_; }; template <typename Resource, typename Deleter>RAIIWrapper<Resource, Deleter> makeRAII (Resource resource, Deleter deleter) { return RAIIWrapper <Resource, Deleter>(resource, deleter); } template <typename T>RAIIWrapper<T*, std::default_delete<T>> makeRAII (T* ptr) { return RAIIWrapper<T*, std::default_delete<T>>(ptr); } void useRAIIWrapper () { auto file = makeRAII ( std::fopen ("test.txt" , "w" ), [](FILE* f) { if (f) std::fclose (f); } ); auto memory = makeRAII (new int [10 ]); std::mutex mtx; auto lock = makeRAII ( &mtx, [](std::mutex* m) { m->unlock (); } ); lock.get ()->lock (); }
文件处理器的高级实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 class FileHandler {public : FileHandler (const char * filename, const char * mode) { file_ = std::fopen (filename, mode); if (!file_) { throw std::system_error ( errno, std::generic_category (), "Failed to open file" ); } } FileHandler (FileHandler&& other) noexcept : file_ (other.file_) { other.file_ = nullptr ; } ~FileHandler () { if (file_) { std::fclose (file_); file_ = nullptr ; } } FileHandler (const FileHandler&) = delete ; FileHandler& operator =(const FileHandler&) = delete ; FileHandler& operator =(FileHandler&& other) noexcept { if (this != &other) { reset (); file_ = other.file_; other.file_ = nullptr ; } return *this ; } FILE* get () const noexcept { return file_; } size_t write (const char * data, size_t size) { if (!file_) { throw std::runtime_error ("File not open" ); } return std::fwrite (data, 1 , size, file_); } size_t read (char * buffer, size_t size) { if (!file_) { throw std::runtime_error ("File not open" ); } return std::fread (buffer, 1 , size, file_); } int seek (long offset, int origin) { if (!file_) { throw std::runtime_error ("File not open" ); } return std::fseek (file_, offset, origin); } int flush () { if (!file_) { throw std::runtime_error ("File not open" ); } return std::fflush (file_); } void reset () noexcept { if (file_) { std::fclose (file_); file_ = nullptr ; } } FILE* release () noexcept { FILE* temp = file_; file_ = nullptr ; return temp; } explicit operator bool () const noexcept { return file_ != nullptr ; } bool is_open () const noexcept { return file_ != nullptr ; } private : FILE* file_ = nullptr ; }; void processFile (const char * inputFile, const char * outputFile) { FileHandler input (inputFile, "r" ) ; FileHandler output (outputFile, "w" ) ; char buffer[1024 ]; size_t bytesRead; while ((bytesRead = input.read (buffer, sizeof (buffer))) > 0 ) { output.write (buffer, bytesRead); } }
RAII与异常安全性的深度分析 RAII是实现异常安全的关键技术。当程序抛出异常时,栈会被展开(stack unwinding),所有局部对象的析构函数都会被调用,确保资源被正确释放。
异常安全级别 :
基本保证 :即使发生异常,程序也能保持在一致的状态,没有资源泄漏强保证 :如果发生异常,程序会回滚到操作前的状态,仿佛操作从未发生无抛出保证 :操作永远不会抛出异常RAII实现强异常安全 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void unsafeSwap (std::string& a, std::string& b) { std::string temp = a; a = b; b = temp; } void safeSwap (std::string& a, std::string& b) { std::string temp = a; try { a = b; b = std::move (temp); } catch (...) { a = std::move (temp); throw ; } } void simplerSafeSwap (std::string& a, std::string& b) { std::string temp = std::move (a); a = std::move (b); b = std::move (temp); }
RAII与智能指针的异常安全 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void unsafeFunction () { int * p = new int (42 ); riskyOperation (); delete p; } void safeFunction () { std::unique_ptr<int > p (new int (42 )) ; riskyOperation (); } void saferFunction () { auto p = std::make_unique <int >(42 ); riskyOperation (); }
作用域守卫(Scope Guard)的高级实现 作用域守卫是RAII的一种特殊形式,用于在作用域结束时执行特定的清理操作。C++11及以上版本可以使用lambda表达式和模板实现通用的作用域守卫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 template <typename Func>class ScopeGuard {public : explicit ScopeGuard (Func&& func) : func_(std::forward<Func>(func)), active_(true) { } ScopeGuard (ScopeGuard&& other) noexcept : func_ (std::move (other.func_)), active_ (other.active_) { other.active_ = false ; } ~ScopeGuard () { if (active_) { func_ (); } } ScopeGuard (const ScopeGuard&) = delete ; ScopeGuard& operator =(const ScopeGuard&) = delete ; void dismiss () noexcept { active_ = false ; } private : Func func_; bool active_; }; template <typename Func>ScopeGuard<Func> makeScopeGuard (Func&& func) { return ScopeGuard <Func>(std::forward<Func>(func)); } #define SCOPE_EXIT \ auto CONCAT(scope_guard_, __LINE__) = makeScopeGuard([&]() #define CONCAT(a, b) CONCAT_IMPL(a, b) #define CONCAT_IMPL(a, b) a##b void useScopeGuard () { FILE* file = std::fopen ("test.txt" , "w" ); if (!file) { return ; } SCOPE_EXIT { if (file) { std::fclose (file); std::cout << "File closed by scope guard" << std::endl; } }); std::fprintf (file, "Hello, Scope Guard!" ); } void processTransaction (Database& db) { db.beginTransaction (); auto rollbackGuard = makeScopeGuard ([&db]() { db.rollbackTransaction (); std::cout << "Transaction rolled back" << std::endl; }); rollbackGuard.dismiss (); db.commitTransaction (); std::cout << "Transaction committed" << std::endl; }
RAII与移动语义的深度结合 C++11引入的移动语义极大地增强了RAII的能力,使得资源可以在不同对象之间高效转移,而不需要复制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 class NetworkConnection {public : NetworkConnection (const char * host, int port) { sock_ = socket (AF_INET, SOCK_STREAM, 0 ); if (sock_ < 0 ) { throw std::system_error ( errno, std::generic_category (), "Failed to create socket" ); } sockaddr_in addr{}; addr.sin_family = AF_INET; addr.sin_port = htons (port); if (inet_pton (AF_INET, host, &addr.sin_addr) <= 0 ) { close (sock_); throw std::system_error ( errno, std::generic_category (), "Invalid address" ); } if (connect (sock_, reinterpret_cast <sockaddr*>(&addr), sizeof (addr)) < 0 ) { close (sock_); throw std::system_error ( errno, std::generic_category (), "Failed to connect" ); } std::cout << "Connected to " << host << ":" << port << std::endl; } NetworkConnection (NetworkConnection&& other) noexcept : sock_ (other.sock_) { other.sock_ = -1 ; std::cout << "Connection moved" << std::endl; } ~NetworkConnection () { if (sock_ >= 0 ) { close (sock_); std::cout << "Connection closed" << std::endl; } } NetworkConnection (const NetworkConnection&) = delete ; NetworkConnection& operator =(const NetworkConnection&) = delete ; NetworkConnection& operator =(NetworkConnection&& other) noexcept { if (this != &other) { if (sock_ >= 0 ) { close (sock_); } sock_ = other.sock_; other.sock_ = -1 ; std::cout << "Connection assigned via move" << std::endl; } return *this ; } ssize_t send (const void * data, size_t size) { if (sock_ < 0 ) { throw std::runtime_error ("Not connected" ); } return ::send (sock_, data, size, 0 ); } ssize_t recv (void * buffer, size_t size) { if (sock_ < 0 ) { throw std::runtime_error ("Not connected" ); } return ::recv (sock_, buffer, size, 0 ); } explicit operator bool () const noexcept { return sock_ >= 0 ; } bool is_connected () const noexcept { return sock_ >= 0 ; } private : int sock_ = -1 ; }; NetworkConnection createConnection () { return NetworkConnection ("localhost" , 8080 ); } void useConnection () { NetworkConnection conn = createConnection (); NetworkConnection conn2 = std::move (conn); }
RAII与线程安全的深度集成 RAII可以与互斥锁结合,实现线程安全的资源访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 class ThreadSafeData {public : ThreadSafeData (int value) : value_ (value) {} int get () const { std::lock_guard<std::mutex> lock (mutex_) ; return value_; } void set (int value) { std::lock_guard<std::mutex> lock (mutex_) ; value_ = value; } template <typename Func> auto modify (Func&& func) { std::lock_guard<std::mutex> lock (mutex_) ; return func (value_); } private : mutable std::mutex mutex_; int value_; }; void useThreadSafeData () { ThreadSafeData data (42 ) ; std::vector<std::thread> threads; for (int i = 0 ; i < 10 ; ++i) { threads.emplace_back ([&data, i]() { data.modify ([i](int & value) { value += i; return value; }); }); } for (auto & thread : threads) { thread.join (); } std::cout << "Final value: " << data.get () << std::endl; } template <typename T>class ThreadSafeWrapper {public : template <typename ... Args> ThreadSafeWrapper (Args&&... args) : data_ (std::forward<Args>(args)...) {} template <typename Func> auto withLock (Func&& func) const { std::lock_guard<std::mutex> lock (mutex_) ; return func (data_); } template <typename Func> auto withLockMut (Func&& func) { std::lock_guard<std::mutex> lock (mutex_) ; return func (data_); } private : T data_; mutable std::mutex mutex_; }; void useThreadSafeWrapper () { ThreadSafeWrapper<std::vector<int >> safeVector ({1 , 2 , 3 , 4 , 5 }); int sum = safeVector.withLock ([](const std::vector<int >& v) { return std::accumulate (v.begin (), v.end (), 0 ); }); safeVector.withLockMut ([](std::vector<int >& v) { v.push_back (6 ); v.push_back (7 ); }); safeVector.withLock ([](const std::vector<int >& v) { for (int num : v) { std::cout << num << " " ; } std::cout << std::endl; }); }
RAII的最佳实践与性能优化 始终使用RAII管理资源 :
对于任何需要手动释放的资源,都应该使用RAII包装器 避免手动调用释放函数,减少资源泄漏的风险 优先使用标准库的RAII工具 :
使用std::unique_ptr、std::shared_ptr管理内存 使用std::lock_guard、std::unique_lock管理互斥锁 使用std::fstream管理文件 使用std::thread管理线程 实现自定义RAII类时的注意事项 :
禁止拷贝 :对于独占资源的RAII类,应该禁止拷贝操作支持移动 :对于需要转移所有权的RAII类,应该支持移动操作异常安全 :构造函数应该在获取资源失败时抛出异常,析构函数不应该抛出异常状态管理 :应该跟踪资源的所有权状态,避免重复释放资源验证 :在构造函数中应该验证资源获取是否成功使用作用域控制资源生命周期 :
通过局部变量的作用域控制资源的生命周期 使用临时作用域精确控制资源的释放时机 结合移动语义提高性能 :
对于大型资源,使用移动语义减少拷贝开销 实现移动构造函数和移动赋值运算符 使用std::move转移资源所有权 使用作用域守卫处理复杂的清理逻辑 :
对于需要在作用域结束时执行的清理操作,使用作用域守卫 支持条件清理(如事务回滚) 测试异常安全性 :
确保在异常情况下资源能够被正确释放 测试各种异常场景,确保程序状态一致 文档化资源管理策略 :
明确记录RAII类的资源管理策略 说明拷贝和移动语义的行为 性能优化策略 :
内联析构函数 :对于简单的RAII类,析构函数可以内联,减少函数调用开销小对象优化 :对于小型RAII对象,考虑使用小对象优化,减少堆分配避免虚函数 :对于性能关键的RAII类,避免使用虚函数,减少运行时开销使用 noexcept :对于不会抛出异常的移动操作,使用noexcept标记,提高性能RAII的核心价值 :
RAII不仅仅是一种技术,更是一种设计哲学,它将资源管理的责任交给了对象的生命周期管理,使得代码更加简洁、安全、可维护。通过RAII,我们可以:
消除资源泄漏 :确保资源在不再需要时被正确释放提高异常安全性 :即使在异常情况下也能保持程序的一致性简化代码 :将资源管理逻辑集中在一处,提高代码可读性增强可维护性 :减少手动资源管理的错误,提高代码质量促进良好的设计 :鼓励将资源管理与业务逻辑分离在现代C++中,RAII已经成为资源管理的标准做法,与智能指针、移动语义等特性相结合,为C++程序员提供了强大、安全、高效的资源管理工具。掌握RAII的精髓,是成为优秀C++程序员的必备条件。 // 自动解锁 }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #### RAII的最佳实践 1. **始终使用RAII管理资源**:对于任何需要手动释放的资源,都应该使用RAII 2. **遵循零规则**:如果类不需要手动管理资源,不要定义析构函数、拷贝构造函数和移动构造函数 3. **遵循三/五规则**:如果类需要手动管理资源,应该正确实现析构函数、拷贝构造函数、移动构造函数、拷贝赋值运算符和移动赋值运算符 4. **使用智能指针**:对于内存资源,优先使用标准库的智能指针 5. **避免裸指针**:尽量减少使用裸指针,特别是作为函数参数和返回值 6. **明确资源所有权**:在设计API时,明确资源的所有权转移语义 7. **使用`noexcept`**:对于析构函数和移动操作,使用`noexcept`标记,提高性能和异常安全性 8. **初始化成员变量**:在声明时初始化成员变量,避免未初始化的资源指针 **RAII的设计原则**: - **单一职责**:每个RAII类只管理一种资源 - **资源获取的原子性**:构造函数应该要么成功获取资源,要么抛出异常,不应该留下部分初始化的对象 - **资源释放的安全性**:析构函数不应该抛出异常,否则会导致程序终止 - **移动语义的支持**:对于需要转移资源所有权的场景,应该支持移动操作 ## 资源管理工具 ### 标准库中的RAII工具 C++标准库提供了多种RAII工具,用于管理各种资源: #### `std::lock_guard` `std::lock_guard`是一个互斥锁的RAII包装器,在构造时锁定互斥锁,在析构时自动解锁,确保即使发生异常也能正确释放锁。 ```cpp #include <mutex> #include <vector> std::mutex dataMutex; std::vector<int> sharedData; void addData(int value) { std::lock_guard<std::mutex> lock(dataMutex); // 构造时锁定 sharedData.push_back(value); // 析构时自动解锁 }
std::lock_guard的实现原理 :
构造函数接受一个互斥锁引用,并立即调用lock()方法 析构函数调用互斥锁的unlock()方法 禁止拷贝和移动,确保锁的所有权不被转移 std::unique_lockstd::unique_lock是一个更灵活的互斥锁RAII包装器,支持延迟锁定、超时锁定和手动锁定/解锁操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <mutex> #include <chrono> #include <thread> std::mutex mtx; void tryLockWithTimeout () { std::unique_lock<std::mutex> lock (mtx, std::defer_lock) ; if (lock.try_lock ()) { std::cout << "Lock acquired immediately" << std::endl; } else { std::cout << "Lock not available, trying with timeout..." << std::endl; if (lock.try_lock_for (std::chrono::seconds (1 ))) { std::cout << "Lock acquired after timeout" << std::endl; } else { std::cout << "Failed to acquire lock" << std::endl; return ; } } lock.unlock (); std::cout << "Lock released manually" << std::endl; lock.lock (); std::cout << "Lock reacquired" << std::endl; }
std::unique_lock的特点 :
大小通常为两个指针(16字节),比std::lock_guard大 支持移动操作,可以在不同作用域间转移锁的所有权 提供owns_lock()方法检查是否持有锁 可以与条件变量配合使用 std::scoped_lockC++17引入的std::scoped_lock可以同时锁定多个互斥锁,使用死锁避免算法(如顺序锁定)确保不会发生死锁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <mutex> #include <iostream> std::mutex mtx1, mtx2, mtx3; void processData () { std::scoped_lock lock (mtx1, mtx2, mtx3) ; std::cout << "All locks acquired" << std::endl; }
std::scoped_lock的优势 :
避免手动锁定多个互斥锁时可能出现的死锁 语法简洁,一行代码锁定任意数量的互斥锁 自动处理所有锁的释放,即使发生异常 std::unique_ptr和std::shared_ptr智能指针也是标准库中的RAII工具,用于管理动态内存:
std::unique_ptr :独占所有权的智能指针,适用于大多数场景std::shared_ptr :共享所有权的智能指针,适用于需要多个所有者的场景std::weak_ptr :不增加引用计数的智能指针,用于解决循环引用问题其他RAII工具 std::fstream :管理文件流,自动打开和关闭文件std::thread :管理线程,析构时如果线程仍在运行会调用std::terminate()std::lock_guard 的变体:如std::lock_guard<std::recursive_mutex>std::condition_variable :与互斥锁配合使用,管理线程同步内存管理最佳实践 1. 优先使用智能指针 推荐做法 :
对于独占所有权的场景,使用std::unique_ptr 对于共享所有权的场景,使用std::shared_ptr 避免使用原始指针管理动态内存 性能考虑 :
std::unique_ptr的性能开销极小,与原始指针接近std::shared_ptr有引用计数开销,适用于确实需要共享所有权的场景2. 使用std::make_unique和std::make_shared 为什么推荐 :
异常安全 :避免在分配内存和构造对象之间发生异常导致的内存泄漏性能优化 :std::make_shared只进行一次内存分配(同时分配对象和控制块)代码简洁 :语法更简洁,减少重复代码使用示例 :
1 2 3 4 5 6 7 auto p1 = std::make_unique <int >(42 );auto p2 = std::make_shared <std::string>("Hello" );std::unique_ptr<int > p3 (new int (42 )) ;std::shared_ptr<std::string> p4 (new std::string("Hello" )) ;
3. 避免循环引用 循环引用的危害 :
导致内存泄漏,因为引用计数永远不会减为零 增加内存使用,延缓资源释放 解决方案 :
使用std::weak_ptr打破循环引用 重新设计数据结构,避免循环依赖 示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Node {public : std::shared_ptr<Node> next; std::weak_ptr<Node> prev; ~Node () { std::cout << "Node destroyed" << std::endl; } }; void createList () { auto n1 = std::make_shared <Node>(); auto n2 = std::make_shared <Node>(); n1->next = n2; n2->prev = n1; }
4. 合理使用RAII RAII的应用场景 :
内存管理(智能指针) 文件操作(std::fstream) 同步原语(std::lock_guard) 网络连接(自定义RAII包装器) 数据库连接(自定义RAII包装器) RAII设计原则 :
每个RAII类只管理一种资源 构造函数获取资源,析构函数释放资源 禁止拷贝或正确实现拷贝语义 支持移动语义以提高性能 5. 避免内存泄漏 常见内存泄漏原因 :
未配对使用new和delete 异常导致的资源未释放 循环引用(std::shared_ptr) 全局对象的资源未释放 预防措施 :
使用智能指针和RAII 定期使用内存分析工具检测 编写单元测试验证资源释放 采用静态分析工具检查潜在问题 6. 避免悬空指针和野指针 悬空指针 :指向已释放内存的指针野指针 :未初始化或指向无效内存的指针
预防措施 :
使用智能指针自动管理生命周期 释放内存后将指针置为nullptr 总是初始化指针 采用现代C++风格,减少裸指针的使用 7. 内存安全编程 最佳实践 :
使用标准库容器代替手动内存管理 避免使用reinterpret_cast进行危险的类型转换 使用std::span(C++20)处理数组,避免越界访问 采用边界检查工具(如AddressSanitizer) 内存优化 内存分配策略 内存分配是影响程序性能的重要因素,合理的分配策略可以显著提高程序效率:
减少动态内存分配 :
优先使用栈分配和静态分配 对于小对象,使用栈分配 对于固定大小的全局数据,使用静态分配 批量分配 :
使用std::vector等容器,一次性分配足够的内存 预留空间(reserve)减少重新分配 批量处理数据,减少分配次数 内存池 :
对于频繁分配和释放的小对象,使用内存池 预分配大块内存,然后分割成小块使用 减少内存碎片,提高分配速度 对象池 :
对于固定大小的对象,使用对象池 复用对象,避免频繁构造和析构 适用于对象创建成本高的场景 多态内存资源(C++17+) :
使用std::pmr命名空间中的内存资源管理工具 自定义内存分配策略 适用于需要特殊内存管理的场景 内存池实现示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 class MemoryPool {public : MemoryPool (size_t blockSize, size_t blockCount) : blockSize (blockSize), blockCount (blockCount) { pool = static_cast <char *>(std::malloc (blockSize * blockCount)); if (!pool) { throw std::bad_alloc{}; } for (size_t i = 0 ; i < blockCount; ++i) { char * block = pool + i * blockSize; *reinterpret_cast <char **>(block) = freeList; freeList = block; } } ~MemoryPool () { std::free (pool); } void * allocate () { if (!freeList) { throw std::bad_alloc{}; } void * block = freeList; freeList = *reinterpret_cast <char **>(freeList); return block; } void deallocate (void * ptr) { if (!ptr) return ; *reinterpret_cast <char **>(ptr) = freeList; freeList = static_cast <char *>(ptr); } MemoryPool (const MemoryPool&) = delete ; MemoryPool& operator =(const MemoryPool&) = delete ; MemoryPool (MemoryPool&&) = delete ; MemoryPool& operator =(MemoryPool&&) = delete ; private : size_t blockSize; size_t blockCount; char * pool; char * freeList; }; MemoryPool pool (sizeof (int ), 100 ) ;int * p = static_cast <int *>(pool.allocate ());*p = 42 ; pool.deallocate (p);
多态内存资源(C++17+) C++17引入的std::pmr命名空间提供了灵活的内存资源管理机制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <memory_resource> #include <vector> #include <string> void usePolymorphicMemoryResources () { char buffer[1024 ]; std::pmr::monotonic_buffer_resource pool (buffer, sizeof (buffer)) ; std::pmr::vector<int > vec (&pool) ; for (int i = 0 ; i < 100 ; ++i) { vec.push_back (i); } std::pmr::synchronized_pool_resource syncPool; std::pmr::vector<std::pmr::string> strings (&syncPool) ; strings.push_back ("Hello" ); strings.push_back ("World" ); std::pmr::unsynchronized_pool_resource unsyncPool; std::pmr::vector<double > doubles (&unsyncPool) ; for (int i = 0 ; i < 50 ; ++i) { doubles.push_back (i * 1.1 ); } }
内存对齐 内存对齐是指变量或对象在内存中的起始地址必须是某个值的倍数,正确的对齐可以提高内存访问速度:
内存对齐的原因 :
硬件通常按字长访问内存,未对齐的访问需要多次内存操作 某些指令集要求数据必须对齐 提高缓存利用率 C++中的内存对齐控制 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 std::cout << "alignof(char): " << alignof (char ) << std::endl; std::cout << "alignof(int): " << alignof (int ) << std::endl; std::cout << "alignof(double): " << alignof (double ) << std::endl; alignas (16 ) int x; struct alignas (32 ) MyStruct { int a; double b; char c; }; std::cout << "sizeof(MyStruct): " << sizeof (MyStruct) << std::endl; std::cout << "alignof(MyStruct): " << alignof (MyStruct) << std::endl; union alignas (64 ) MyUnion { char data[64 ]; double value; };
内存对齐的性能影响 :
对齐的数据访问速度更快 但可能会增加内存使用(填充) 对于频繁访问的数据结构,对齐是值得的 内存使用分析 定期分析内存使用情况,找出优化机会:
分析工具 :
Valgrind :Linux下的内存分析工具套件
Memcheck :检测内存泄漏、越界访问等Massif :分析内存使用和分配情况Cachegrind :分析缓存使用情况AddressSanitizer :Google开发的内存错误检测工具
检测内存泄漏、越界访问、使用已释放内存等 集成到主流编译器(GCC、Clang、MSVC) 运行时开销比Valgrind小 Visual Studio Memory Profiler :Windows下的内存分析工具
Intel VTune Profiler :性能分析工具,包括内存分析
分析步骤 :
识别内存热点 :找出消耗内存最多的部分分析分配模式 :识别频繁的小内存分配检测内存泄漏 :确保所有资源都被正确释放优化内存使用 :根据分析结果进行优化内存优化的高级技术 对象布局优化 :
按大小排序成员变量,减少填充 使用位域节省空间 考虑使用std::compressed_pair减少空基类开销 字符串优化 :
使用短字符串优化(SSO)的字符串实现 对于固定长度的字符串,使用std::array<char, N> 避免不必要的字符串拷贝 容器优化 :
为容器预留足够空间(reserve) 选择合适的容器类型 考虑使用自定义分配器 缓存优化 :
数据局部性:将相关数据放在一起 避免伪共享:对齐数据避免缓存行冲突 预取数据:使用__builtin_prefetch等指令 内存映射 :
使用mmap(POSIX)或MapViewOfFile(Windows)处理大文件 减少内存拷贝,提高I/O性能 适用于需要随机访问大文件的场景 内存映射示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> #include <fstream> #include <vector> class MemoryMappedFile {public : MemoryMappedFile (const char * filename) { std::ifstream file (filename, std::ios::binary | std::ios::ate) ; if (file) { size = file.tellg (); data = new char [size]; file.seekg (0 ); file.read (data, size); } } ~MemoryMappedFile () { delete [] data; } const char * getData () const { return data; } size_t getSize () const { return size; } private : char * data = nullptr ; size_t size = 0 ; }; void processLargeFile (const char * filename) { MemoryMappedFile mmf (filename) ; const char * data = mmf.getData (); size_t size = mmf.getSize (); std::cout << "File size: " << size << " bytes" << std::endl; if (size > 0 ) { std::cout << "First byte: " << static_cast <int >(data[0 ]) << std::endl; } }
内存管理的未来发展 C++标准委员会持续改进内存管理机制,未来的发展方向包括:
更智能的内存分配器 :自适应分配策略,根据使用模式自动调整内存安全 :减少内存错误,提高程序安全性垃圾回收 :可选的垃圾回收机制,用于特定场景更灵活的内存资源管理 :扩展std::pmr功能硬件加速 :利用硬件特性提高内存管理效率作为C++程序员,了解这些发展趋势,掌握先进的内存管理技术,对于编写高性能、可靠的C++程序至关重要。
示例:内存管理的综合应用 自定义智能指针 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 template <typename T>class SmartPtr {public : explicit SmartPtr (T* ptr = nullptr ) : ptr(ptr) { } ~SmartPtr () { delete ptr; } SmartPtr (const SmartPtr&) = delete ; SmartPtr& operator =(const SmartPtr&) = delete ; SmartPtr (SmartPtr&& other) noexcept : ptr (other.ptr) { other.ptr = nullptr ; } SmartPtr& operator =(SmartPtr&& other) noexcept { if (this != &other) { delete ptr; ptr = other.ptr; other.ptr = nullptr ; } return *this ; } T& operator *() const { return *ptr; } T* operator ->() const { return ptr; } explicit operator bool () const { return ptr != nullptr ; } private : T* ptr; }; void useSmartPtr () { SmartPtr<int > p (new int (42 )) ; std::cout << *p << std::endl; SmartPtr<int > p2 = std::move (p); std::cout << *p2 << std::endl; }
资源管理类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 class DatabaseConnection {public : DatabaseConnection (const std::string& connectionString) { std::cout << "Connecting to database: " << connectionString << std::endl; connected = true ; } ~DatabaseConnection () { if (connected) { std::cout << "Disconnecting from database" << std::endl; connected = false ; } } DatabaseConnection (const DatabaseConnection&) = delete ; DatabaseConnection& operator =(const DatabaseConnection&) = delete ; DatabaseConnection (DatabaseConnection&& other) noexcept : connected (other.connected) { other.connected = false ; } DatabaseConnection& operator =(DatabaseConnection&& other) noexcept { if (this != &other) { if (connected) { std::cout << "Disconnecting from database" << std::endl; } connected = other.connected; other.connected = false ; } return *this ; } void executeQuery (const std::string& query) { if (connected) { std::cout << "Executing query: " << query << std::endl; } else { throw std::runtime_error ("Not connected to database" ); } } private : bool connected; }; void useDatabase () { try { DatabaseConnection db ("server=localhost;database=test" ) ; db.executeQuery ("SELECT * FROM users" ); } catch (const std::exception& e) { std::cerr << "Error: " << e.what () << std::endl; } }
总结 内存与资源管理是C++编程中的重要主题,直接关系到程序的性能和稳定性。通过合理使用动态内存分配、智能指针和RAII等技术,可以有效地管理内存和资源,避免内存泄漏、悬空指针等问题。
在现代C++中,应该优先使用智能指针和RAII来管理内存和资源,减少手动内存管理的错误。同时,合理的内存分配策略和内存优化技术可以提高程序的性能和内存使用效率。
通过本章的学习,读者应该掌握C++中的内存管理和资源管理技术,能够编写更加安全、高效的C++程序。