enable_shared_from_this类的作用和实现

目录

 

问题:如何安全地将this指针返回给调用者

enable_shared_from_this的实现分析


问题:如何安全地将this指针返回给调用者

一般来说,我们不能直接将this指针返回。
想象这样的情况,该函数将this指针返回到外部某个变量保存,然后这个对象自身已经析构了,但外部变量并不知道,此时如果外部变量使用这个指针,就会使得程序崩溃。

使用智能指针shared_ptr看起来是个不错的解决方法。但问题是如何去使用它呢?我们来看如下代码:

struct Bad
{
	std::shared_ptr<Bad> getptr() {
		return std::shared_ptr<Bad>(this);
	}
	~Bad() { std::cout << "Bad::~Bad() called\n"; }
};

int main()
{
	// Bad, each shared_ptr thinks it's the only owner of the object
	std::shared_ptr<Bad> bp1 = std::make_shared<Bad>();
	std::shared_ptr<Bad> bp2 = bp1->getptr();
	std::cout << "bp2.use_count() = " << bp2.use_count() << '\n';
}// UB: double-delete of Bad

程序异常,输出:

bp2.use_count() = 1
Bad::~Bad() called

 在 getptr函数构造智能指针时, 将裸指针this交给shared_ptr管理,shared_ptr无法确定这个对象是不是被 shared_ptr 管理着, 因此这样构造的 shared_ptr 并不是与其他 shared_ptr 共享一个计数器, 那么, 在析构时就会导致对象被重复释放, 从而引发错误。

如何在一个对象内部构造该对象的 shared_ptr , 即使该对象已经被 shared_ptr 管理着, 也不会造成对象被两个独立的智能指针管理。

正确做法是继承 enable_shared_from_this 类, 调用 shared_from_this() 函数生成 shared_ptr, 使用如下:

struct Good : std::enable_shared_from_this<Good> // note: public inheritance
{
	std::shared_ptr<Good> getptr() {
		return shared_from_this();
	}
};

struct Bad
{
	std::shared_ptr<Bad> getptr() {
		return std::shared_ptr<Bad>(this);
	}
	~Bad() { std::cout << "Bad::~Bad() called\n"; }
};

int main()
{
	 //Good: the two shared_ptr's share the same object
	std::shared_ptr<Good> gp1 = std::make_shared<Good>();
	std::shared_ptr<Good> gp2 = gp1->getptr();
	std::cout << "gp2.use_count() = " << gp2.use_count() << '\n';

	// Bad: shared_from_this is called without having std::shared_ptr owning the caller 
	try {
		Good not_so_good;
		std::shared_ptr<Good> gp1 = not_so_good.getptr();
	}
	catch (std::bad_weak_ptr& e) {
		// undefined behavior (until C++17) and std::bad_weak_ptr thrown (since C++17)
		std::cout << e.what() << '\n';
	}
}

输出:

gp2.use_count() = 2
bad_weak_ptr

可以看到对象只够造了一次,使用shared_from_this只会把引用计数加1 

enable_shared_from_this的实现分析

接着来看看enable_shared_from_this 是如何工作的,以下是它的源码:

// enable_shared_from_this的实现
// 基于(/usr/include/c++/7.3.0/bits/shared_ptr.h)
// 此代码是对gcc实现的简化版本, 仅作为描述原理用.
template<typename T>
class enable_shared_from_this
{
public:
    shared_ptr<T> shared_from_this()
    {
        return shared_ptr<T>(this->weak_this);
    }
    shared_ptr<const T> shared_from_this() const
    {
        return shared_ptr<const T>(this->weak_this);
    }
private:
    template<typename>
    friend class shared_ptr;

    template<typename T1>
    void _M_weak_assign(T1* p, const shared_count<>& n)
    {
      weak_this._M_assign(p, n);
    }

    mutable weak_ptr<T> weak_this;
};

enable_shared_from_this<T> 类中定义了一个 weak_ptr<T>, 起到了上文提到的从obj指针生成 shared_ptr<T> 对象的作用. 按照先前的原理, 我们可能认为是在obj初始化的时候, 同时对 weak_this 进行初始化, 但是在这段代码里显然没有对 weak_this 进行任何初始化工作(原始代码里也没有,为什么? 这是因为当对象没有由智能指针管理时, 这些操作是没有必要的. 所以应该把这个任务交给 shared_ptr).

在 shared_ptr<T> 的构造函数中对 weak_ptr<T> 进行处理. 从 Good 类来看, 就是在 std::make_shared<Good>();处对 Good 对象中的 weak_ptr<Good> weak_this 进行处理, 使其指向一个有效的 Good 对象, 并修改 use_count. 上面 Good 类对 enable_shared_from_this 的使用是少数几种有效的方法, 必须保证, 如果对一个对象调用 shared_from_this(), 该对象必须是由 shared_ptr<T> 持有的. 从上一段的原理中可以理解这样做的原因: 第一个持有 Good 对象 g_obj 的 shared_ptr<T> sp1 会对 g_obj 的 weak_this 进行处理, 使其有效. 如果没有这一步, 在调用 shared_from_this() 时, weak_this 是一个无效值, 即 weak_this.expire() == true, 就会抛出异常.

那么在 shared_ptr 的构造函数中是如何处理 weak_ptr 的呢?在 shared_ptr 中定义了这样一个函数

template<typename _Yp, typename _Yp2 = typename remove_cv<_Yp>::type>
typename enable_if<__has_esft_base<_Yp2>::value>::type
_M_enable_shared_from_this_with(_Yp* __p) noexcept
{
    if(auto __base = __enable_shared_from_this_base(_M_refcount, __p))
        __base->_M_weak_assign(const_cast<_Yp2*>(__p), _M_refcount);
}

template<typename _Yp, typename _Yp2 = typename remove_cv<_Yp>::type>
typename enable_if<!__has_esft_base<_Yp2>::value>::type
_M_enable_shared_from_this_with(_Yp*) noexcept { }

其中 _Yp 是 shared_ptr 管理的对象的类型. 这两个模板函数表示:

当 _Yp 是 enable_shared_from_this 的子类时, 就会生成第一个函数, 其功能是通过 _Yp 对象的指针来调用其 _M_weak_assign 函数以修改 _Yp 对象的 weak_this 成员, 而实际上 _M_weak_assign 调用的是 _M_assign 函数. 

否则生成第二个函数体为空的函数.

// from shared_ptr_base.h class __weak_ptr, derived by weak_ptr

void _M_assign(_Tp* __ptr, const __shared_count<_Lp>& __refcount) noexcept
{
    if (use_count() == 0)
    {
        _M_ptr = __ptr;
        _M_refcount = __refcount;
    }
}

_M_enable_shared_from_this_with 函数在 shared_ptr<_Yp> 的构造函数中被调用, 从而检测 _Yp 是否继承自 make_shared_from_this, 并进行相应的处理. 这里的 _M_refcount 是 shared_ptr 的成员, 用来记录 _Yp 被多少 shared_ptr 管理. 这样, 就完成了对 weak_ptr 的处理, 使其成为一个有效值. 在以后调用 shared_from_this() 函数时, 就能利用 weak_this 调用 shared_ptr 的构造函数, 从而生成一个共享同一对象的 shared_ptr.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值