C++智能指针实现

上一篇介绍内存池的文章中提到一个基于内存池的智能指针。C++没有GC机制,需要程序员自己管理内存,而智能指针则是C++程序员管理内存的利器。智能指针的原理早已广为人知,通俗来讲就是用类来表示指针(用类来表示对象是OOP思想的核心),成员函数里构建一个指向对象的指针,在构造函数,赋值函数,复制构造函数,析构函数等会改变对象个数的地方增加或减少引用计数,当引用计数为0时,释放指针指向的内存,从而避免了程序员手动释放,减少了内存泄漏和野指针的危险。C++11引入了智能指针和弱指针,极大的方便了编码。何为弱指针?弱指针的对象持有指向这块内存的指针,却不增加引用计数。为什么要添加这个机制呢?因为智能指针看起来完美无缺,却有一个致命的缺陷:

class A;
class B {
public:
    std::shared_ptr<A> _a_ptr;
    ~B() { std::cout << "BBB" << std::endl; }
};

class A {
public:
    std::shared_ptr<B> _b_ptr;
    ~A() { std::cout << "AAA" << std::endl; }
};

int main() {
    {
        std::shared_ptr<A> a(new A);
        std::shared_ptr<B> b(new B);
        a->_b_ptr = b;
        b->_a_ptr = a;
    }
    int a = 0;
    a++;
}

当智能指针循环引用的时候,内存没有得到释放!!这就是弱指针上场的机遇。

class A {
public:
    std::weak_ptr<B> _b_ptr;
    ~A() { std::cout << "AAA" << std::endl; }
};

我们只要将其中一个类的指针类型改为弱指针,就能够有效的释放占有的内存。不过弱指针使用时需要转换为智能指针使用。
我在这里借鉴C++11智能指针的实现,实现了基于内存池的智能指针和弱指针,从而避免了再手动释放内存给内存池的步骤。这里我们要实现的功能是将从内存池中获取到的内存交给一个类的对象来管理,这个对象保持一个关于这个内存的引用者的引用计数,当计数为零时将这块内存还给内存池。
首先我们定义一个管理引用计数的类,把引用计数和指针管理的类分开来存放,这个概念在《C++沉思录》中有很详细的讲解(这本书把面向对象编程讲解的极其细致,很多东西使人有醍醐灌顶之感),这个类只负责维护两个引用的计数,智能指针计数和弱指针计数,为了支持多线程,我们将这两个成员变量定义为原子类型。C++11引入了原子类型的定义,何为原子类型? 就是对其进行加减乘除等操作时都是线程安全的,下面是这个类的定义及其实现:

//reference count class
class CRefCount {
public:
    // construct
    CRefCount() : _uses(1), _weaks(0) {}

    // ensure that derived classes can be destroyed properly
    virtual ~CRefCount() noexcept {}

    // increment use count
    void IncrefUse() {  
        _uses++;
    }

    // increment weak reference count
    void IncrefWeak() {
        _weaks++;
    }

    //decrement use count
    bool DecrefUse() {  
        if (--_uses == 0) { 
            return true;
        }
        return false;
    }

    // decrement weak reference count
    bool DecrefWeak() {
        if (--_weaks == 0) {
            return true;
        }
        return false;
    }

    // return use count
    long GetUseCount() const {  
        return _uses;
    }

    // return true if _uses == 0
    bool Expired() const  { 
        return (_uses == 0);
    }

private:
    std::atomic_long _uses;
    std::atomic_long _weaks;
};

下面我们实现一个指针的基类,实现了两种指针所共有的操作:

// base class for CMemSharePtr and CMemWeakPtr
template<typename T>
class CBasePtr {
public:
    typedef CBasePtr<T>                 _BasePtr;
    typedef std::function<void(T*&)>    _PtrDeleter;
    typedef std::function<void(CRefCount*&)>    _RefDeleter;
    typedef std::pair<_PtrDeleter, _RefDeleter> _Deleter;

    // construct
    CBasePtr() noexcept : _ptr(0), _ref_count(0), _deleter(std::make_pair(nullptr, nullptr)){}
    CBasePtr(T* ptr, CRefCount* ref) noexcept : _ptr(ptr), _ref_count(ref), _deleter(std::make_pair(nullptr, nullptr)) {}
    CBasePtr(T* ptr, CRefCount* ref, _Deleter& func) noexcept : _ptr(ptr), _ref_count(ref), _deleter(func) {}


    // construct CBasePtr object that takes resource from _Right
    CBasePtr(const _BasePtr& r) : _ptr(r._ptr), _ref_count(r._ref_count), _deleter(r._deleter) {
        if (_ref_count) {
            _ref_count->IncrefUse();
        }
    }

