智能指针一些实现分析
提供值传递但是指针语义的功能。通过指针占用并且对管理对象,在离开作用域时释放该对象。
在使用上还有另外一个很好用的功能,精简了代码复杂度,管理的对象类可以省略以下的函数
- 默认构造函数
- 复制构造函数
- 复制赋值函数
比如有一个类 Fd 用于管理 fd ,并且拥有 fd 的所有权,所以 Fd 一定需要包含一个 fd 实体及不应该被复制。
所以需要实现三个函数
- 普通的 fd 构造函数
- 移动构造函数
- 析构函数
            
            
              cpp
              
              
            
          
          struct Fd {
    Fd() = delete;
    Fd(const Fd &) = delete;
    Fd &operator=(const Fd &) = delete;
    Fd(int fd) : fd_(fd) { }
    Fd(Fd &&other) : fd_(other.fd_) { other.fd_ = -1; }
    ~Fd() {
        if (fd_ != -1)
            ::close(fd_);
    }
    int fd_;
};如果 Fd 内几个函数被 delete ,在使用 std::map 的情况下,编译是不通过的。
            
            
              cpp
              
              
            
          
          std::map<std::string, Fd> fdmap;
auto x = fdmap["x"];未实现默认构造函数的报错如下:
            
            
              txt
              
              
            
          
          test.cpp:78:23:   required from here
/usr/include/c++/12/tuple:1818:9: error: no matching function for call to 'Fd::Fd()'
 1818 |         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~在实现了 移动构造函数 但是未实现 复制构造函数 的情况下,默认不会生成 复制构造函数 ,报错如下:
            
            
              txt
              
              
            
          
          test.cpp: In function 'int main()':
test.cpp:78:23: error: use of deleted function 'constexpr Fd::Fd(const Fd&)'
   78 |     auto x = fdmap["x"];
test.cpp:7:8: note: 'constexpr Fd::Fd(const Fd&)' is implicitly declared as deleted because 'Fd' declares a move constructor or move assignment operator使用智能指针进行包装一下,强化了类的设计,一定只能通过一个 fd 来构造一个 Fd 。
            
            
              cpp
              
              
            
          
          std::map<std::string, std::shared_ptr<Fd>> fd_ptr_map;
fd_ptr_map.emplace("x", std::make_shared<Fd>(2));
auto x1 = fd_ptr_map["x"];
if (x1)
    std::cout << x1->fd_ << std::endl; // 2也能同样引用在多态的场景
            
            
              cpp
              
              
            
          
          struct B {};
struct D : public B {};
void fn(std::shared_ptr<B> p) {}
auto p = std::make_shared<D>();
fn(p); // okstd::unique_ptr
可以算是从 auto_ptr 演化而来的智能指针,和 auto_ptr 最大的不同为 unque_ptr 不支持复制操作,语义上是唯一的(符合命名)。
一些其他的特性支持
- 自定义删除器
- 移动语义
在细看 unique_ptr 的实现之前,先看一下 auto_ptr 及其被废弃的原因。
std::auto_ptr(C++17废弃)
std::auto_ptr 是最早的智能指针,对于所有权的管理比较原始,和语义的基础设施也有点关系。
被废弃的最大原因,auto_ptr 支持赋值操作,旧的指针被置为 0(有点类似移动语义)。
代码的实现如下:
            
            
              cpp
              
              
            
          
          auto_ptr &operator=(auto_ptr &__a) throw() {
    reset(__a.release());
    return *this;
}
element_type *release() throw() {
    element_type *__tmp = _M_ptr;
    _M_ptr = 0;
    return __tmp;
}
void reset(element_type *__p = 0) throw() {
    if (__p != _M_ptr) {
        delete _M_ptr;
        _M_ptr = __p;
    }
}这样带来的后果就是之前的 auto_ptr 还能够继续使用,没有限制,非常容易造成问题。
            
            
              cpp
              
              
            
          
          auto p2 = std::auto_ptr<int>(new int(3));
