C++智能指针是一种用于自动管理动态分配内存的对象, 以避免内存泄漏和悬挂指针(dangling pointer)
std::unique_ptr
定义
独占所有权智能指针, 同时只能有一个std::unique_ptr
指向对象, 无法被复制, 可通过std::move
转移所有权
使用 RAII(Resource Acquisition Is Initialization)原理管理动态内存, 超出作用域时自动调用 delete 释放
操作
创建和初始化
- 使用new操作符
std::unique_ptr<int> up(new int(10));
- 使用std::make_unique 函数(C++14引入)
auto up = std::make_unique<int>(20);
访问
使用解引用操作符 *
获取对象
auto up = std::make_unique<int>(20);
std::cout << *up << std::endl;
转移所有权
std::unique_ptr 不允许复制, 但可以通过 std::move 转移所有权
auto up = std::make_unique<int>(20);
// 转移所有权, up 变为空
auto up2 = std::move(up);
释放所有权
使用 release 方法释放所有权, 并返回原始指针, 需要手动删除对象以避免内存泄漏
auto up = std::make_unique<int>(20);
// 释放所有权, 返回原始指针
int* p = up.release();
// 手动删除对象
delete p;
重置和重新分配
使用 reset 方法重置 std::unique_ptr, 释放当前对象并管理新对象
如果调用 reset 时不传递任何参数, 则会释放当前对象并使 std::unique_ptr 变为空
auto up = std::make_unique<int>(20);
// 重置并管理新的对象
up.reset(new int(30));
// 释放当前对象, up变为空
up.reset();
自定义删除器
std::unique_ptr 允许自定义删除器, 用于在对象销毁时执行特定清理操作
struct CustomDeleter {
void operator()(int* p) const {
std::cout << "deleting int pointer" << std::endl;
delete p;
}
};
// 使用自定义删除器
std::unique_ptr<int, CustomDeleter> up(new int(40), CustomDeleter());
#include <iostream>
#include <memory>
int main() {
// 使用 make_unique 初始化
std::unique_ptr<int> p1 = std::make_unique<int>(10);
std::cout << "value: " << *p1 << std::endl;
// 直接构造
std::unique_ptr<int> p2(new int(20));
// 转移所有权
std::unique_ptr<int> p3 = std::move(p1); // p1 现在为空
if (!p1) {
std::cout << "p1 is null after move." << std::endl;
}
// 自动释放
return 0;
} // p2 和 p3 超出作用域时内存会被释放
std::shared_ptr
定义
共享所有权智能指针, 多个std::shared_ptr
可指向同一个对象
使用引用计数来管理对象生命周期, 当最后一个std::shared_ptr
销毁时引用计数归零, 对象自动销毁释放内存
操作
创建和初始化
使用 std::make_shared 工厂函数来创建和初始化
// 指向一块 10 个 int 类型数据堆内存空间
std::shared_ptr<int> p = std::make_shared<int>(10);
访问
#include <iostream>
#include <memory>
int main() {
int var = 0xFF;
printf("var address: %p, var value: %d\n", &var, var);
std::shared_ptr<int> sp = std::make_shared<int>(var);
printf("sp value: %p\n", *p);
return 0;
}
复制和共享
std::shared_ptr 支持复制操作, 可通过赋值将多个 std::shared_ptr 实例共享同一个对象
此时对象的引用计数会增加
std::shared_ptr<int> sp = std::make_shared<int>(10);
// 复制, 引用计数增加
std::shared_ptr<int> sp2 = sp;
引用计数
使用 use_count 方法获取当前 std::shared_ptr引用计数
#include <iostream>
#include <memory>
int main() {
int value = 0xFF;
std::shared_ptr<int> sp = std::make_shared<int>(value);
printf("sp use count: %d\n", sp.use_count());
std::shared_ptr<int> tp = sp;
printf("sp use count: %d\n", sp.use_count());
return 0;
}
获取原始指针
使用 get 方法获取 std::shared_ptr 所管理对象原始指针
不会增加引用计数, 因此在使用原始指针时需要小心, 避免手动删除对象或导致悬空指针
#include <iostream>
#include <memory>
int main() {
int var = 0xFF;
int *p = &var;
printf("p value: %p, *p = %d\n", p, *p);
std::shared_ptr<int> sp = std::make_shared<int>(0xFF);
printf("*sp value: %d\n", *sp);
int* gp = sp.get();
printf("gp value: %p, *gp = %d\n", gp, *gp);
return 0;
}
std::weak_ptr
弱引用智能指针, 不控制对象生命周期, 用于打破shared_ptr
循环引用, 避免无法释放内存
不能直接访问对象, 需要通过std::shared_ptr
操作
操作
创建
std::weak_ptr必须和 std::shared_ptr 配合使用
可以从一个 std::shared_ptr 或另一个 std::weak_ptr 对象构造
std::shared_ptr<int> sp = std::make_shared<int>(10);
// 通过 std::shared_ptr 初始化
std::weak_ptr<int> wp = sp;
访问状态
expired 方法用于检查所观察std::shared_ptr 是否已经失效(即所管理对象是否已经被销毁)
std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp;
if (!wp.expired()) {
// wp 所观察 std::shared_ptr 仍然有效
}
转换为 std::shared_ptr
lock 方法可尝试转换成std::shared_ptr
如果转换成功(即所观察对象仍然存在), 则返回有效std::shared_ptr
, 否则返回空
std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp;
std::shared_ptr<int> sp2 = wp.lock();
if (sp2) {
// 转换成功, 可以使用 sp2 访问对象
printf("*sp value: %d\n", *sp);
} else {
// 转换失败, 对象已经被销毁
printf("resource has been released");
}
重置
std::weak_ptr
可以通过赋值操作进行重置, 使其不再观察任何 std::shared_ptr
std::weak_ptr<int> wp2;
// wp 现在不再观察任何 std::shared_ptr
wp = wp2;