C++11 引入了 3 个智能指针类型:
std::unique_ptr<T>
:独占资源所有权的指针;std::shared_ptr<T>
:共享资源所有权的指针,它的原理是使用引用计数实现对同一块内存的多个引用,在最后一个引用被释放时,指向的内存才释放;std::weak_ptr<T>
:共享资源的观察者,需要和std::shared_ptr
一起使用,不影响资源的生命周期。
1. std::unique_ptr
{
std::unique_ptr<int> uptr = std::make_unique<int>(200);
std::unique_ptr<int> uptr1 = uptr; // 编译错误,std::unique_ptr<T> 是 move-only 的
std::unique_ptr<int> uptr2 = std::move(uptr); // 转为右值后
assert(uptr == nullptr);
}
2. std::shared_ptr
{
std::shared_ptr<int> sptr = std::make_shared<int>(200);
assert(sptr.use_count() == 1); // 此时引用计数为 1
{
std::shared_ptr<int> sptr1 = sptr;
assert(sptr.get() == sptr1.get());
assert(sptr.use_count() == 2); // sptr 和 sptr1 共享资源,引用计数为 2
}
assert(sptr.use_count() == 1); // sptr1 已经释放
}
// use_count 为 0 时自动释放内存
错误用法1:退出{ }
p1、p2都会对p0析构,导致p0被删除两次,报错;
{
int *p0 = new int(1);
shared_ptr<int> p1(p0);
shared_ptr<int> p2(p0);
}
错误用法2:循环引用;
struct Father{
shared_ptr<Son> son_;
};
struct Son{
shared_ptr<Father> father_;
};
int main(){
auto father = make_shared<Father>();
auto son = make_shared<Son>();
father->son_ = son;
son->father_ = father;
return 0;
}
main
函数退出之前,Father_obj
、Son_obj
的引用计数都是 2
,退出main
后,引用计数为1
,导致对象没法被销毁,从而引起内存泄漏。
3. std::weak_ptr
std::weak_ptr
为弱引用指针(std::shared_ptr
是强引用指针),弱引用指针不会增加引用计数。
使用std::weak_ptr
修正错误用法2:
struct Father{
shared_ptr<Son> son_;
};
struct Son{
weak_ptr<Father> father_;
};
int main(){
auto father = make_shared<Father>(); // father ptr points to a Father obj
auto son = make_shared<Son>();
father->son_ = son;
son->father_ = father;
return 0;
}
- 退出
main
之前,Son_obj
引用数为2
,Father_obj
引用为1
; - 退出
main
后,son 指针被销毁 ⇒Son_obj
引用数为1;
father 指针被销毁 ⇒father_obj
引用数为0
; father_obj
引用数为0
⇒Father_obj
被析构 ⇒Father_obj.son
被销毁⇒Son_obj
引用数为0
⇒Son_obj
被析构;- 最终
father_obj
与Son_obj
被正常析构。