auto p3 = p2;
std::cout << *p3 << std::endl; // 3
std::cout << *p2 << std::endl; // Segmentation fault而且命名上语义不清晰,估计标准委员会也懒得改了,配合 C++11 新增的 delete 关键字和移动语义,直接出一个语义更清晰的 unique_ptr 。
细节
通过 C++11 新增的关键字 delete 将复制的动作删除
            
            
              cpp
              
              
            
          
          // Disable copy from lvalue.
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;在析构函数内通过删除器对对象进行删除,这样就可以自定义删除了。
            
            
              cpp
              
              
            
          
          ~unique_ptr() noexcept {
    static_assert(__is_invocable<deleter_type &, pointer>::value,
                  "unique_ptr's deleter must be invocable with a pointer");
    auto &__ptr = _M_t._M_ptr();
    if (__ptr != nullptr)
        get_deleter()(std::move(__ptr));
    __ptr = pointer();
}
/// Calls `delete __ptr`
_GLIBCXX23_CONSTEXPR
void operator()(_Tp *__ptr) const {
    static_assert(!is_void<_Tp>::value, "can't delete pointer to incomplete type");
    static_assert(sizeof(_Tp) > 0, "can't delete pointer to incomplete type");
    delete __ptr;
}另外还提供了一个 operator bool 函数,可以使用类似普通指针的判断 if (p) { }
            
            
              cpp
              
              
            
          
          explicit operator bool() const noexcept {
    return get() == pointer() ? false : true;
}支持移动语义
            
            
              cpp
              
              
            
          
          auto p1 = std::make_unique<Fd>(2);
auto p2(std::move(p1));
if (p2)
    std::cout << p2->fd_ << std::endl; // 2
// 实现
unique_ptr(unique_ptr<_Up, _Ep> &&__u) noexcept
    : _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter())) {}std::shared_ptr
内部提供了引用计数的智能指针,支持复制的语义,在计数为 0 的时候,对象被释放。
某些场景下 unique_ptr 可能使用受限
            
            
              cpp
              
              
            
          
          std::map<std::string, std::unique_ptr<int>> m;
m.emplace("x", std::make_unique<int>(10));
auto p = m["x"];
// 编译失败
test.cpp: In function 'int main()':
test.cpp:65:19: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
   65 |     auto p = m["x"];
      |                   ^
In file included from /usr/include/c++/12/memory:76,
                 from test.cpp:3:
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~这个时候就需要使用 shared_ptr 来解决这个问题,其内部 operator= 实现为计数加 1
            
            
              cpp
              
              
            
          
          __shared_count(const __shared_count &__r) noexcept : _M_pi(__r._M_pi) {
    if (_M_pi != nullptr)
        _M_pi->_M_add_ref_copy();
}
void _M_add_ref_copy() { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }析构函数的最终实现为,计数-1,如果为最后一个计数,那么就进行释放。
            
            
              cpp
              
              
            
          
          if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1)
    _M_release_last_use();实现
通过 gdb 跟踪一下 std::make_shared 的调用链,停在调用 enable_from_this 的地方。
            
            
              cpp
              
              
            
          
          template <typename _Tp, typename... _Args>
inline shared_ptr<_NonArray<_Tp>> make_shared(_Args && ...__args) {
    using _Alloc = allocator<void>;
    _Alloc __a;
    return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a}, std::forward<_Args>(__args)...);
}
class shared_ptr {
  private:
    template <typename _Yp, typename... _Args>
    friend shared_ptr<_NonArray<_Yp>> make_shared(_Args &&...);
};
template <typename _Alloc, typename... _Args>
shared_ptr(_Sp_alloc_shared_tag<_Alloc> __tag, _Args &&...__args)
    : __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...) {}
template <typename _Alloc, typename... _Args>
__shared_ptr(_Sp_alloc_shared_tag<_Alloc> __tag, _Args &&...__args)
    : _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...) {
    _M_enable_shared_from_this_with(_M_ptr);
}把其中出现的数据结构抽出来,内部有两个变量分别是元素的指针和引用计数结构体。另外继承了一个结构体 __shared_ptr_access .
            
            
              cpp
              
              
            
          
          template <typename _Tp>
