C++11智能指针标准库简化实现及enable_shared_from_this解析

shared_ptr数据定义

shared_ptr使用引用计数管理一个对象资源,简单定义如下:

cpp 复制代码
template <typename T>
class SharedPtr {
private:
    T* _ptr;
    atomic<int>* _cntrl;
}
  • 以上实现简化了标准库控制块,只有计数,缺少了弱引用计数(weak_ptr使用)
  • 实际使用中,引用计数为0时,并没有释放控制块资源,但是资源已经不可用了,当弱引用计数计数也为0时,才会释放控制块资源

标准库控制块实现:

cpp 复制代码
class __shared_count {
protected:
    long __shared_owners_;  // 用于shared_ptr 引用计数
};
class __shared_weak_count : private __shared_count {
    long __shared_weak_owners_; // 用于weak_ptr 弱引用计数
};
class shared_ptr {
private:
    element_type*      __ptr_;
    __shared_weak_count* __cntrl_;
};

源码中有一个值得学习的实现:

  • 源码中拷贝赋值运算符函数使用了一个栈变量
  • 相当于使用了构造函数增加要复制的资源的引用计数析构函数减少现在管理的资源的引用计数
  • 不用额外定义一个引用计数加减的函数,简化实现如下:
cpp 复制代码
SharedPtr& operator=(const SharedPtr& sp) noexcept {
    // 建立一个栈变量 相当于使用了构造函数和析构函数 不用额外定义一个引用计数加减的函数
    SharedPtr(sp).swap(*this);
    return *this;
}
void swap(SharedPtr& sp) {
    std::swap(_ptr, sp._ptr);
    std::swap(_cntrl, sp._cntrl);
}

weak_ptr

  • weak_ptr数据成员和shared_ptr一致,多了几个成员函数,判断资源是否可用以及获得shared_ptr
  • 未实现弱引用计数
cpp 复制代码
template <typename T>
class WeakPtr {
public:
    WeakPtr() {}
    template <typename U>
    WeakPtr(const SharedPtr<U>& sp) noexcept : _ptr(sp._ptr), _cntrl(sp._cntrl) {}
    template <typename U>
    WeakPtr& operator=(const SharedPtr<U>& sp) noexcept {
        WeakPtr(sp).swap(*this);
        return *this;
    }
    ~WeakPtr() {
        _ptr = nullptr;
        _cntrl = nullptr;
    };
    SharedPtr<T> Lock() const noexcept {
        SharedPtr<T> p;
        if (_cntrl) {
            int cur = _cntrl->load();
            while (cur != 0) {
                if (_cntrl->compare_exchange_strong(cur, cur + 1)) {
                    p._cntrl = _cntrl;
                    break;
                }
            }
        }
        if (p._cntrl) {
            p._ptr = _ptr;
        }
        return p;
    }
    bool Expired() const { return _cntrl == nullptr || _cntrl->load() == 0; }

private:
    void swap(WeakPtr& sp) {
        std::swap(_ptr, sp._ptr);
        std::swap(_cntrl, sp._cntrl);
    }

    T* _ptr;
    atomic<int>* _cntrl;
};

源码中有一个值得学习的原子变量的使用方式:

  • 当引用计数为0时,不可以操作,所以需要查询和修改两个操作
  • 使用while循环compare_exchange_strong函数,只有当原子变量在查询后未变化时,才修改原子变量的值

enable_shared_from_this机制

enable_shared_from_this为了解决this指针转为shared_ptr的问题,例子如下:

cpp 复制代码
std::vector<std::shared_ptr<Widget>> processedWidgets;
class Widget : public std::enable_shared_from_this<Widget> {
public:
  void process() {
    // 每次调用都会创建一个引用计数 会造成资源多次delete
    // processedWidgets.emplace_back(shared_ptr<Widget>(this));
    processedWidgets.emplace_back(shared_from_this());
  }
};

shared_from_this函数会返回指向同一个控制款的shared_ptr,避免了资源多次delete

