1 简介
C++ 原始指针依赖编程人员手动管理内存,其中new一个指针简单,但是准确且合理地在什么时候delete一个指针则比较考验编程人员的能力,加大了编程的工作量且容易犯错。
C++11标准之后引入了智能指针的概念,与原始指针最大不同以及提升点是指向对象的内存资源由智能指针管理。简单来说,不用编程人员费劲去想指针什么时候释放,会不会存在指针资源泄露之类的问题。智能指针这一特性对编程人员非常友好,也是自推出之后很受欢迎的原因。
目前引入的智能指针有如下:
unique_ptrshared_ptrweek_ptr
详细的使用方法以及api接口前人已经总结了很多,文章末尾会给出阅读资料参考。本文接下来主要论述本人在使用中的遇到的注意事项。
2 unique_ptr
unique_ptr是一种独占的智能指针,所谓独占是指一个对象同时只能被一个unique_ptr占有,不允许与其他智能指针共享对象(如多个shared_ptr可以指向同一个对象)。
注意:阅读书籍和博客时常常会看到这样一句话:
C++未提供make_unique函数,需要自己实现。
这句话是有背景的,其实在C++ 14标准中已经给出了make_unique的函数,C++ 11标准时确实没有,而大量博客以及书籍(比如《C++ Primer》第5版)是基于C++ 11标准的。
3 shared_ptr
与unique_ptr不同的是,shared_ptr允许多个智能指针指向同一个对象,且对象的内存管理与unique_ptr也不同。unique_ptr由于独占,当其生存期结束后,则会自动释放所指向的对象。而shared_ptr是根据引用计数来决定所指向对象的内存管理。所谓引用计数,简单来说就是目前程序中还有多少shared_ptr指向该对象的个数,当引用计数为0时,则会释放掉该对象。
4 weak_ptr
weak_ptr与上文提到的两种智能指针不太一样,他不控制所指向对象的生存期,weak_ptr用来指向shared_ptr管理的对象,但是不改变该对象的引用计数。换句话说,就算仍然有weak_ptr指向该对象,该对象仍然该释放释放。如其名,weak。
那么问题来了,weak_ptr有什么作用?在起初学习智能指针的时候,只是了解有这么一个智能指针,后面编程实践时常用的也都是unique_ptr和shared_ptr,直到后面遇到一个场景,发现weak_ptr可以用来解决循环引用的问题。
是这样一个场景:
class A
{
public:
A();
// Class A拥有一个指针指向一个Class B object
std::shared_ptr<B> b_;
};
class B
{
public:
B();
// Class B拥有一个指针指向Class A object
// std::shared_ptr<A> a_; error!!! 发生循环引用,shared_ptr的引用计数始终不能为0
std::weak_ptr<A> a_;
}
int main()
{
{
// 记a_ptr的引用计数为cnt1
// 记b_ptr的引用计数为cnt2
std::shared_ptr<A> a_ptr = std::make_shared<A>(); // cnt1 = 1
std::shared_ptr<B> b_ptr = std::make_shared<B>(); // cnt2 = 1;
a_ptr->b_ = b_ptr; // cnt2 = 2
b_ptr->a_ = a_ptr; // if using shared_ptr cnt1 = 2 else cnt1 = 1
}
// 离开作用域
// cnt1-- cnt1 = 0
// cnt2-- cnt2 = 1
// 1. a_ptr先释放
// 2. 随之cnt2-- cnt2 = 0, b_ptr释放
// if using shared_ptr in B class
// 离开作用域
// cnt1-- cnt1 = 1
// cnt2-- cnt2 = 1
// 两者都不会释放,发生了循环引用问题!
return 0;
}
当然,尽可能在上层类设计阶段规避循环引用的问题,这是一种不合理的类的建模。