    // construct CBasePtr object that takes resource from _Right
    CBasePtr(_BasePtr&& r) : _ptr(r._ptr), _ref_count(r._ref_count), _deleter(r._deleter) {
        r._ptr          = nullptr;
        r._ref_count    = nullptr;
        r._deleter      = std::make_pair(nullptr, nullptr);
    }

    // construct CBasePtr object that takes resource from _Right
    _BasePtr& operator=(_BasePtr&& r) {
        _ptr            = r._ptr;
        _ref_count      = r._ref_count;
        _deleter        = r._deleter;

        r._ptr          = nullptr;
        r._ref_count    = nullptr;
        r._deleter      = std::make_pair(nullptr, nullptr);
        return (*this);
    }

    // construct CBasePtr object that takes resource from _Right
    _BasePtr& operator=(const _BasePtr& r) {
        _ptr         = r._ptr;
        _ref_count   = r._ref_count;
        _deleter     = r._deleter;
        if (_ref_count) {
            _ref_count->IncrefUse();
        }
        return (*this);
    }

    // return use count
    long UseCount() const noexcept {    
        return (_ref_count ? _ref_count->GetUseCount() : 0);
    }

    // return pointer to resource
    T* Get() const noexcept {   
        return (_ptr);
    }

    // test if expired
    bool Expired() const noexcept { 
        return (!_ref_count || _ref_count->Expired());
    }

    // release resource
    void Reset() {  
        Reset(0, 0);
    }

    // release resource and take ownership from CMemWeakPtr _Other._Ptr
    void Reset(const _BasePtr& other) {
        Reset(other._ptr, other._ref_count, other._deleter);
    }

    // release resource and take _Other_ptr through _Other_rep
    void Reset(T *other_ptr, CRefCount * other_rep, _Deleter& deleter) {    
        if (other_rep)
            other_rep->IncrefUse();
        _Reset0(other_ptr, other_rep, deleter);
    }

    // release weak reference to resource
    void Resetw() {
        _Resetw(0, 0);
    }

    // release weak reference to resource and take _Other._Ptr
    void Resetw(_BasePtr& other) {
        Resetw(other._ptr, other._ref_count, other._deleter);
    }

    void Resetw(T *other_ptr, CRefCount *other_rep, _Deleter& func) {
        if (other_rep)
            other_rep->IncrefWeak();
        _Resetw0(other_ptr, other_rep, func);
    }

protected:
    // release resource and take _Other_ptr through _Other_rep
    void Reset(T *other_ptr, CRefCount * other_rep) {
        if (other_rep)
            other_rep->IncrefUse();
        _Reset0(other_ptr, other_rep);
    }

    // release resource and take new resource
    void _Reset0(T *other_ptr, CRefCount *other_rep) {
        _DecrefUse();

        _ref_count = other_rep;
        _ptr       = other_ptr;
        _deleter   = std::make_pair(nullptr, nullptr);
    }

    // release resource and take new resource
    void _Reset0(T *other_ptr, CRefCount *other_rep, _Deleter& func) {
        _DecrefUse();

        _ref_count  = other_rep;
        _ptr        = other_ptr;
        _deleter    = std::make_pair(nullptr, nullptr);
    }

    // decrement use reference count
    void _DecrefUse() {
        if (_ref_count && _ref_count->DecrefUse()) {
            _Destroy();
        }
    }

    // decrement use reference count
    void _DecrefWeak() {
        if (_ref_count && _ref_count->DecrefWeak()) {
            _DestroyThis();
        }
    }

    // point to _Other_ptr through _Other_rep
    void _Resetw(T *other_ptr, CRefCount *other_rep) {  
        if (other_rep)
            other_rep->IncrefWeak();
        _Resetw0(other_ptr, other_rep);
    }

    // release resource and take new resource
    void _Resetw0(T *other_ptr, CRefCount *other_rep) {
        _DecrefWeak();

        _ref_count  = other_rep;
        _ptr        = other_ptr;
        _deleter    = std::make_pair(nullptr, nullptr);
    }

    // release resource and take new resource
    void _Resetw0(T *other_ptr, CRefCount *other_rep, _Deleter& func) {
        _DecrefWeak();

        _ref_count  = other_rep;
        _ptr        = other_ptr;
        _deleter    = func;
    }

    //release resource
    virtual void _Destroy() noexcept {
        if (_deleter.first) {
            _deleter.first(_ptr);
        }
        if (_deleter.second) {
            _deleter.second(_ref_count);
        }
    }

    virtual void _DestroyThis() noexcept {

    }

    virtual ~CBasePtr() {}

protected:
    T           *_ptr;
    CRefCount   *_ref_count;
    std::pair<_PtrDeleter, _RefDeleter> _deleter;
};