class shared_ptr : public __shared_ptr<_Tp> {
};
enum _Lock_policy { _S_single, _S_mutex, _S_atomic };
static const _Lock_policy __default_lock_policy = _S_atomic;
template <typename _Tp, _Lock_policy _Lp = __default_lock_policy>
class __shared_ptr : public __shared_ptr_access<_Tp, _Lp> {
  private:
    element_type *_M_ptr;            // Contained pointer.
    __shared_count<_Lp> _M_refcount; // Reference counter.
};__shared_ptr_access
提供了 shared_ptr 的 * 和 -> 操作。一般情况下使用的类型为
            
            
              cpp
              
              
            
          
          __shared_ptr_access<int, (__gnu_cxx::_Lock_policy)2, false, false>实现为通过继承的方式,在父类中调用子类的成员函数。
            
            
              cpp
              
              
            
          
          // Define operator* and operator-> for shared_ptr<T>.
template <typename _Tp, _Lock_policy _Lp, bool = is_array<_Tp>::value, bool = is_void<_Tp>::value>
class __shared_ptr_access {
  public:
    using element_type = _Tp;
    element_type &operator*() const noexcept {
        __glibcxx_assert(_M_get() != nullptr);
        return *_M_get();
    }
    element_type *operator->() const noexcept {
        _GLIBCXX_DEBUG_PEDASSERT(_M_get() != nullptr);
        return _M_get();
    }
  private:
    element_type *_M_get() const noexcept {
        return static_cast<const __shared_ptr<_Tp, _Lp> *>(this)->get();
    }
};__shared_count
__shared_count 为智能指针的核心实现,包含一个成语变量 _M_pi ,为 _Sp_counted_base 指针类型.
            
            
              cpp
              
              
            
          
          template<_Lock_policy _Lp>
class __shared_count {
  private:
    _Sp_counted_base<_Lp>*  _M_pi;
}_Sp_counted_base
内部定义了基本的引用计数的操作成员函数,和对象释放的虚函数接口。
包含两个变量,加上虚表 _Sp_counted_base 的大小为 16 个字节。
            
            
              cpp
              
              
            
          
          _Atomic_word  _M_use_count;     // #shared
_Atomic_word  _M_weak_count;    // #weak + (#shared != 0)_Sp_counted_ptr_inplace
继承了 _Sp_counted_base ,对象的内存申请和构造在这个类中实现
            
            
              cpp
              
              
            
          
          template <typename _Tp, typename _Alloc, _Lock_policy _Lp>
class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp> {
    using __allocator_type = __alloc_rebind<_Alloc, _Sp_counted_ptr_inplace>;
    // Alloc parameter is not a reference so doesn't alias anything in __args
    template <typename... _Args>
    _Sp_counted_ptr_inplace(_Alloc __a, _Args &&...__args) : _M_impl(__a) {
        // _GLIBCXX_RESOLVE_LIB_DEFECTS
        // 2070.  allocate_shared should use allocator_traits<A>::construct
        allocator_traits<_Alloc>::construct(__a, _M_ptr(),
                                            std::forward<_Args>(__args)...); // might throw
    }
    
    ~_Sp_counted_ptr_inplace() noexcept { }
    class _Impl {
        __gnu_cxx::__aligned_buffer<_Tp> _M_storage;
    };
    _Impl _M_impl;
};_M_impl 中存放的是对象的内存,对象的内存随着析构函数释放。
另外实现了 _M_dispose 和 _M_destroy 两个虚函数.
code
            
            
              cpp
              
              
            
          
          virtual void _M_dispose() noexcept {
    allocator_traits<_Alloc>::destroy(_M_impl._M_alloc(), _M_ptr());
}
// Override because the allocator needs to know the dynamic type
virtual void _M_destroy() noexcept {
    __allocator_type __a(_M_impl._M_alloc());
    __allocated_ptr<__allocator_type> __guard_ptr{__a, this};
    this->~_Sp_counted_ptr_inplace();
}__shared_count 的实现
在 构造函数 内构造对象(及指针)和引用计数。
            
            
              cpp
              
              
            
          
          template <typename _Tp, typename _Alloc, typename... _Args>
