主要概念
std::enable_shared_from_this,能通过其shared_from_this()函数获得当前shared_ptr的副本,指向原指针对象一样,此副本析构的时候不会导致二次析构(如果直接用std::shared_tr<T>(this), 就会有这个问题),因为有了副本,就不会有直接使用原本的shared_ptr对象,从而避免shared_ptr的循环引用问题。
一般会这样用:
class NodeBase : public std::enable_shared_from_this<NodeBase>
{
public:
NodeBase()
{
std::cout << "NodeBase::constructor()...\n";
}
[[nodiscard]] std::shared_ptr<NodeBase> getptr()
{
return shared_from_this();
}
virtual ~NodeBase()
{
std::cout << "NodeBase::destructor()...\n";
}
};
template<typename T>
class BigUnitFT : public std::enable_shared_from_this<BigUnitFT<T>>
{
public:
T value;
BigUnitFT()
{
std::cout << "BigUnitFT::constructor()...\n";
}
virtual ~BigUnitFT()
{
std::cout << "BigUnitFT::destructor()...\n";
}
};
void testMain()
{
{
std::shared_ptr<NodeBase> p0 = std::make_shared<NodeBase>();
std::shared_ptr<NodeBase> q0 = p0->getptr();
std::cout << "(p0 == q0): " << (p0.get() == q0.get()) << std::endl;
std::cout << p0.use_count() << std::endl;
std::cout << q0.use_count() << std::endl;
}
{
std::shared_ptr<BigUnitFT<double>> p0(new BigUnitFT<double>());
std::shared_ptr<BigUnitFT<double>> q0 = p0->shared_from_this();
std::cout << "(p0 == q0): " << (p0.get() == q0.get()) << std::endl;
std::cout << p0.use_count() << std::endl;
std::cout << q0.use_count() << std::endl;
}
}
如果将std::enable_shared_from_this用于更复杂的实现需求中(例如某些类模板、多重继承等应用情况)可能会很不方便,这时候就需要用实现原理自己实现相关功能。先简单看一下源码:
enable_shared_from_this是一个基类,在MSVC中enable_shared_from_this功能实现依赖的两个关键模板函数, 位于:
Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\memory文件中的template <class _Ty>
class shared_ptr : public _Ptr_base<_Ty> {}
定义中。
Linux GCC 请参见: gcc\libstdc++-v3\include\bits\shared_ptr_base.h
也就是说enable_shared_from_this实际上是基于std::shared_ptr的扩展
// 构造的时候调用
template <class _Ux>
void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept
{ // take ownership of _Px
this->_Ptr = _Px;
this->_Rep = _Rx;
if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>)
{
if (_Px && _Px->_Wptr.expired())
{
_Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));
}
}
}
// 析构的时候调用
void _Set_ptr_rep_and_enable_shared(nullptr_t, _Ref_count_base* const _Rx) noexcept
{ // take ownership of nullptr
this->_Ptr = nullptr;
this->_Rep = _Rx;
}
由此可简洁的自定义一个enable_shared_from_this_t类来happy一下,代码如下:
template <class _Ty>
class enable_shared_from_this_t
{
public:
// 下面这一句是关键代码
using _Esft_type = enable_shared_from_this_t;
_NODISCARD std::shared_ptr<_Ty> shared_from_this()
{
return std::shared_ptr<_Ty>(_Wptr);
}
protected:
constexpr enable_shared_from_this_t() noexcept
{
}
~enable_shared_from_this_t() = default;
private:
template <class _Yty>
friend class std::shared_ptr;
// _Wptr变量名不可变
mutable std::weak_ptr<_Ty> _Wptr;
};
自定义实现
如何自定义实现 std::enable_shared_from_this才具有的返回shared_ptr副本功能呢?,直接上代码(MSVC)吧:
class BigUnit
{
public:
using _Esft_type = BigUnit;
BigUnit()
{
std::cout << "BigUnit::constructor()...\n";
}
[[nodiscard]] std::shared_ptr<BigUnit> getSharedPtr()
{
return std::shared_ptr<BigUnit>(_Wptr);
}
[[nodiscard]] std::shared_ptr<const BigUnit> getSharedPtr() const
{
return std::shared_ptr<const BigUnit>(_Wptr);
}
virtual ~BigUnit()
{
std::cout << "BigUnit::destructor()...\n";
}
private:
template <class _Yty>
friend class std::shared_ptr;
mutable std::weak_ptr<BigUnit> _Wptr;
};
好了,可以用了。如果还要更高级的功能,自己扩展实现一个shared_ptr<TTTT>。。。