首先要说明的一个问题是:

如何安全地将 this 指针返回给调用者?

一般来说, 我们不能直接将 this 指针返回. 想象这样的情况, 该函数将 this 指针返回到外部某个变量保存, 然后这个对象自身已经析构了, 但外部变量并不知道, 那么此时如果外部变量使用这个指针, 就会使得程序崩溃. 那么使用智能指针 shared_ptr 呢? 当然, 使得 c++ 指针安全目前来说最有效也使用最多的办法就是使用智能指针 shared_ptr. 首先假设你已经理解shared_ptr 的工作原理。然后我们来看下面一份代码:

#include <iostream>
#include <memory>

class Bad {
public:
    Bad()  { std::cout << "Bad()" << std::endl; }
    ~Bad() { std::cout << "~Bad()" << std::endl; }
    std::shared_ptr<Bad> getPtr() {
        return std::shared_ptr<Bad>(this);  //返回shared_ptr
    }
};

int main(int argc, char const *argv[])
{
    std::shared_ptr<Bad> bp1(new Bad);
    std::shared_ptr<Bad> bp2 = bp1->getPtr();
    std::cout << "bp2.use_count: " << bp2.use_count() << std::endl;
    return 0;
}

//运行结果:
Bad()
bp2.use_count: 1
~Bad()
~Bad()
a(822,0x7fff737b5300) malloc: *** error for object 0x7fe74bc04b50: pointer being freed was not allocated

我们可以看到, 对象只构造了一次, 但却析构了两次. 并且在增加一个指向的时候 shared_ptr的计数并没有增加. 也就是说, 这个时候 bp1 和 bp2 都认为只有自己才是这个智能指针的拥有者。其实也就是说, 这里建了两个智能指针同时处理 this 指针. 每个智能指针在计数为0的时候都会调用一次Bad对象的析构函数. 所以会出问题.

其实现在问题就变成了, 如何在对象中获得一个指向当前对象的 shared_ptr 对象. 如果我们能够做到这个, 那么直接将这个shared_ptr 对象返回, 就不会造成新建 shared_ptr 的问题了.

enable_shared_from_this 是一个以其派生类为模板类型实参的基类模板,继承它,派生类的this指针就能变成一个 shared_ptr。先来看下面一份代码:

#include <iostream>
#include <memory>

class Good: public std::enable_shared_from_this<Good> {  //注意模板类参数的填写,Good
public:
    Good() { std::cout << "Good()" << std::endl; }
    ~Good() { std::cout << "~Good()" << std::endl; }

    std::shared_ptr<Good> getPtr() {
        return shared_from_this();    // shared_ptr
    }
};

int main(int argc, char const *argv[])
{
    std::shared_ptr<Good> bp1(new Good);
    std::shared_ptr<Good> bp2 = bp1->getPtr();
    std::cout << "bp2.use_count: " << bp2.use_count() << std::endl;
    return 0;
}

//执行结果:
Good()
bp2.use_count: 2
~Good()

enable_shared_from_this 的工作原理

template<class T> class enable_shared_from_this
{
protected:

    enable_shared_from_this() BOOST_NOEXCEPT {}
    enable_shared_from_this(enable_shared_from_this const &) BOOST_NOEXCEPT  {}

    enable_shared_from_this & operator=(enable_shared_from_this const &) BOOST_NOEXCEPT {
        return *this;
    }
    ~enable_shared_from_this() BOOST_NOEXCEPT {} // ~weak_ptr<T> newer throws, so this call also must not throw


public:

    shared_ptr<T> shared_from_this()
    {
        shared_ptr<T> p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
    }

    shared_ptr<T const> shared_from_this() const
    {
        shared_ptr<T const> p( weak_this_ );   //每次调用shared_from_this()时候,就会根据 weak_ptr来构造一个临时的shared_ptr
        BOOST_ASSERT( p.get() == this );
        return p;
    }

public: // actually private, but avoids compiler template friendship issues

    // Note: invoked automatically by shared_ptr; do not call
    template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
    {
        if( weak_this_.expired() )
        {
            weak_this_ = shared_ptr<T>( *ppx, py );
        }
    }

private:

    mutable weak_ptr<T> weak_this_;  //注意这里是使用一个weak_ptr对象来构造对外的 shared_ptr对象。
    
};