__shared_count(_Tp *&__p, _Sp_alloc_shared_tag<_Alloc> __a, _Args &&...__args) {
    typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type;
    typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
    auto __guard = std::__allocate_guarded(__a2);
    _Sp_cp_type *__mem = __guard.get();
    auto __pi = ::new (__mem) _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
    __guard = nullptr;
    _M_pi = __pi;
    __p = __pi->_M_ptr(); // 设置对象指针
}在 析构函数 内释放对象(引用计数),最终调用 _Sp_counted_ptr_inplace::_M_release
            
            
              cpp
              
              
            
          
          ~__shared_count() noexcept {
    if (_M_pi != nullptr)
        _M_pi->_M_release();
}
template <>
inline void _Sp_counted_base<_S_atomic>::_M_release() noexcept {
    _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);
    // ...
    if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1) {
        _M_release_last_use(); // 调用析构函数
    }
}
void _M_release_last_use_cold() noexcept { _M_release_last_use(); }
void _M_release_last_use() noexcept {
    _GLIBCXX_SYNCHRONIZATION_HAPPENS_AFTER(&_M_use_count);
    _M_dispose();
    // ...
}
// 后面就是虚函数 _Sp_counted_base::_M_dispose 了在 复制构造函数 内增加引用计数
            
            
              cpp
              
              
            
          
          __shared_count(const __shared_count &__r) noexcept : _M_pi(__r._M_pi) {
    if (_M_pi != nullptr)
        _M_pi->_M_add_ref_copy();
}
// _Sp_counted_base
void _M_add_ref_copy() { __gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1); }线程安全
_Sp_counted_base 额外对 锁协议(lock policy) 进行特化实现了三套接口,对引用计数的操作保证线程安全,但是后续取出来的指针的线程安全需要用户去保证。
            
            
              cpp
              
              
            
          
          template <>
inline bool _Sp_counted_base<_S_single>::_M_add_ref_lock_nothrow() noexcept {
    if (_M_use_count == 0)
        return false;
    ++_M_use_count;
    return true;
}
template <>
inline bool _Sp_counted_base<_S_mutex>::_M_add_ref_lock_nothrow() noexcept {
    __gnu_cxx::__scoped_lock sentry(*this);
    if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, 1) == 0) {
        _M_use_count = 0;
        return false;
    }
    return true;
}
template <>
inline bool _Sp_counted_base<_S_atomic>::_M_add_ref_lock_nothrow() noexcept {
    // Perform lock-free add-if-not-zero operation.
    _Atomic_word __count = _M_get_use_count();
    do {
        if (__count == 0)
            return false;
        // Replace the current counter value with the old value + 1, as
        // long as it's not changed meanwhile.
    } while (!__atomic_compare_exchange_n(&_M_use_count, &__count, __count + 1, true,
                                          __ATOMIC_ACQ_REL, __ATOMIC_RELAXED));
    return true;
}unique_ptr 到 shared_ptr 转换
unique_ptr 可以转换为 shared_ptr ,需要复制对象内存指针和 移动 删除器,最终实现为
            
            
              cpp
              
              
            
          
          template <typename _Yp, typename _Del, typename = _UniqCompatible<_Yp, _Del>>
__shared_ptr(unique_ptr<_Yp, _Del> &&__r) : _M_ptr(__r.get()), _M_refcount() {
    auto __raw = __to_address(__r.get());
    _M_refcount = __shared_count<_Lp>(std::move(__r));
    _M_enable_shared_from_this_with(__raw);
}
// Special case for unique_ptr<_Tp,_Del> to provide the strong guarantee.
template <typename _Tp, typename _Del>
explicit __shared_count(std::unique_ptr<_Tp, _Del> &&__r) : _M_pi(0) {
    // ...
    _Alloc __a;
    _Sp_cd_type *__mem = _Alloc_traits::allocate(__a, 1);
    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    // 3548. shared_ptr construction from unique_ptr should move (not copy) the deleter
    _Alloc_traits::construct(__a, __mem, __r.release(), std::forward<_Del>(__r.get_deleter()));
    _M_pi = __mem;
}std::weak_ptr
cppreference 的描述是对被 std::shared_ptr 管理的对象存在非拥有性(「弱」)引用,表达临时所有权的概念,在访问所引用的对象前必须先转换为 std::shared_ptr。
一个简单的例子来演示循环引用 shared_ptr 导致资源未释放
            
            
              cpp
              
              
            
          
          class ClassB;
