智能指针shared_ptr
在使用原始指针的时候,经常出现如下情形,A指针和B指针同时指向堆上的一个对象object,在运行的过程中,同过A指针使用delete把这个对象object释放了,而且也把A指向了null,但是这个时候B指针就成了空悬指针,如果对B进行操作,程序就会崩溃。
针对这种情况的一个解决办法就是,在A、B指针和object对象之间再加一层代理指针proxy,A和B都指向proxy,proxy代理指针指向实际的对象object。那么当对A进行delete操作的时候,通过proxy指针,把object对象释放,然后proxy指向0。这样,再使用B去通过proxy访问对象的时候,发现代理指针指向空,也就知道对象已经被释放了。但是这里还是有一个问题,什么时候释放代理指针?
对于这个问题的解决办法就是,再代理指针数据里面再加一个计数,每增加一个指向object的指针时,计数+1,没一个指针delete的时候,计数-1.当计数=0的时候,释放代理指针。这也就是智能指针的思路,使用引用计数来决定是否对指针指向的内容进行销毁操作。
循环引用
智能指针虽然好用,但是它也存在一个问题就是,循环引用,可以看如下代码段:
#include <iostream>
#include <memory>
#include <iostream>
class B;
class A{
public:
A(){}
~A(){
std::cout<<"A destruct"<<std::endl;
}
void setPtr(std::shared_ptr<B> _ptr){
ptr = _ptr;
}
private:
std::shared_ptr<B> ptr;
};
class B{
public:
B(){}
~B(){
std::cout<<"B destruct"<<std::endl;
}
void setPtr(std::shared_ptr<A> _ptr){
ptr = _ptr;
}
private:
std::shared_ptr<A> ptr;
};
int main()
{
if(1)
{
std::shared_ptr<A> p1(new A());
std::shared_ptr<B> p2(new B());
p1->setPtr(p2);
p2->setPtr(p1);
}
return 0;
}
在运行的时候发现并不会执行到A和B的析构函数中去,这是因为出现循环引用了。在p1的指针指向了p2,所以p2要释放必须p1中的指针先释放掉,同理,p2中的指针指向了p1,p1需要释放的话,必须p2中的指针先释放,这就导致两个指针指向的对象始终得不到释放。
weak_ptr
弱指针与shared_ptr的区别在于weak_ptr对象引用资源时不会增加引用计数,需要对weak_ptr指向的对象进行访问时,只需要使用lock()返回一个shared_ptr,这个shared_ptr可以访问到指向的对象,并且在这个shared_ptr作用域内,对象不会被释放掉。
把上述例子中的A类中的shared_ptr<B>换成weak_ptr<B>就不会出现循环引用。