共计 2160 个字符,预计需要花费 6 分钟才能阅读完成。
在 C ++ 开发中,手动管理预分配的内存块是常见但容易出错的操作。开发者经常需要显式调用 delete 释放内存,稍有不慎就会导致内存泄漏或悬垂指针问题。特别是在处理固定大小内存块(如示例中的 1024 字节)时,重复的分配 / 释放代码会使逻辑变得臃肿,且难以保证异常安全。

unique_ptr 与 shared_ptr 的选择依据
当需要独占所有权时,unique_ptr是轻量级且零开销的最佳选择。其特点包括:
- 禁止拷贝构造,支持移动语义
- 默认调用
delete释放资源 - 编译期检查所有权唯一性
而 shared_ptr 适用于需要共享所有权的场景:
- 引用计数实现自动释放
- 支持拷贝和移动操作
- 自定义删除器不影响类型系统
对于 1024 字节的固定内存块管理,若无特殊共享需求,优先推荐使用unique_ptr。
unique_ptr 管理栈上内存
标准库未提供直接接管已分配内存的 make_unique 重载,需通过自定义删除器实现:
#include <memory>
#include <cstring>
// 自定义删除器:对 malloc 分配的内存执行 free
struct FreeDeleter {void operator()(void* ptr) const {std::free(ptr);
std::cout << "Memory freed by custom deleter\n";
}
};
int main() {
// 分配 1024 字节堆内存
void* raw_mem = std::malloc(1024);
if (!raw_mem) throw std::bad_alloc();
// 用 unique_ptr 接管,指定自定义删除器
std::unique_ptr<void, FreeDeleter> managed_mem(raw_mem);
// 使用示例:写入数据
std::strcpy(static_cast<char*>(managed_mem.get()), "Hello, smart pointer!");
return 0;
} // 作用域结束自动调用 FreeDeleter
关键点说明:
- 删除器类型作为
unique_ptr的模板第二参数 get()方法获取原始指针进行操作- 退出作用域时自动触发资源释放
shared_ptr 结合内存池
当需要在多个组件间共享内存块时,可使用 shared_ptr 配合内存池:
#include <memory>
#include <iostream>
class MemoryPool {
public:
static void* Alloc(size_t size) {void* block = std::malloc(size);
if (!block) throw std::bad_alloc();
std::cout << "Allocated" << size << "bytes from pool\n";
return block;
}
static void Dealloc(void* ptr) {std::free(ptr);
std::cout << "Returned memory to pool\n";
}
};
int main() {
try {
// 创建共享指针并指定内存池删除器
std::shared_ptr<void> shared_mem(MemoryPool::Alloc(1024),
[](void* ptr) {MemoryPool::Dealloc(ptr); }
);
// 拷贝共享指针
auto copy_mem = shared_mem;
std::cout << "Use count:" << copy_mem.use_count() << "\n";} catch (const std::exception& e) {std::cerr << "Error:" << e.what() << "\n";
}
return 0;
}
异常安全要点:
- 分配失败时立即抛出异常
- 删除器通过 lambda 表达式绑定内存池方法
- 引用计数归零时自动回调删除器
避坑指南
循环引用检测
当使用 shared_ptr 时,相互持有的对象会导致内存泄漏:
struct Node {std::shared_ptr<Node> next;};
auto node1 = std::make_shared<Node>();
auto node2 = std::make_shared<Node>();
node1->next = node2;
node2->next = node1; // 循环引用!
解决方案:
- 使用
weak_ptr打破强引用 - 定期运行内存分析工具(如 Valgrind)检测
多线程安全
shared_ptr的引用计数本身是线程安全的- 但指针解引用操作需要额外同步:
std::shared_ptr<int> ptr = std::make_shared<int>(42);
// 线程不安全访问
void unsafe_access() {if (!ptr.expired()) {*ptr += 1; // 竞态条件!}
}
// 安全做法:使用原子操作或 mutex
进阶思考
- 变长内存管理 :如何设计可扩展方案处理动态大小的内存块?考虑结合
std::vector或自定义分配策略 - placement new 应用 :在已分配的内存上构造对象时,如何与智能指针协同工作?研究
std::allocate_shared的用法 - 高性能替代方案 :在实时系统中,引用计数可能成为瓶颈,可探索内存区域(arena) 或对象池模式
正文完