class ClassA {
  public:
    std::shared_ptr<ClassB> b_ptr;
    ClassA(std::shared_ptr<ClassB> b) : b_ptr(b) {}
    ~ClassA() { std::cout << "ClassA destructed" << std::endl; }
};
class ClassB {
  public:
    std::shared_ptr<ClassA> a_ptr;
    ClassB(std::shared_ptr<ClassA> a) : a_ptr(a) {}
    ~ClassB() { std::cout << "ClassB destructed" << std::endl; }
};
int main() {
    auto a = std::make_shared<ClassA>(nullptr);
    auto b = std::make_shared<ClassB>(a);
    a->b_ptr = b;
    // 此时,a 和 b 形成循环引用
    // 程序结束时,它们不会被释放,因为引用计数不会降为0
    return 0;
}引用计数在程序退出的时候分别为 1 ,如果在类中使用 weak_ptr 来代替 shared_ptr ,那么引用计数不会增加,将打破循环引用。
a 和 b 的所有权属于 main 函数,而内部的 a 和 b 属于一个观察者的角色。
一个经典的观察者模式的 demo 代码,演示了如何使用 weak_ptr 扮演一个观察者的角色,此刻的 shared_ptr 的所有权只属于 main 函数。
            
            
              cpp
              
              
            
          
          class Observer {
  public:
    virtual ~Observer() = default;
    virtual void update(int state) = 0;
};
class Foo {
  public:
    Foo() { std::cout << "Foo::Foo" << std::endl; }
    ~Foo() { std::cout << "Foo::~Foo observers_.size=" << observers_.size() << std::endl; }
    void attach(std::shared_ptr<Observer> observer) { observers_.push_back(observer); }
    void detach(const Observer *observer) {
        observers_.erase(std::remove_if(observers_.begin(), observers_.end(),
                                        [&observer](std::weak_ptr<Observer> ob_weak) {
                                            return ob_weak.lock().get() == observer;
                                        }));
    }
    void notify(int state) const {
        for (auto ob_weak : observers_)
            ob_weak.lock()->update(state);
    }
  private:
    std::vector<std::weak_ptr<Observer>> observers_;
};
class FooObserver : public Observer {
  public:
    static std::shared_ptr<FooObserver> create(int fd, std::shared_ptr<Foo> foo) {
        auto observer = std::make_shared<FooObserver>(fd, foo);
        foo->attach(observer); // 完成初始化
        return observer;
    }
    explicit FooObserver(int id, std::shared_ptr<Foo> foo) : id_(id), foo_(foo) {
        std::cout << "FooObserver::FooObserver() Update id=" << id_
                  << ",foo_.use_count=" << foo_.use_count() << std::endl;
    }
    ~FooObserver() {
        std::cout << "FooObserver::~FooObserver id=" << id_
                  << ", foo_.use_count=" << foo_.use_count() << std::endl;
        foo_.lock()->detach(this);
    }
    void update(int state) { std::cout << "Update id=" << id_ << ", state=" << state << std::endl; }
  private:
    int id_;
    std::weak_ptr<Foo> foo_;
};
int main() {
    auto foo = std::make_shared<Foo>();
    auto ob1 = FooObserver::create(101, foo);
    {
        auto ob2 = FooObserver::create(102, foo);
        foo->notify(1);
    }
    foo->notify(2);
}
// 程序输出如下:
// Foo::Foo
// FooObserver::FooObserver() Update id=101,foo_.use_count=3
// FooObserver::FooObserver() Update id=102,foo_.use_count=3
// Update id=101, state=1
// Update id=102, state=1
// FooObserver::~FooObserver id=102, foo_.use_count=1
// Update id=101, state=2
// FooObserver::~FooObserver id=101, foo_.use_count=1
// Foo::~Foo observers_.size=0实现
weak_ptr 和 shared_ptr 的实现相似,内部包含两个成员变量
            
            
              cpp
              
              
            
          
          template <typename _Tp, _Lock_policy _Lp = __default_lock_policy>
class __weak_ptr {
    element_type*     _M_ptr;         // Contained pointer.
    __weak_count<_Lp> _M_refcount;    // Reference counter.
};__weak_count 和 __shared_count 类似,实现了计数操作和保存对象指针( shared_count 是申请内存及构造对象),两者都包含同类型指针。
            
            
              cpp
              
              
            
          
          template <_Lock_policy _Lp = __default_lock_policy> class
