std::
shared_ptr
属于STL库,它的作用和auto_ptr相似,也是管理一个对象指针,提供一个释放内存的封装操作,但是它可以和其他shared_ptr对象共享对指针的管理。
它的实现原理是再内部维护一个引用计数,当引用计数为0的时候进行释放销毁操作。
std::shared_ptr的构造函数有多重,我们常用的是默认构造函数和带有指针参数的构造函数:
default (1) | constexpr shared_ptr() noexcept; |
---|
from pointer (3) | template <class U> explicit shared_ptr (U* p); |
---|
copy (6) | shared_ptr (const shared_ptr& x) noexcept; template <class U> shared_ptr (const shared_ptr<U>& x) noexcept; |
---|
std::shared_ptr<int> pTest; //默认构造.
std::shared_ptr<int> pTest2(nullptr); //默认构造
std::shared_ptr<int> pTest3(pTest); //默认构造
std::shared_ptr<int> pTest4(new int(10)); //引用为1
std::shared_ptr<int> pTest5(pTest4); //引用为2
std::shared_ptr在操作符=的实现中同样会对引用计数进行操作,原理和构造函数差不多。
到这里我们已经可以清楚地知道std::shared_ptr可以共享所有权,并且用引用计数来进行管理,当引用计数为0的时候才会进行释放操作,那么就可能造成一种引用环路问题,我们用一个网上的例子(
http://blog.csdn.net/shanno/article/details/7363480
):
#include <iostream>
#include <memory>
class Woman;
class Man
{
private:
std::weak_ptr<Woman> _wife;
//std::shared_ptr<Woman> _wife;
public:
void setWife(std::shared_ptr<Woman> woman)
{
_wife = woman;
}
void doSomthing()
{
if (_wife.lock())
{
}
}
~Man()
{
std::cout << "kill man\n";
}
};
class Woman
{
private:
//std::weak_ptr<Man> _husband;
std::shared_ptr<Man> _husband;
public:
void setHusband(std::shared_ptr<Man> man)
{
_husband = man;
}
~Woman() {
std::cout << "kill woman\n";
}
};
int main(int argc, char** argv)
{
std::shared_ptr<Man> m(new Man());
std::shared_ptr<Woman> w(new Woman());
if (m && w)
{
m->setWife(w);
w->setHusband(m);
}
return 0;
}
在Man类内部会引用一个Woman,Woman类内部也引用一个Man。当一个man和一个woman是夫妻的时候,他们直接就存在了相互引用问题。man内部有个用于管理wife生命期的shared_ptr变量,也就是说wife必定是在husband去世之后才能去世。同样的,woman内部也有一个管理husband生命期的shared_ptr变量,也就是说husband必须在wife去世之后才能去世。这就是循环引用存在的问题:husband的生命期由wife的生命期决定,wife的生命期由husband的生命期决定,最后两人都死不掉,违反了自然规律,导致了内存泄漏。
要解决这个问题我们就需要认识std::weak_ptr。std::weak_ptr同样是一种智能指针,但是是为了配合shared_ptr工作而存在的。它可以从一个shared_ptr或者weak_ptr中来构造,但是并不会改变引用计数
default (1) | constexpr weak_ptr() noexcept; |
---|---|
copy (2) | weak_ptr (const weak_ptr& x) noexcept; template <class U> weak_ptr (const weak_ptr<U>& x) noexcept; |
from shared_ptr (3) | template <class U> weak_ptr (const shared_ptr<U>& x) noexcept; |
我们如果用weak_ptr来解决上述问题就比较简单了,在环路引用中改变一个引用为weak_ptr,不增加引用计数,在使用的时候仅仅临时产生一个shared_ptr对象来进行操作,当然如果在这个临时对象的声明周期没有销毁前原来的shared_ptr销毁了,那么管理的指针会延迟到这个临时shared_ptr销毁的时候销毁。
和std::shared_ptr相关的比较常用的方法有:
std::shared_ptr<int> pTest6 = std::make_shared<int>(new int());
std::shared_ptr<A> pFather = std::dynamic_pointer_cast<B>(pChild);//static_pointer_cast const_point_cast形式差不多。
std::shared_ptr更方便地管理内存,我们不需要再考虑析构时的顺序问题了,完全交给智能指针来管理,大大减少了程序猿的负担,减少bug的产生。