shared_from_this的作用是返回一个指向对象this指针的有效shared_ptr.
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_from_this();
}
}
int main()
{
shared_ptr<Y> p(new Y);
shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p));
}
如果不使用shared_from_this,而是直接返回一个用this指针构造的shared_ptr,如下图所示:
class Y: public enable_shared_from_this<Y>
{
public:
shared_ptr<Y> f()
{
return shared_ptr<Y>(this);
}
}
这是一个错误的方法,会导致很严重的后果。主要原因是这会导致shared_ptr的引用计数产生错误。 shared_ptr的引用计数是用来表示有多少个shared_ptr指向了同一对象。例如,上面这个例子中有两个shared_ptr(p和q)指向了Y, 所以p和q它们的引用计数应该是2.但是如果采用第二种写法那么p和q的引用都是1,这样当p和q中的一个析构的时候Y这个对象将会被析构,当另一个共享指针也析构的时候就会析构一个null指针。 如果两个shared_ptr,正确的内存布局可能是这样的: 我们知道共享指针析构的时候会将引用计数减1,当引用计数为0时就释放它指向的对象,这个例子中只有当p和q都释放的时候才去释放对象Y. 如果采用第二种错误的写法,其内存布局是这样的: 这种错误写法导致指向同一对象的两个共享指针各自创建了一个控制块,各自维护自己的引用计数,最终导致出现多次释放对象的问题。只要p和q中有一个释放就会马上释放对象Y。 同样以下写法是错误的:
auto pw = new Widget;
…
std::shared_ptr<Widget> spw1(pw);
…
std::shared_ptr<Widget> spw2(pw);
C++为了解决这个由于用裸指针构造共享指针带来的问题,提供了enable_shared_from_this这个API.它是一个用做基类的模板类,模板类型参数是某个想被std::shared_ptr管理且能从该类型的this对象上安全创建std::shared_ptr指针的存在。上面的的例子中,Y将会继承自std::enable_shared_from_this:
class Y: public enable_shared_from_this<Y>
{
......
}
std::enable_shared_from_this是一个用作基类的模板类。它的模板参数总是某个继承自它的类,所以Y继承自std::enable_shared_from_this。 std::enable_shared_from_this定义了一个成员函数,成员函数会创建指向当前对象的std::shared_ptr却不创建多余控制块。这个成员函数就是shared_from_this。 从内部来说,shared_from_this查找当前对象控制块,然后创建一个新的std::shared_ptr指向这个控制块。设计的依据是当前对象已经存在一个关联的控制块。 但是有个问题需要注意,必须已经存在一个指向当前对象的std::shared_ptr(即调用shared_from_this的成员函数外面已经存在一个std::shared_ptr)。如果没有std::shared_ptr指向当前对象(即当前对象没有关联控制块),行为是未定义的,shared_from_this通常抛出一个异常。
|