shared_ptr的原理与应用(三)

2014-11-24 02:08:41 · 作者: · 浏览: 7
1和p2的析构导致,电脑再一次爆炸了。
为了避免这种情况的发生,我们永远不要将new用在shared_ptr构造函数参数列表以外的地方,或者干脆不用new,改用make_shared。
即便我们的程序严格采取上述做法,C++还提供另外一种绕过shared_ptr,直接获取裸指针的方式,那就是this指针。请看下面的事故现场:
class A {
public:
std::shared_ptr getShared() {
return std::shared_ptr(this);
}
};
int main() {
std::shared_ptr pa = std::make_shared();
std::shared_ptr pbad = pa->getShared();
return 0;
}
在此次事故中,pa和pbad拥有各自独立的引用计数器,所以程序将发生相同的"delete野指针"错误。总而言之,管理同一资源的shared_ptr,只能由同一个初始shared_ptr通过一系列赋值或者拷贝构造途径得来。更抽象的说,管理同一资源的shared_ptr的构造顺序,必须是一个无环有向的连通图,无环能够保证没有循环引用,连通性能够保证每个shared_ptr都来自于相同的源。
另外,标准库提供了一种特殊的接口,来解决"生成this指针的shared_ptr"的问题。
enable_shared_from_this
enable_shared_from_this是标准库中提供的接口(一个基类啦):
template
class enable_shared_from_this {
public:
shared_ptr shared_from_this();
}
如果想要一个由shared_ptr管理的类A对象能够在方法内部得到this指针的shared_ptr,且返回的shared_ptr和管理这个类的shared_ptr共享引用计数,只需让这个类派生自enable_shared_from_this即可,之后调用shared_from_this()即可获得正确的shared_ptr。
一般来说,这个接口是通过weak_ptr实现的:enable_shared_from_this中包含一个weak_ptr,在初始化shared_ptr时,构造函数会检测到这个该类派生于enable_shared_from_this(通过模版黑魔法很容易就能实现这个功能啦),于是将这个weak_ptr指向初始化的shared_ptr。调用shared_from_this,本质上就是weak_ptr的一个lock操作:
class A : enable_shared_from_this {
// ......
};
int main() {
std::shared_ptr pa = std::make_shared();
std::shared_ptr pgood = pa->shared_from_this();
return 0;
}
错误用法3:直接用new构造多个shared_ptr作为实参
之前提到的C++异常处理机制,让我们可以很容易发现下面的代码有内存泄漏的危险:
// 声明
void f(A *p1, B *p2);
// 使用
f(new A, new B);
假如new A先于new B发生(我说"假如",是因为C++的函数参数的计算顺序是不确定的),那么如果new B抛出异常,那么new A分配的内存将会发生泄漏。作为一个刚学会shared_ptr的优秀程序员,我们可以如此"解决"该问题:
// 声明
void f(shared_ptr p1, shared_ptr p2);
// 使用
可惜,这么写依然有可能发生内存泄漏,因为两个shared_ptr的构造有可能发生在new A与new B之后,这涉及到C++里称作sequence after,或sequence point的性质,该性质保证:
new B在shared_ptr构造之前发生
两个shared_ptr的构造在f调用之前发生
在满足以上三条性质的前提下,各操作可以以任意顺序执行。详情请见Herb Shutter的文章:Exception-Safe Function Calls 。
make_shared
若我们这么调用f:
一句话建议:总是使用make_shared来生成shared_ptr!