1. 动态内存分配的核心价值
作为一名有着十年C++开发经验的老程序员,我见过太多因为动态内存管理不当导致的程序崩溃和内存泄漏问题。动态内存分配是C++程序员必须掌握的硬核技能,它直接关系到程序的稳定性和性能表现。
在C++中,我们通常把内存分为栈(stack)和堆(heap)两大区域。栈内存由编译器自动管理,分配和释放都非常高效,但它有两个致命缺陷:一是大小固定,二生命周期与作用域绑定。这就导致了很多实际开发场景中栈内存无法满足需求:
- 当我们需要在运行时才能确定数组大小时
- 当我们需要创建生命周期超出当前作用域的对象时
- 当我们需要管理大量数据但栈空间有限时
提示:在32位系统上,默认栈大小通常只有1-2MB,而堆内存理论上可以占到进程可用虚拟内存的绝大部分。
2. 静态内存与动态内存的深度对比
2.1 栈内存的工作机制
栈内存的管理完全由编译器自动完成,遵循LIFO(后进先出)原则。每次函数调用时,编译器会在栈上为局部变量分配内存,函数返回时自动释放。这种机制效率极高,但灵活性很差。
cpp复制void stackExample() {
int a = 10; // 栈上分配4字节
char str[100]; // 栈上分配100字节
} // 函数结束,a和str自动释放
栈内存的特点:
- 分配/释放速度快(只需移动栈指针)
- 无需手动管理
- 大小固定(编译时确定)
- 生命周期与作用域绑定
- 空间有限(默认1-2MB)
2.2 堆内存的运作原理
堆内存由开发者手动管理,通过new/delete运算符向操作系统申请和释放内存。堆内存的分配过程要复杂得多:
- 程序调用new运算符
- 运行时库检查空闲内存链表
- 如果找到合适块则分配,否则向OS申请更多内存
- 更新内存管理数据结构
- 返回分配的内存地址
cpp复制void heapExample() {
int* p = new int; // 从堆分配4字节
*p = 20;
delete p; // 必须手动释放
}
堆内存的特点:
- 分配/释放速度较慢(涉及系统调用)
- 需要手动管理
- 大小灵活(运行时决定)
- 生命周期由开发者控制
- 空间大(取决于系统资源)
3. new运算符的完全指南
3.1 基础用法与内存初始化
new运算符的核心作用是向堆区申请内存并返回指针。根据初始化方式的不同,有几种常见用法:
cpp复制// 1. 默认初始化(基本类型不初始化,类类型调用默认构造函数)
int* p1 = new int; // 值未定义
// 2. 值初始化(C++11起支持)
int* p2 = new int(); // 初始化为0
int* p3 = new int(42); // 初始化为42
// 3. 数组初始化
int* arr1 = new int[5]; // 未初始化
int* arr2 = new int[5](); // 全部初始化为0
int* arr3 = new int[5]{1,2,3}; // 前三个初始化为1,2,3,其余为0
3.2 异常处理与nothrow版本
默认情况下,new在分配失败时会抛出std::bad_alloc异常。但在某些场景下,我们可能希望优雅地处理失败而不是抛出异常:
cpp复制#include <new> // 必须包含此头文件
// 传统异常处理方式
try {
int* p = new int[1000000000];
// 使用内存...
delete[] p;
} catch (const std::bad_alloc& e) {
std::cerr << "内存分配失败: " << e.what() << '\n';
}
// nothrow方式
int* p = new(std::nothrow) int[1000000000];
if (p == nullptr) {
std::cerr << "内存分配失败\n";
} else {
// 使用内存...
delete[] p;
}
3.3 对齐内存分配
在某些高性能场景中,我们需要确保内存地址按特定边界对齐。C++17引入了对齐版本的new:
cpp复制#include <iostream>
#include <new>
int main() {
// 分配对齐到64字节边界的内存
alignas(64) int* p = new int;
// 检查对齐情况
std::cout << "地址: " << p
<< " 对齐: " << alignof(*p)
<< " 模64: " << (reinterpret_cast<uintptr_t>(p) % 64)
<< std::endl;
delete p;
return 0;
}
4. delete的正确使用姿势
4.1 基本用法与常见陷阱
delete运算符用于释放new分配的内存,看似简单实则暗藏玄机:
cpp复制int* p = new int(10);
// 正确用法
delete p;
p = nullptr; // 重要:避免野指针
// 常见错误1:重复删除
// delete p; // 崩溃!
// 常见错误2:删除栈内存
int x = 20;
// delete &x; // 灾难性错误!
// 常见错误3:删除不完整类型
class Incomplete;
Incomplete* inc = nullptr;
// delete inc; // 未定义行为
4.2 数组删除的特殊性
使用new[]分配的数组必须用delete[]释放,这一点绝对不能混淆:
cpp复制// 正确示例
int* arr = new int[10];
delete[] arr; // 必须使用delete[]
arr = nullptr;
// 危险示例
int* arr2 = new int[10];
// delete arr2; // 未定义行为!可能部分释放或崩溃
为什么必须配对使用?因为new[]会在分配的内存块头部存储数组大小等信息,delete[]需要这些信息来正确释放所有元素并调用析构函数。
4.3 删除后的指针处理
删除内存后,指针本身并不会被自动置空,这会导致危险的野指针问题:
cpp复制int* p = new int(10);
delete p;
// p现在成为野指针
// *p = 20; // 未定义行为!
// 正确做法
p = nullptr;
// 删除nullptr是安全的
delete p; // 无操作
5. 对象生命周期管理
5.1 构造与析构的调用时机
当使用new/delete管理对象时,构造函数和析构函数的调用时机非常关键:
cpp复制class Widget {
public:
Widget() { std::cout << "构造\n"; }
~Widget() { std::cout << "析构\n"; }
};
void objectLifecycle() {
Widget* w = new Widget; // 调用构造函数
delete w; // 调用析构函数
w = nullptr;
}
5.2 对象数组的特殊处理
对象数组的构造和析构需要特别注意,每个元素都会独立调用构造函数和析构函数:
cpp复制class Item {
public:
Item() { std::cout << "Item构造\n"; }
~Item() { std::cout << "Item析构\n"; }
};
void objectArrayDemo() {
Item* items = new Item[3]; // 调用3次构造函数
delete[] items; // 调用3次析构函数
items = nullptr;
}
5.3 继承体系中的内存管理
在继承体系中,基类指针指向派生类对象时,必须确保基类有虚析构函数:
cpp复制class Base {
public:
virtual ~Base() {} // 必须为虚析构函数
};
class Derived : public Base {
public:
~Derived() override {}
};
void inheritanceDemo() {
Base* b = new Derived;
delete b; // 正确调用Derived的析构函数
b = nullptr;
}
6. 高级话题与性能优化
6.1 自定义内存分配器
对于性能敏感的应用,可以实现自定义的内存分配策略:
cpp复制class CustomAllocator {
public:
static void* allocate(size_t size) {
std::cout << "分配 " << size << " 字节\n";
return ::operator new(size);
}
static void deallocate(void* p) {
std::cout << "释放内存\n";
::operator delete(p);
}
};
// 使用自定义分配器
void* mem = CustomAllocator::allocate(100);
CustomAllocator::deallocate(mem);
6.2 内存池技术
内存池可以显著减少频繁分配释放小块内存的开销:
cpp复制class MemoryPool {
struct Block {
Block* next;
};
Block* freeList = nullptr;
public:
void* allocate(size_t size) {
if (freeList == nullptr) {
return ::operator new(size);
}
Block* block = freeList;
freeList = freeList->next;
return block;
}
void deallocate(void* p) {
Block* block = static_cast<Block*>(p);
block->next = freeList;
freeList = block;
}
};
6.3 替代方案:智能指针
现代C++推荐使用智能指针来自动管理内存:
cpp复制#include <memory>
void smartPointerDemo() {
// 独占所有权
std::unique_ptr<int> up(new int(10));
// 共享所有权
std::shared_ptr<int> sp1 = std::make_shared<int>(20);
std::shared_ptr<int> sp2 = sp1;
// 弱引用
std::weak_ptr<int> wp = sp1;
}
7. 实战中的陷阱与解决方案
7.1 内存泄漏检测技巧
检测内存泄漏的几种实用方法:
- 重载new/delete运算符记录分配释放
- 使用工具如Valgrind、Dr. Memory
- Windows下的CRT调试功能
- 自定义内存跟踪器
cpp复制// 简单内存跟踪示例
class MemoryTracker {
static std::map<void*, size_t> allocations;
public:
static void* trackAlloc(size_t size) {
void* p = malloc(size);
allocations[p] = size;
return p;
}
static void trackFree(void* p) {
allocations.erase(p);
free(p);
}
static void reportLeaks() {
for (const auto& [addr, size] : allocations) {
std::cerr << "泄漏 " << size << " 字节 @ " << addr << "\n";
}
}
};
// 重载全局operator new
void* operator new(size_t size) {
return MemoryTracker::trackAlloc(size);
}
7.2 多线程环境下的安全
多线程中使用new/delete需要特别注意:
cpp复制#include <mutex>
std::mutex allocMutex;
void* threadSafeAlloc(size_t size) {
std::lock_guard<std::mutex> lock(allocMutex);
return ::operator new(size);
}
void threadSafeFree(void* p) {
std::lock_guard<std::mutex> lock(allocMutex);
::operator delete(p);
}
7.3 异常安全保证
确保在异常发生时不会泄漏内存:
cpp复制class Resource {
int* data;
public:
Resource() : data(new int[100]) {}
~Resource() { delete[] data; }
// 禁用拷贝
Resource(const Resource&) = delete;
Resource& operator=(const Resource&) = delete;
// 启用移动
Resource(Resource&& other) noexcept : data(other.data) {
other.data = nullptr;
}
};
8. 现代C++的最佳实践
8.1 优先使用RAII
资源获取即初始化(RAII)是C++的核心范式:
cpp复制class FileHandle {
FILE* file;
public:
explicit FileHandle(const char* name) : file(fopen(name, "r")) {
if (!file) throw std::runtime_error("打开文件失败");
}
~FileHandle() {
if (file) fclose(file);
}
// 其他方法...
};
void raiiDemo() {
FileHandle f("test.txt"); // 资源自动管理
// 使用文件...
} // 自动关闭
8.2 避免裸new/delete
现代C++代码中应该尽量减少显式的new/delete:
cpp复制// 不好的做法
void oldStyle() {
int* arr = new int[100];
// 使用数组...
delete[] arr;
}
// 好的做法
void modernStyle() {
std::vector<int> arr(100);
// 使用vector...
} // 自动释放
8.3 使用标准库容器
标准库容器已经封装了内存管理:
cpp复制#include <vector>
#include <string>
#include <unordered_map>
void containerDemo() {
std::vector<int> vec; // 动态数组
std::string str; // 动态字符串
std::unordered_map<int, std::string> map; // 哈希表
vec.push_back(10);
str = "Hello";
map[1] = "One";
} // 所有资源自动释放
9. 性能调优实战
9.1 内存分配性能对比
不同分配方式的性能差异:
cpp复制#include <chrono>
#include <iostream>
void performanceTest() {
const int count = 100000;
// 栈分配测试
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < count; ++i) {
int arr[100]; // 栈分配
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "栈分配耗时: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()
<< "μs\n";
// 堆分配测试
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < count; ++i) {
int* arr = new int[100]; // 堆分配
delete[] arr;
}
end = std::chrono::high_resolution_clock::now();
std::cout << "堆分配耗时: "
<< std::chrono::duration_cast<std::chrono::microseconds>(end-start).count()
<< "μs\n";
}
9.2 内存碎片问题
频繁分配释放不同大小内存会导致碎片:
cpp复制void fragmentationDemo() {
// 分配大量不同大小的内存块
void* blocks[1000];
for (int i = 0; i < 1000; ++i) {
blocks[i] = ::operator new((i % 50 + 1) * 16);
}
// 随机释放部分块
for (int i = 0; i < 500; ++i) {
::operator delete(blocks[i*2]);
blocks[i*2] = nullptr;
}
// 尝试分配大块内存可能失败
void* largeBlock = ::operator new(100000);
if (!largeBlock) {
std::cerr << "分配大块内存失败 - 内存碎片化\n";
}
::operator delete(largeBlock);
// 清理
for (int i = 0; i < 1000; ++i) {
if (blocks[i]) ::operator delete(blocks[i]);
}
}
9.3 缓存友好的内存布局
优化内存访问模式提升性能:
cpp复制struct BadLayout {
int id;
bool active;
double values[100];
char name[50];
}; // 可能有填充和缓存不友好
struct GoodLayout {
double values[100]; // 大块数据连续
int id;
bool active;
char name[50];
}; // 更好的缓存局部性
void cacheDemo() {
const int count = 10000;
GoodLayout* good = new GoodLayout[count];
BadLayout* bad = new BadLayout[count];
// 测试访问性能...
delete[] good;
delete[] bad;
}
10. 跨平台注意事项
10.1 内存对齐差异
不同平台可能有不同的对齐要求:
cpp复制void alignmentDemo() {
// 获取类型的自然对齐要求
std::cout << "int对齐: " << alignof(int) << "\n";
std::cout << "double对齐: " << alignof(double) << "\n";
// 跨平台对齐分配
alignas(16) int* alignedArr = new int[10];
// 使用对齐内存...
delete[] alignedArr;
}
10.2 内存模型差异
不同平台的内存模型可能影响多线程程序:
cpp复制#include <atomic>
void memoryModelDemo() {
std::atomic<int> counter{0};
// 原子操作保证跨平台一致性
counter.fetch_add(1, std::memory_order_relaxed);
}
10.3 调试工具链
各平台的调试工具不同:
- Linux: Valgrind, AddressSanitizer
- Windows: CRT调试堆, Dr. Memory
- macOS: Instruments, AddressSanitizer
cpp复制// 使用AddressSanitizer检测内存错误
void asanDemo() {
int* arr = new int[10];
arr[10] = 0; // 越界写入
delete[] arr;
}
// 编译时添加-fsanitize=address选项
11. 从底层理解new/delete
11.1 operator new的三种形式
C++实际上提供了三种全局operator new:
cpp复制// 1. 普通的operator new
void* p1 = ::operator new(100);
// 2. nothrow版本
void* p2 = ::operator new(100, std::nothrow);
// 3. placement new(不分配内存,只在指定位置构造对象)
char buffer[sizeof(int)];
int* p3 = new(buffer) int(42);
// 对应的释放方式
::operator delete(p1);
::operator delete(p2);
p3->~int(); // placement new需要显式调用析构函数
11.2 new表达式的实现机制
一个简单的new表��式如new T实际上会执行以下步骤:
- 调用operator new分配内存
- 在分配的内存上构造对象
- 返回指向对象的指针
类似地,delete表达式会:
- 调用对象的析构函数
- 调用operator delete释放内存
11.3 重载operator new/delete
类可以重载自己的operator new/delete:
cpp复制class CustomAlloc {
public:
static void* operator new(size_t size) {
std::cout << "自定义new, 大小: " << size << "\n";
return ::operator new(size);
}
static void operator delete(void* p) {
std::cout << "自定义delete\n";
::operator delete(p);
}
};
void customAllocDemo() {
CustomAlloc* p = new CustomAlloc;
delete p;
}
12. 替代内存管理方案
12.1 内存池实现
一个简单的内存池实现示例:
cpp复制class SimpleMemoryPool {
struct Block {
Block* next;
};
Block* freeList = nullptr;
size_t blockSize;
public:
explicit SimpleMemoryPool(size_t size) : blockSize(size) {}
void* allocate() {
if (freeList == nullptr) {
return ::operator new(blockSize);
}
Block* block = freeList;
freeList = freeList->next;
return block;
}
void deallocate(void* p) {
Block* block = static_cast<Block*>(p);
block->next = freeList;
freeList = block;
}
~SimpleMemoryPool() {
while (freeList) {
Block* next = freeList->next;
::operator delete(freeList);
freeList = next;
}
}
};
12.2 区域分配器
区域分配器(Region Allocator)一次性分配大块内存:
cpp复制class RegionAllocator {
struct Region {
Region* next;
size_t used;
size_t size;
char data[];
};
Region* current = nullptr;
size_t defaultSize;
Region* newRegion(size_t minSize) {
size_t size = std::max(defaultSize, minSize);
Region* region = static_cast<Region*>(::operator new(sizeof(Region) + size));
region->next = nullptr;
region->used = 0;
region->size = size;
return region;
}
public:
explicit RegionAllocator(size_t size = 4096) : defaultSize(size) {
current = newRegion(defaultSize);
}
void* allocate(size_t size) {
if (current->used + size > current->size) {
Region* newRegion = newRegion(size);
newRegion->next = current;
current = newRegion;
}
void* ptr = current->data + current->used;
current->used += size;
return ptr;
}
void reset() {
while (current->next) {
Region* next = current->next;
::operator delete(current);
current = next;
}
current->used = 0;
}
~RegionAllocator() {
while (current) {
Region* next = current->next;
::operator delete(current);
current = next;
}
}
};
12.3 对象池模板
通用对象池实现:
cpp复制template <typename T>
class ObjectPool {
union PoolItem {
T object;
PoolItem* next;
};
PoolItem* freeList = nullptr;
std::vector<PoolItem*> blocks;
public:
~ObjectPool() {
for (auto block : blocks) {
::operator delete(block);
}
}
template <typename... Args>
T* construct(Args&&... args) {
if (freeList == nullptr) {
allocateBlock();
}
PoolItem* item = freeList;
freeList = freeList->next;
new (&item->object) T(std::forward<Args>(args)...);
return &item->object;
}
void destroy(T* obj) {
obj->~T();
PoolItem* item = reinterpret_cast<PoolItem*>(obj);
item->next = freeList;
freeList = item;
}
private:
static constexpr size_t BLOCK_SIZE = 64;
void allocateBlock() {
PoolItem* block = static_cast<PoolItem*>(::operator new(BLOCK_SIZE * sizeof(PoolItem)));
blocks.push_back(block);
for (size_t i = 0; i < BLOCK_SIZE - 1; ++i) {
block[i].next = &block[i+1];
}
block[BLOCK_SIZE-1].next = nullptr;
freeList = block;
}
};
13. 实战经验分享
13.1 内存泄漏排查技巧
在实际项目中排查内存泄漏的经验:
- 重载全局operator new/delete记录分配点
- 使用工具如Valgrind或AddressSanitizer
- 在关键代码段前后检查内存使用量
- 使用RAII包装资源
- 实现引用计数或智能指针
cpp复制// 简单内存跟踪器实现
class MemoryTracker {
struct AllocInfo {
size_t size;
const char* file;
int line;
};
static std::map<void*, AllocInfo> allocations;
public:
static void* trackAlloc(size_t size, const char* file, int line) {
void* p = ::operator new(size);
allocations[p] = {size, file, line};
return p;
}
static void trackFree(void* p) {
allocations.erase(p);
::operator delete(p);
}
static void reportLeaks() {
for (const auto& [addr, info] : allocations) {
std::cerr << "泄漏 " << info.size << " 字节, 分配于 "
<< info.file << ":" << info.line << "\n";
}
}
};
// 重载全局operator new
void* operator new(size_t size, const char* file, int line) {
return MemoryTracker::trackAlloc(size, file, line);
}
void operator delete(void* p) noexcept {
MemoryTracker::trackFree(p);
}
#define new new(__FILE__, __LINE__)
void leakDemo() {
int* p = new int;
// 忘记delete...
}
int main() {
leakDemo();
MemoryTracker::reportLeaks();
return 0;
}
13.2 性能优化案例
一个真实项目中的内存性能优化案例:
cpp复制// 优化前:频繁分配小对象
void processItems(const std::vector<Item>& items) {
for (const auto& item : items) {
Processor* p = new Processor(item); // 每次循环都分配
p->process();
delete p; // 每次循环都释放
}
}
// 优化后:使用对象池
void processItemsOptimized(const std::vector<Item>& items) {
ObjectPool<Processor> pool;
for (const auto& item : items) {
Processor* p = pool.construct(item); // 从池中获取
p->process();
pool.destroy(p); // 归还到池中
}
}
13.3 多线程内存管理
多线程环境下安全的内存管理策略:
cpp复制class ThreadSafeAllocator {
std::mutex mtx;
public:
void* allocate(size_t size) {
std::lock_guard<std::mutex> lock(mtx);
return ::operator new(size);
}
void deallocate(void* p) {
std::lock_guard<std::mutex> lock(mtx);
::operator delete(p);
}
};
// 线程局部存储(TLS)优化
thread_local ThreadSafeAllocator tlsAllocator;
void threadFunc() {
void* p = tlsAllocator.allocate(100);
// 使用内存...
tlsAllocator.deallocate(p);
}
14. C++17/20新特性
14.1 内存资源API
C++17引入了内存资源(std::pmr)命名空间:
cpp复制#include <memory_resource>
#include <vector>
void pmrDemo() {
// 创建内存池资源
std::pmr::unsynchronized_pool_resource pool;
// 使用内存池的vector
std::pmr::vector<int> vec(&pool);
vec.reserve(100);
// 分配时会使用内存池
for (int i = 0; i < 100; ++i) {
vec.push_back(i);
}
} // 自动释放所有内存
14.2 对齐内存分配改进
C++17简化了对齐内存分配:
cpp复制#include <new>
void alignedAllocDemo() {
// 分配对齐到64字节边界的内存
alignas(64) int* p = new int;
// C++17起可��直接检查对齐
static_assert(alignof(*p) >= 64);
delete p;
}
14.3 硬件干涉大小
C++20引入了硬件干涉大小:
cpp复制#include <new>
void hardwareInterferenceDemo() {
// 获取硬件干涉大小(缓存行大小)
constexpr size_t hw_ifs = std::hardware_destructive_interference_size;
// 分配避免伪共享的结构
struct alignas(hw_ifs) CacheLineAligned {
int data;
char padding[hw_ifs - sizeof(int)];
};
CacheLineAligned* arr = new CacheLineAligned[100];
// 使用...
delete[] arr;
}
15. 工具链支持
15.1 调试工具集成
主流IDE对内存调试的支持:
-
Visual Studio:
- 内存诊断工具
- CRT调试堆
- 泄漏检测
-
CLion:
- Valgrind集成
- AddressSanitizer支持
- 内存视图
-
Xcode:
- Instruments工具
- AddressSanitizer
- Memory Graph Debugger
15.2 静态分析工具
检测内存问题的静态分析工具:
-
Clang-Tidy:
- 检测潜在内存泄漏
- 检查new/delete配对
- 发现未初始化的内存
-
Cppcheck:
- 内存泄漏检测
- 无效的释放操作
- 越界访问检查
-
PVS-Studio:
- 专业级静态分析
- 内存管理错误检测
- 跨平台支持
15.3 动态分析工具
运行时内存分析工具:
-
Valgrind:
- Memcheck工具
- 内存泄漏检测
- 非法内存访问
-
AddressSanitizer:
- 快速内存错误检测器
- 检测use-after-free
- 堆栈缓冲区溢出
-
Dr. Memory:
- Windows平台内存调试
- 未初始化内存读取
- 内存泄漏检测
16. 设计模式与内存管理
16.1 工厂模式中的内存管理
使用工厂方法安全创建对象:
cpp复制class Product {
public:
virtual ~Product() = default;
virtual void operation() = 0;
};
class Factory {
public:
virtual ~Factory() = default;
virtual std::unique_ptr<Product> create() = 0;
};
class ConcreteProduct : public Product {
public:
void operation() override {
std::cout << "具体产品操作\n";
}
};
class ConcreteFactory : public Factory {
public:
std::unique_ptr<Product> create() override {
return std::make_unique<ConcreteProduct>();
}
};
void factoryDemo() {
std::unique_ptr<Factory> factory = std::make_unique<ConcreteFactory>();
std::unique_ptr<Product> product = factory->create();
product->operation();
}
16.2 享元模式的内存优化
使用享元模式减少内存使用:
cpp复制class Flyweight {
std::string sharedState;
public:
explicit Flyweight(const std::string& state) : sharedState(state) {}
void operation(const std::string& uniqueState) {
std::cout << "共享状态: " << sharedState
<< " 唯一状态: " << uniqueState << "\n";
}
};
class FlyweightFactory {
std::map<std::string, std::shared_ptr<Flyweight>> flyweights;
public:
std::shared_ptr<Flyweight> getFlyweight(const std::string& key) {
if (flyweights.find(key) == flyweights.end()) {
flyweights[key] = std::make_shared<Flyweight>(key);
}
return flyweights[key];
}
};
void flyweightDemo() {
FlyweightFactory factory;
auto fw1 = factory.getFlyweight("状态A");
fw1->operation("唯一1");
auto fw2 = factory.getFlyweight("状态A"); // 重用
fw2->operation("唯一2");
}
16.3 观察者模式中的资源管理
安全实现观察者模式:
cpp复制class Observer {
public:
virtual ~Observer() = default;
virtual void update() = 0;
};
class Subject {
std::vector<std::weak_ptr<Observer>> observers;
std::mutex mtx;
public:
void attach(const std::shared_ptr<Observer>& obs) {
std::lock_guard<std::mutex> lock(mtx);
observers.emplace_back(obs);
}
void notify() {
std::lock_guard<std::mutex> lock(mtx);
for (auto it = observers.begin(); it != observers.end(); ) {
if (auto obs = it->lock()) {
obs->update();
++it;
} else {
it = observers.erase(it);
}
}
}
};
class ConcreteObserver : public Observer,
public std::enable_shared_from_this<ConcreteObserver> {
public:
void update() override {
std::cout << "观察者收到通知\n";
}
};
void observerDemo() {
auto subject = std::make_shared<Subject>();
auto observer = std::make_shared<ConcreteObserver>();
subject->attach(observer->shared_from_this());
subject->notify();
}
17. 并发环境下的内存管理
17.1 原子操作与内存顺序
理解内存顺序对并发程序的影响:
cpp复制#include <atomic>
#include <thread>
std::atomic<int> data;
std::atomic<bool> ready{false};
void producer() {
data.store(42, std::memory_order_relaxed);
ready.store(true, std::memory_order_release);
}
void consumer() {
while (!ready.load(std::memory_order_acquire)) {
std::this_thread::yield();
}
std::cout << data.load(std::memory_order_relaxed) << "\n";
}
void memoryOrderDemo() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
}
17.2 无锁数据结构
实现简单的无锁栈:
cpp复制template <typename T>
class LockFreeStack {
struct Node {
std::shared_ptr<T> data;
Node* next;
Node(const T& d) : data(std::make_shared<T>(d)) {}
};
std::atomic<Node*> head;
public:
void push(const T& value) {
Node* new_node = new Node(value);
new_node->next = head.load();
while (!head.compare_exchange_weak(new_node->next, new_node)) {
// CAS失败,重试
}
}
std::shared_ptr<T> pop() {
Node* old_head = head.load();
while (old_head &&
!head.compare_exchange_weak(old_head, old_head->next)) {
// CAS失败,重试
}
if (!old_head) return nullptr;
std::shared_ptr<T> res = old_head->data;
delete old_head;
return res;
}
~LockFreeStack() {
while (pop());
}
};
17.3 线程局部存储
使用thread_local优化多线程内存访问:
cpp复制class ThreadCache {
static thread_local std::vector<int> cache;
public:
void addToCache(int value) {
cache.push_back(value);
}
void processCache() {
for (int value : cache) {
std::cout << value << " ";
}
std::cout << "\n";
cache.clear();
}
};
thread_local std::vector<int> ThreadCache::cache;
void threadCacheDemo() {
ThreadCache shared;
auto worker = [&shared]() {
for (int i = 0; i < 5; ++i) {
shared.addToCache(i);
}
shared.processCache();
};
std::thread t1(worker);
std::thread t2(worker);
t1.join();
t2.join();
}