这里会有疑问, shared_from_this()返回的 shared_ptr[107行] 和 之前的[26行]有什么区别?以及,为什么 enable_shared_from_this不直接保存一个 shared_ptr成员?

  • 任何继承自 enable_shared_from_this类的子类A,都会生成一个A类的weak_ptr对象: weak_this_. 而每次调用shared_from_this(), 都是通过同一个 weak_ptr对象来构建出的 shared_ptr对象.
  • 如果enable_shared_from_this类使用的是shared_ptr来保存对象指针, 而不是weak_ptr. 那么该shared_ptr指向自身,那么这个shared_ptr的引用计数最少会是1, 也就是这个对象将永远不能被析构, 而使用weak_ptr不会引起shared_ptr计数的增加。

enable_shared_from_this 的成员变量在 enable_shared_from_this 构造的时候是没有指向任何对象的, 在第一次调用 shared_ptr的时候, 由 shared_ptr 调用 boost::detail::sp_enable_shared_from_this 然后调用enable_shared_from_this 的 internal_accept_owner来对其进行初始化。看下面的过程就明白了:

template<class Y>
explicit shared_ptr( Y * p ): px( p ), pn( p ) 
{   //这里使用了 boost::detail::sp_enable_shared_from_this
    boost::detail::sp_enable_shared_from_this( this, p, p );
}
template< class X, class Y, class T >
inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx,
                                        Y const * py, 
                                        boost::enable_shared_from_this< T > const * pe )
{
    if( pe != 0 )
    {   //而在这里面又调用 enable_shared_from_this 的 _internal_accept_owner
        pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
    }
}
template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
{
    if( weak_this_.expired() ) // 成员函数expired()的功能等价于use_count()==0
    {
        weak_this_ = shared_ptr<T>( *ppx, py );
    }
}

从这个构造过程可以看出,当 weak_ptr 第一次指向一个对象后,它就担起了观察者的角色,一直观察着同一个对象

什么场景下需要在成员函数中获取对象自己的智能指针?

  • 声明周期保护. 比如, 我们设计了一个回调, 当回调被调用时在成员函数中做一些对象的清理操作(或其他任何操作)
#inlcude <memory>

class A : public std::enable_shared_from_this<A> {
public:
	void Test() {
		//下面一行代码保护对象生命周期
		auto self = shared_from_this();
		{
			std::lock_guard<std::mutex> lock(mutex_);
			//该函数被回调时候,如果该对象已经被上层释放,那么程序就会crush
			delegate_->OnComplete(); //do something
		}
	}
}
  • lambda 表达式捕获 this指针, 如果this指向的对象被析构了, 那么再次使用this时候就会crush
#include <memory>
class Apple {
 public:
  void Test() {
    //lambda 捕获this 指针
    http_client_->Request([this] {
      // Do Something
    });
  }

 private:
  std::unique_ptr<HttpClientt> http_client_;
}

//改进:
class Apple : public std::enable_shared_from_this<Apple> {
public:
  Apple() {

  }
 void Test() {
   auto weak_self = weak_from_this();
   //这里捕获weak,所以如果Apple 对象被析构了。 weak_self.lock将返回一个null
   http_client_->Request([weak_self] {
     if (auto self = weak_self.lock()) {
       //Do Something
     }
   });
 }

 private:
  std::unique_ptr<HttpClientt> http_client_;
};

注意点

  • 不能在构造函数中使用
  • 不能在析构函数中使用 (进入到对象的析构函数,说明强引用计数已经变为0了,这个时候转换成shared_ptr肯定是不行的)