_PtrDeleter 和 _RefDeleter 是两个函数对象,当引用计数为0时智能指针调用他们释放从内存池中申请的内存,一个负责释放上层应用所使用的内存,一个释放引用计数类指针占有的内存。之后是两种指针的实现代码:

// class for reference counted resource management
template<class T>
class CMemSharePtr : public CBasePtr<T> {   
public:
    // construct
    CMemSharePtr() noexcept : CBasePtr() {}
    CMemSharePtr(T* ptr, CRefCount* ref) noexcept : CBasePtr(ptr, ref) {}
    CMemSharePtr(T* ptr, CRefCount* ref, _Deleter& func) noexcept : CBasePtr(ptr, ref, func) {}

    CMemSharePtr(const _BasePtr& r) : CBasePtr(r) {}
    CMemSharePtr(_BasePtr&& r) : CBasePtr(r) {}

    CMemSharePtr& operator=(_BasePtr&& r) {
        _BasePtr::operator=(r);
        return (*this);
    }

    CMemSharePtr& operator=(const _BasePtr& r) {
        _BasePtr::operator=(r);
        return (*this);
    }

    ~CMemSharePtr() {  
        this->_DecrefUse();
    }

    _BasePtr& operator==(const _BasePtr& r) noexcept {
        return _ptr == r._ptr;
    }

    // return pointer to resource
    T *operator->() const noexcept {
        return (this->Get());
    }

    template<typename T2>
    T2 *operator->() const noexcept {
        return dynamic_cast<T2*>(this->Get());
    }

    // return pointer to resource
    T operator*() const noexcept {
        return (*(this->Get()));
    }

    // return true if no other CMemSharePtr object owns this resource
    bool unique() const noexcept {
        return (this->UseCount() == 1);
    }

    // test if CMemSharePtr object owns no resource
    explicit operator bool() const noexcept {
        return (this->Get() != 0);
    }
};

// class for pointer to reference counted resource.
// construc from CMemSharePtr
template<class T>
class CMemWeakPtr : public CBasePtr<T> {
public:
    CMemWeakPtr() {
        Resetw();
    }

    CMemWeakPtr(_BasePtr& r) {
        Resetw(r);
    }

    // construct CBasePtr object that takes resource from _Right
    CMemWeakPtr& operator=(_BasePtr&& r) {
        _BasePtr::operator=(r);
        return (*this);
    }

    // construct CBasePtr object that takes resource from _Right
    CMemWeakPtr& operator=(_BasePtr& r) {
        Resetw(r);
        return (*this);
    }

    // release resource
    ~CMemWeakPtr() noexcept {
        this->_DecrefWeak();
    }

    // convert to CMemSharePtr
    CMemSharePtr<T> Lock() const noexcept {
        if (Expired()) {
            return CMemWeakPtr();
        }
        return (CMemSharePtr<T>(*this));
    }
};

我们重载了->操作符和*操作符,来使指针对象使用起来就像是裸指针一样,这正是重载操作符的魅力所在。这样一来客户创建指针的时候,还需要手动的将回收函数复制给指针,不然就不能归还内存。干脆好人做到底,送佛送到西,再封装两个函数来负责构造智能指针:

template<typename T, typename... Args >
CMemSharePtr<T> MakeNewSharedPtr(CMemaryPool& pool, Args&&... args) {
    T* o = pool.PoolNew<T>(std::forward<Args>(args)...);
    CRefCount* ref = pool.PoolNew<CRefCount>();
    std::pair<std::function<void(T*&)>, std::function<void(CRefCount*&)>> del = std::make_pair(std::bind(&CMemaryPool::PoolDelete<T>, &pool, std::placeholders::_1), std::bind(&CMemaryPool::PoolDelete<CRefCount>, &pool, std::placeholders::_1));
    return CMemSharePtr<T>(o, ref, del);
}

template<typename T>
CMemSharePtr<T> MakeMallocSharedPtr(CMemaryPool& pool, int size) {
    T* o = (T*)pool.PoolMalloc<T>(size);
    CRefCount* ref = pool.PoolNew<CRefCount>();
    std::pair<std::function<void(T*&)>, std::function<void(CRefCount*&)>> del = std::make_pair(std::bind(&CMemaryPool::PoolFree<T>, &pool, std::placeholders::_1, size), std::bind(&CMemaryPool::PoolDelete<CRefCount>, &pool, std::placeholders::_1));
    return CMemSharePtr<T>(o, ref, del);
}

template<typename T>
CMemSharePtr<T> MakeLargeSharedPtr(CMemaryPool& pool) {
    T* o = pool.PoolLargeMalloc<T>();
    CRefCount* ref = pool.PoolNew<CRefCount>();
    std::pair<std::function<void(T*&)>, std::function<void(CRefCount*&)>> del = std::make_pair(std::bind(&CMemaryPool::PoolLargeFree<T>, &pool, std::placeholders::_1), std::bind(&CMemaryPool::PoolDelete<CRefCount>, &pool, std::placeholders::_1));
    return CMemSharePtr<T>(o, ref, del);
}