enable_shared_from_this内部使用weak_ptr实现,简化标准库后的实现源码如下:

cpp 复制代码
template <typename T>
class EnableSharedFromThis {
public:
    SharedPtr<T> SharedFromThis() { return SharedPtr<T>(_weak_this); }
    SharedPtr<const T> SharedFromThis() const {
        // const变量调用此函数
        return SharedPtr<const T>(_weak_this);
    }

protected:
    EnableSharedFromThis() noexcept {}
    // 构造函数
    EnableSharedFromThis(const EnableSharedFromThis&) noexcept {}
    // 拷贝构造函数
    EnableSharedFromThis& operator=(const EnableSharedFromThis&) noexcept { return *this; }
    ~EnableSharedFromThis() {}

private:
    mutable WeakPtr<T> _weak_this;

    template <typename U>
    friend class SharedPtr;
};

可以看到未指向资源的成员变量_weak_this,SharedFromThis函数weak_ptr中提升返回指向资源的shared_ptr

实际_weak_this的初始化是在shared_ptr构造函数中完成的,所以使用enable_shared_from_this时,需要外部定义存在一个shared_ptr指向当前对象,如果不定义,会导致_weak_this为空,调用SharedFromThis函数时,会抛bad_weak_ptr异常,和用weak_ptr创建一个shared_ptr时抛的异常一致

shared_ptr构造函数中初始化_weak_this的简化实现代码如下:

cpp 复制代码
SharedPtr(T* p) : _ptr(p) {
    unique_ptr<T> hold(p);
    _cntrl = new atomic<int>(1);
    hold.release();
    __enable_weak_this(p, p);
}

其中主要实现精髓是__enable_weak_this成员函数(初始化了enable_shared_from_this中的_weak_this),此外使用了unique_ptr先保持资源,防止new时抛出异常,造成内存泄露

接下来可以看__enable_weak_this实现:

  • 实现使用了模板,当类继承了EnableSharedFromThis时,实例化第一个模板,初始化EnableSharedFromThis基类中的_weak_this
  • 当类未继承EnableSharedFromThis时,实例化第二个模板,什么都不做
cpp 复制代码
// 如果继承了EnableSharedFromThis 实例化这个
template <class _Yp, class _OrigPtr>
typename enable_if<is_convertible<_OrigPtr*, const EnableSharedFromThis<_Yp>*>::value,
                    void>::type
__enable_weak_this(const EnableSharedFromThis<_Yp>* __e, _OrigPtr* __ptr) noexcept {
    typedef typename remove_cv<_Yp>::type _RawYp;
    if (__e && __e->_weak_this.Expired()) {
        __e->_weak_this =
            SharedPtr<_RawYp>(*this, const_cast<_RawYp*>(static_cast<const _Yp*>(__ptr)));
    }
}

// 没继承EnableSharedFromThis 实例化这个
void __enable_weak_this(...) noexcept { cout << "---__enable_weak_this no op" << endl; }

参考资料

相关推荐
Dream it possible!5 小时前
LeetCode 热题 100_字符串解码(71_394_中等_C++)(栈)
c++·算法·leetcode
My Li.6 小时前
c++的介绍
开发语言·c++
邪恶的贝利亚8 小时前
C++之序列容器(vector,list,dueqe)
开发语言·c++
原来是猿8 小时前
蓝桥备赛(13)- 链表和 list(上)
开发语言·数据结构·c++·算法·链表·list
成功助力英语中国话8 小时前
SDK编程,MFC编程,WTL编程之间的关系
c++·mfc
仟濹8 小时前
【算法 C/C++】二维差分
c语言·c++·算法
总斯霖9 小时前
题解:士兵排列
数据结构·c++·算法
稳兽龙9 小时前
P4268 [USACO18FEB] Directory Traversal G
c++·算法·换根dp
放氮气的蜗牛9 小时前
C++从入门到精通系列教程之第十篇:异常处理与调试技巧
开发语言·jvm·c++
LL59621456910 小时前
CEF在MFC上的示例工程
c++·mfc·cef