__weak_count {
    _Sp_counted_base<_Lp>*  _M_pi;
};这里只关注 weak_ptr 和 shared_ptr 的关联的部分,其余细节部分忽略.
基于 shared_ptr 构造 weak_ptr
就是将复制一下指针,弱引用计数+1
            
            
              cpp
              
              
            
          
          __weak_count(const __shared_count<_Lp>& __r) noexcept
: _M_pi(__r._M_pi)
{
    if (_M_pi != nullptr)
        _M_pi->_M_weak_add_ref();
}从 weak_ptr 转换到 shared_ptr
weak_ptr 的 lock 实现如下,还是对指针的一个复制( _M_ptr 和 _M_pi )
            
            
              cpp
              
              
            
          
          // weak_ptr::lock
shared_ptr<_Tp> lock() const noexcept { return shared_ptr<_Tp>(*this, std::nothrow); }
// This constructor is non-standard, it is used by weak_ptr::lock().
shared_ptr(const weak_ptr<_Tp> &__r, std::nothrow_t) noexcept
    : __shared_ptr<_Tp>(__r, std::nothrow) {}
// This constructor is used by __weak_ptr::lock() and
// shared_ptr::shared_ptr(const weak_ptr&, std::nothrow_t).
__shared_ptr(const __weak_ptr<_Tp, _Lp> &__r, std::nothrow_t) noexcept
    : _M_refcount(__r._M_refcount, std::nothrow) {
    _M_ptr = _M_refcount._M_get_use_count() ? __r._M_ptr : nullptr;
}
// Now that __weak_count is defined we can define this constructor:
template <_Lock_policy _Lp>
inline __shared_count<_Lp>::__shared_count(const __weak_count<_Lp> &__r, std::nothrow_t) noexcept
    : _M_pi(__r._M_pi) {
    if (_M_pi && !_M_pi->_M_add_ref_lock_nothrow())
        _M_pi = nullptr;
}enable_shared_from_this
可以通过调用成员函数 shared_from_this 让一个对象生成一个 shared_ptr 实例,进行共享所有权,
先看一下 enable_shared_from_this 结构,内部包含了一个 weak_ptr ,上面已经了解其用法: 作为观察者,必要的时候转换为 shared_ptr 共享所有权 。
            
            
              cpp
              
              
            
          
          template <typename _Tp> class enable_shared_from_this {
    mutable weak_ptr<_Tp>  _M_weak_this;
};shared_from_this
那么何时转换为 shared_ptr ? 答:shared_from_this 。其实现为
            
            
              cpp
              
              
            
          
          shared_ptr<_Tp>
shared_from_this()
{ return shared_ptr<_Tp>(this->_M_weak_this); }构造 shared_ptr 的过程和 weak_ptr::lock 相似,但是 lock() 不抛异常,shared_from_this 的构造是会抛异常的。
因为 lock 惯用在一些判断的逻辑内,而 shared_from_this 一般是作为一个有效所有权的实例参数传递,在后续的处理中再进行判断过于繁琐,直接抛异常更直观一些。
            
            
              cpp
              
              
            
          
          // Now that __weak_count is defined we can define this constructor:
template <_Lock_policy _Lp>
inline __shared_count<_Lp>::__shared_count(const __weak_count<_Lp> &__r) : _M_pi(__r._M_pi) {
    if (_M_pi == nullptr || !_M_pi->_M_add_ref_lock_nothrow())
        __throw_bad_weak_ptr();
}标准规定 只容许在先前已由 std::shared_ptr 管理的对象上调用 shared_from_this,如何实现?
__shared_ptr 在构造的最后一步构造可能存在的 enable_shared_from_this
            
            
              cpp
              
              
            
          
          template <typename _Alloc, typename... _Args>