template<typename T>
CMemSharePtr<T> MakeLargeSharedPtr(CMemaryPool& pool, int size) {
    T* o = pool.PoolLargeMalloc<T>(size);
    CRefCount* ref = pool.PoolNew<CRefCount>();
    std::pair<std::function<void(T*&)>, std::function<void(CRefCount*&)>> del = std::make_pair(std::bind(&CMemaryPool::PoolLargeFree<T>, &pool, std::placeholders::_1, size), std::bind(&CMemaryPool::PoolDelete<CRefCount>, &pool, std::placeholders::_1));
    return CMemSharePtr<T>(o, ref, del);
}

到这里便可以大功告成,配合我们之前所说的内存池,让上层业务既高效的使用了内存,又免去了内存释放的担忧,可谓多快好省乎?
下边是测试使用代码:

#include "PoolSharedPtr.h"
int main() {
    CMemaryPool pool(1024, 10);
    auto ptr = MakeNewSharedPtr<test1>(pool, 1, 2, 3, 4);
    ptr->aaaa = 100;

    {
        auto weak = CMemWeakPtr<test1>(ptr);
        auto ptrtr = weak.Lock();
    }

    auto ptr2 = MakeMallocSharedPtr<char>(pool, 55);
    strcpy(*ptr2, "100000");

    auto ptr3 = MakeLargeSharedPtr<char>(pool);
    strcpy(*ptr3, "100000");

    CMemWeakPtr<char> ptr5;
    {
        auto ptr4 = MakeMallocSharedPtr<char>(pool, 55);
        {
            CMemSharePtr<char> ptr2(ptr4);
        }
        {
            CMemSharePtr<char> ptr2 = ptr4;
        }

        {
            ptr5 = ptr4;
        }
    }

    auto ptr6 = CMemSharePtr<char>();
    if (ptr6) {
        int a = 0;
        a++;
    }

    int a = 0;
    a++;
}

Update:
1,使用测试的时候发现std::function复制的时候非常占用CPU,每次创建智能指针,传递智能指针的时候都会引起复制。函数对象本质上是一个实现了operator()的类对象,每个智能指针都持有一份对象实属不智。所以我在这里修改了一下,将创建智能指针时所使用的内存池对象指针保存在智能指针中,以免不必要的复制引起性能损耗。
2,为了支持shared_from_this,模仿boost新建了一个名称为CEnableSharedFromThis的类,在这个类中声明一个CMemWeakPtr成员变量,使继承了CEnableSharedFromThis的的类可以通过this指针建立智能指针。

template<class T>
class CEnableSharedFromThis {
public:
    typedef T _EStype;
    CMemSharePtr<T> memshared_from_this() {
        return (_weak_ptr.Lock());
    }

protected:
    constexpr CEnableSharedFromThis() noexcept {
    }

    CEnableSharedFromThis(const CEnableSharedFromThis&) noexcept {
    }

    CEnableSharedFromThis& operator=(const CEnableSharedFromThis&) noexcept {
        return (*this);
    }

    ~CEnableSharedFromThis() noexcept {
    }

private:
    template<class T1, class T2>
    friend void DoEnable(T1 *ptr, CEnableSharedFromThis<T2> *es, CRefCount *ref_ptr, CMemaryPool* pool = 0, int size = 0, MemoryType type = TYPE_NEW);
    CMemWeakPtr<T> _weak_ptr;
};

在智能指针的构造函数中为_weak_ptr赋值,但是如何才能区分到底到底有没有_weak_ptr呢(类有没有继承CEnableSharedFromThis)?这里用到C++11的一个新特性,可以根据编译期信息推断类是否有特定的成员变脸或成员函数(go的低配版反射)

template<typename T>
struct has_member_weak_ptr {
    template <typename _T>
    static auto check(_T)->typename std::decay<decltype(_T::_weak_ptr)>::type;
    static void check(...);
    using type = decltype(check(std::declval<T>()));
    enum { value = !std::is_void<type>::value };
};

template<class T>
inline void EnableShared(T *ptr, CRefCount *ref_ptr, CMemaryPool* pool, int size, MemoryType type) {
    if (ptr) {
        if (has_member_weak_ptr<T>::value > 0) {
            DoEnable(ptr, (CEnableSharedFromThis<T>*)ptr, ref_ptr, pool, size, type);
        }
    }
}

在智能指针的构造函数中调用EnableShared即可。
3,添加了线程锁
最后是源码地址:
GitHub:https://github.com/caozhiyi/Base

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值