Effective c++学习笔记(item13的引申:RCSP can't break cycles of references)
RCSP: 是应用计数smart pointer的缩写。引用计数智能指针实际就是std::shared_ptr,在遇到循环引用的时候是会有问题的。书中item13中只是这么一句说明,没有额外查资料估计也就不了了之了。
1 什么是循环引用
这里重点描述下循环引用的场景和解决方法。
- 考虑以下的代码,为什么在智能指针离开作用域后没有自己释放。
class CA;
class CB;
class CA{
public:
~CA(){count<<"release CA"<<endl;}
shared_ptr<CB> AsCB;
};
class CB{
public:
~CB(){count<<"release CB"<<endl;}
shared_ptr<CA> BsCA;
};
void testCircleofReference()
{
shared_ptr<CA> a(new CA);
shared_ptr<CB> b(new CB);
a->AsCB = b;
b->BsCA = a;
}
int main()
{
testCircleofReference();
}
在testCircleofReference函数中,shared_ptr<CA> a(new CA);
语句会让RCSP a的引用计数+1, b->BsCA = a;
这个语句也会让a的引用计数+1(其实此时b->BsCA的引用计数和a的引用计数是共享的都是2。当testCircleofReference函数执行完毕后,因为a,b都离开了作用域,所以引用计数都会-1,但此时还各自有一次的引用计数,所以,智能指针a虽然释放了,但a关联的资源(new CA)不会释放,执行的表现就是没有调用CA的析构函数。
- 考虑下面的代码,为什么可以正确释放资源,下面的代码和上面的代码只差一行,因为这一行的注释,使得循环环断开了。
class CA;
class CB;
class CA{
public:
~CA(){count<<"release CA"<<endl;}
shared_ptr<CB> AsCB;
};
class CB{
public:
~CB(){count<<"release CB"<<endl;}
shared_ptr<CA> BsCA;
};
void testCircleofReference()
{
shared_ptr<CA> a(new CA);
shared_ptr<CB> b(new CB);
a->AsCB = b;
//b->BsCA = a;//这一注释使循环引用的环断开,不再存在循环引用
}
int main()
{
testCircleofReference();
}
上述的代码在testCircleofReference函数执行完毕后,a,b因为离开作用域会释放掉。b的引用计数变为0,会触发资源CB的释放,CB的释放会触发成员对象BsCA的释放,此时a的引用计数会被-1,而变成0,从而又出发a所关联的资源释放。
weak_ptr的作用
weak_ptr就是若引用的意思,如果一个shared_ptr赋值给weak_ptr,引用计数不变化。稍微调整下上面的例子,得到新的例子:
class CA;
class CB;
class CA{
public:
~CA(){count<<"release CA"<<endl;}
weak_ptr<CB> AsCB;//通过weak_ptr打断循环引用
};
class CB{
public:
~CB(){count<<"release CB"<<endl;}
shared_ptr<CA> BsCA;
};
void testCircleofReference()
{
shared_ptr<CA> a(new CA);
shared_ptr<CB> b(new CB);
a->AsCB = b;//因为AsCB是weak_ptr,所以这句话的赋值不会让b的引用计数+1。
b->BsCA = a;
}
int main()
{
testCircleofReference();
}
在testCircleofReference函数运行之后,a,b因为离开作用域,引用计数会-1,此时关联CA的引用计数是1,关联CB的引用计数是0,所以会触发CB的释放,CB的释放导致CA的引用计数-1,变成0,从而又引发CA的资源释放。
尽量避免循环引用的情况
weak_ptr毕竟只是一个规避手段,我想最好的方式还是在设计阶段规避这种资源的循环引用。就像设计上一个不提倡循环依赖一样。
网友评论