__shared_ptr(_Sp_alloc_shared_tag<_Alloc> __tag, _Args &&...__args)
    : _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...) {
    _M_enable_shared_from_this_with(_M_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 _Tp1>
void _M_weak_assign(_Tp1 *__p, const __shared_count<_Lp> &__n) const noexcept {
    _M_weak_this._M_assign(__p, __n);
}
// Used by __enable_shared_from_this.
void _M_assign(_Tp *__ptr, const __shared_count<_Lp> &__refcount) noexcept {
    if (use_count() == 0) {
        _M_ptr = __ptr;
        _M_refcount = __refcount;
    }
}std::shared_ptr 持有的对象 T 进行构造发生在 _M_refcount 内,还没有到达 _M_enable_shared_from_this_with 处。
所以如果在构造函数内调用 shared_from_this 时,enable_shared_from_this::_M_weak_this 引用的对象为空,会抛出异常。
这就是为何上面观察者模式的 demo 使用了一个工厂函数 create 的原因.
同样的,在析构函数函数内也不能调用 shared_from_this,在析构函数调用之前,引用计数已经被置为 0
            
            
              cpp
              
              
            
          
          template <>
inline void _Sp_counted_base<_S_atomic>::_M_release() noexcept {
    _GLIBCXX_SYNCHRONIZATION_HAPPENS_BEFORE(&_M_use_count);
    // ...
    if (__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1) == 1) {
        _M_release_last_use(); // 内部调用析构函数
    }
}智能指针内存占用
unique_ptr 分配的内存为对象本身的大小,本身占用 8 个字节。
shared_ptr 占用情况为 16 字节( _Sp_counted_base 的两个变量加虚表)+ 对象对齐占用内存,比如有一个对象为
            
            
              cpp
              
              
            
          
          struct Fd {
    int32_t fd;
};
sizeof(Fd) == 4;那么 std::shared_ptr 的分配内存为 24 字节,智能指针本身内存为 8 个字节。
weak_ptr 不涉及内存分配,占用 16 个字节。
benchmark
对智能指针的 operator* 进行 benchmark,以普通指针作为参考,测试代码如下
            
            
              cpp
              
              
            
          
          // 防止进行编译期优化
static uint64_t normal = reinterpret_cast<uint64_t>(&normal);
static uint64_t unique = reinterpret_cast<uint64_t>(&unique);
static uint64_t shared = reinterpret_cast<uint64_t>(&shared);
static void BM_normal_ptr(benchmark::State &state) {
    auto x = new uint64_t(1);
    for (auto _ : state) {
        *x += *x + 1;
        normal += *x;
    }
}
static void BM_unique_ptr(benchmark::State &state) {
    auto x = std::make_unique<uint64_t>(1);
    for (auto _ : state) {
        *x += *x + 1;
        unique += *x;
    }
}
static void BM_shared_ptr(benchmark::State &state) {
    auto x = std::make_shared<uint64_t>(1);
    for (auto _ : state) {
        *x += *x + 1;
        shared += *x;
    }
}结果如下:
            
            
              txt
              
              
            
          
          // 优化等级 -O0
--------------------------------------------------------
Benchmark              Time             CPU   Iterations
--------------------------------------------------------
BM_normal_ptr       1.24 ns         1.24 ns    562874831
BM_unique_ptr       27.7 ns         27.7 ns     25264819
BM_shared_ptr       9.35 ns         9.35 ns     75253037
// 优化等级 -O2
--------------------------------------------------------
Benchmark              Time             CPU   Iterations
--------------------------------------------------------
BM_normal_ptr      0.219 ns        0.219 ns   3157103856
BM_unique_ptr      0.217 ns        0.217 ns   3182580916
BM_shared_ptr      0.217 ns        0.217 ns   3206275755挺意外的一个点是 unique_ptr 在没有优化的情况下居然是最耗时间的,不过在 -O2 的优化等级下,三个指针的访问速度是同一个级别的。
总结
- 使用智能指针可以减少代码的行数,加强所有权语义
- unique_ptr 不支持复制的动作,适用无共享所有权的地方
- shared_ptr 适用于需要共享所有权的地方,并且智能指针本身的操作是线程安全的,但是不保证对象操作是线程安全的。
- weak_ptr 适用于观察者的角色,作为辅助 shared_ptr 而存在,可以切断循环引用。
- enable_shared_from_this 适用在对象内部转换一个新的 shared_ptr,比如在 asio 中作为 tcp 连接读写函数的参数
 
- 内存占用 shared_ptr 相比 unique_ptr 多出 16+ 字节分配。
- benchmark 开启 -O2 的优化等级后,三类指针的解指针访问速度是同一个等级的。