1.shared_ptr 互相引用会出现资源不释放的问题,造成内存泄漏。
比如这段代码:
class C2;
class C1 {
private:
std::shared_ptr<C2> _c2;
public:
void setC2(std::shared_ptr<C2> c2) {
_c2 = c2;
}
~C1() {
std::cout << "kill c1\n";
}
};
class C2 {
private:
std::shared_ptr<C1> _c1;
public:
void setC1(std::shared_ptr<C1> c1) {
_c1 = c1;
}
~C2() {
std::cout << "kill c2\n";
}
};
void testptr()
{
std::shared_ptr<C1> c1(new C1());
std::shared_ptr<C2> c2(new C2());
if (c1 && c2)
{
c1->setC2(c2);
c2->setC1(c1);
}
这个问题并不直观。
换一种思考方式,假设在某一个时刻,智能指针中的C1对象被销毁,那么c1._c2才会销毁,这个时候引用了C2类型的shared_ptr引用计数才会有可能变成0(按照我们的例子,就是在这个时刻引用计数变成0),智能指针中的C2对象才会被销毁,而也只有在智能指针中的C2对象被销毁时,引用了C1类型的shared_ptr引用计数才会变成0,这个时候才会去析构智能指针中的C1对象,所以分析到这里,我们会发想刚才的假设不会发生。这样就形成了一个互相牵制的死结。
两种解决方案:
1.如果确实需要按照testptr函数中的方式调用,则根据业务理清两个类的关系,哪个类生命周期依赖另一个,将命长的那个类作为shared_ptr作为命短的类的成员,将命短的那个类作为wead_ptr作为命长的类的成员,并在使用的时候,通过wead_ptr::lock()得到shared_ptr,保证使用范围内,命短的成员不会挂掉。
2.避免testptr函数中的调用方式,如果业务允许,可以考虑:
c1->setC2(c2);
c2->setC1(c1_1);//std::shared_ptr<C1> c1_1(new C1());
weak_ptr并不像shared_ptr一样维护引用计数,所以不会存在像shared_ptr的问题,但是引入的新问题是,如果使用weak_ptr,即便是你定义了一个变量,这个变量的存活期也并不能由你来掌控,所以是一个弱控制的指针,只有在调用lock拿到一个shared_ptr的这段时间里,是你可以确定你的变量是真实存活着的。并且weak_ptr并不能单独使用,只能配合shared_ptr或者其他weak_ptr一同使用,来解决shared_ptr互相引用的问题。
unique_ptr旨在替代auto_ptr,因为auto_ptr的copy导致的被copy对象变成null的问题,整体用法就是提供对内存对象的独占访问,并且使用release显式移交所有权。