山寨shared_ptr实现

C++ May 02, 2020

Counter计数器

  1. counter里面需要引用计数 一个strong_ptr 一个weak_ptr
  2. counter里面需要deleter用于擦没有virtual destructor类的屁股
// count.h

#pragma once

class DeleterBase {
public:
    virtual void destroy() = 0; // 擦屁股方法
};

template<class P>
class Deleter : public DeleterBase {
public:
    Deleter(P* p) : original_p(p) {}

    virtual void destroy() final {
        delete original_p;
    }

    P* original_p; // 原理就是记录原始指针的类型
};

// 下面就是完整的counter
class Count {
public:
    template<class Original>
    Count(Original* p) : strong_count(1), weak_count(0) {
        this->deleter_base = new Deleter<Original>(p);
    }
    std::atomic<uint64_t> strong_count;
    std::atomic<uint64_t> weak_count;
    DeleterBase* deleter_base;
};

SharePtr实现

#include "count.h"

template<class T>
class SharePtr {
public:
    // 默认构造
    SharePtr() : p(nullptr), cb(nullptr) {

    }

    // 裸指针构造
    SharePtr(T* t) : p(t) {  
        this->cb = new Count(t);
        this->p = t;
    }
    
    // 同类型复制构造 需要增加计数
    // 这个是必须的 否则编译器会生成默认的复制构造函数 导致不会增加计数
    SharePtr(const SharePtr<T>& other)
    {
        if (other) {
            this->cb = other.cb;
            this->p = other.p;
            this->cb->strong_count++;
        }else {
            ::new(this)(SharePtr);
        }
    }
    
    // 非同类型复制构造 需要增加计数 只能子类构造基类指针
    // 如果other是空指针 我们也构造空指针
    template<class Other>
    SharePtr(const SharePtr<Other>& other)
    {
        if (other) {
            this->cb = other.cb;
            this->p = other.p;
            this->cb->strong_count++;
        }else {
            ::new(this)(SharePtr);
        }
    }
    
    // reset操作
    // 计数减1 如果强引用计数减到0了 那就析构
    // 如果强引用计数减到0了且弱计数也为0 删除cb
    void reset() {
        if (cb) {
            --cb->strong_count;
            if (cb->strong_count == 0) {
                cb->deleter_base->destroy();
                if (cb->weak_count == 1) {
                    delete cb;
                }else {
                 	// 如果有其他wptr拿着指针 但是cb->strong_count == 0了
                    // 那么他们负责析构cb
                    cb->weak_count--;
                }
            }
            this->cb = nullptr;
            this->p = nullptr;
        }
    }
     
    // 同类型赋值重载 需要增加计数
    SharePtr& operator=(const SharePtr<T>& other) {
        this->reset();
        this->p = other.p;
        this->cb = other.cb;
        this->cb->strong_count++;
        return *this;
    }
    
    // 非同类型赋值重载 需要增加计数 只能子类构造基类指针
    template<class Other>
    SharePtr& operator=(const SharePtr<Other> &other)
    {
        this->reset();
        this->p = other.p;
        this->cb = other.cb;
        this->cb->strong_count++;
        return *this;
    }
    
    /////////   以下是基本操作   ////////
    
    ~SharePtr() {
        reset();
    }

    T* operator->() {
        return this->p;
    }

    T& operator*() {
        return *this->p;
    }

    // 方便 if (xxx) 
    explicit operator bool() const
    {
        return this->cb && this->cb->strong_count > 0;
    }
    
    uint64_t use_count() {
        return this->cb->strong_count;
    }

    T* p;
    Count* cb;
};

WeakPtr实现

考虑到循环引用 我们需要加上weak_ptr

#include "count.h"

template<class T>
class WeakPtr {
public:
    WeakPtr() : p(nullptr), cb(nullptr) {

    }

    // 由SharePtr构造WeakPtr
    WeakPtr(const SharePtr<T>& t) {
        if(t) {
            this->p = t.p;
            this->cb = t.cb;
            this->cb->weak_count++;
        }else {
            ::new(this)(WeakPtr);
        }
    }

    ~WeakPtr() {
        release();
    }

    void release() {
        if (cb) {
            --cb->weak_count;
            if (cb->weak_count == 0) {
                delete cb;
            }
            this->p = nullptr;
            this->cb = nullptr;
        }
    }

    SharePtr<T> lock() {
        if (!cb || cb->strong_count == 0) {
            return SharePtr<T>();
        }
        return SharePtr<T>(*this);
    }

    friend class SharePtr<T>; // SharePtr<T>需要访问本类的私有成员

private:
    T* p;
    Count* cb;
};

我们需要往SharedPtr里面添加对于WeakPtr的构造函数

// 先来个前项声明
template<class T> class WeakPtr;
template<class T>
class SharePtr 
/// .....

// 为了用weak_ptr的lock(),来生成share_ptr用,需要拷贝构造用
// other有可能是0 strong_count的引用 这里就懒得加判断了
SharePtr(WeakPtr<T>& other) 
{
	this->p = other.p;
	this->cb = other.cb;
	this->cb->strong_count++;
}

到此 wptr就完成了

EnableShareFromThis实现

enable_shared_from_this是为了解决在类里面拿自己的shared_ptr的问题

template<class T>
class EnableShareFromThis {
public:
    EnableShareFromThis() { }

    friend class SharePtr<T>;

    SharePtr<T> share_from_this() {
        return this->weak_ptr.lock();
    }

private:
    WeakPtr<T> weak_ptr;
};

初始化问题:

auto p = SharePtr<CLASSNAME>();

SharePtr的构造函数需要构造EnableShareFromThis类

但是并不是所有的CLASSNAME类都继承了EnableShareFromThis类

还是用type_traits解决问题

// 首先加上前向声明
template<class T> class EnableShareFromThis;
template<class T>
class SharePtr
// .....

// 简单地修改一下构造函数即可
SharePtr(T* t) : p(t) {
        this->cb = new Count(t);
        this->p = t;
        if (std::is_base_of<EnableShareFromThis<T>, T>::value) {
            t->weak_ptr.p = t;
            t->weak_ptr.cb = this->cb;
            t->weak_ptr.cb->weak_count++;
        }
    }

完整代码

#include <type_traits>

template<class T> class WeakPtr;
template<class T> class EnableShareFromThis;
template<class T>
class SharePtr {
public:
    SharePtr() : p(nullptr), cb(nullptr) {

    }

    SharePtr(T* t) : p(t) {
        this->cb = new Count(t);
        this->p = t;
        if (std::is_base_of<EnableShareFromThis<T>, T>::value) {
            t->weak_ptr.p = t;
            t->weak_ptr.cb = this->cb;
            t->weak_ptr.cb->weak_count++;
        }
    }

    // 同类型复制构造 需要增加计数
    // 这个是必须的 否则编译器会生成默认的复制构造函数 导致不会增加计数
    SharePtr(const SharePtr<T>& other)
    {
        if (other) {
            this->cb = other.cb;
            this->p = other.p;
            this->cb->strong_count++;
        }else {
            ::new(this)(SharePtr);
        }
    }

    // 非同类型复制构造 需要增加计数 只能子类构造基类指针
    // 如果other是空指针 我们也构造空指针
    template<class Other>
    SharePtr(const SharePtr<Other>& other)
    {
        if (other) {
            this->cb = other.cb;
            this->p = other.p;
            this->cb->strong_count++;
        }else {
            ::new(this)(SharePtr);
        }
    }

    // reset操作
    // 计数减1 如果强引用计数减到0了 那就析构
    // 如果强引用计数减到0了且弱计数也为0 删除cb
    void reset() {
        if (cb) {
            --cb->strong_count;
            if (cb->strong_count == 0) {
                cb->deleter_base->destroy();
                if (cb->weak_count == 1) {
                    delete cb;
                }else {
                 	// 如果有其他wptr拿着指针 但是cb->strong_count == 0了
                    // 那么他们负责析构cb
                    cb->weak_count--;
                }
            }
            this->cb = nullptr;
            this->p = nullptr;
        }
    }

    // 同类型赋值重载 需要增加计数
    SharePtr& operator=(const SharePtr<T>& other) {
        this->reset();
        this->p = other.p;
        this->cb = other.cb;
        this->cb->strong_count++;
        return *this;
    }

    // 非同类型赋值重载 需要增加计数 只能子类构造基类指针
    template<class Other>
    SharePtr& operator=(const SharePtr<Other> &other)
    {
        this->reset();
        this->p = other.p;
        this->cb = other.cb;
        this->cb->strong_count++;
        return *this;
    }

    // WeakPtr构造
    SharePtr(WeakPtr<T>& other)
    {
        this->p = other.p;
        this->cb = other.cb;
        this->cb->strong_count++;
    }

    ~SharePtr() {
        reset();
    }

    explicit operator bool() const
    {
        return this->cb && this->cb->strong_count > 0;
    }

    T* operator->() {
        return this->p;
    }

    T& operator*() {
        return *this->p;
    }

    uint64_t use_count() {
        return this->cb->strong_count;
    }

    T* p;
    Count* cb;
};

template<class T>
class EnableShareFromThis {
public:
    EnableShareFromThis() { }

    friend class SharePtr<T>;

    SharePtr<T> share_from_this() {
        return this->weak_ptr.lock();
    }

private:
    WeakPtr<T> weak_ptr;
};

template<class T>
class WeakPtr {
public:
    WeakPtr() : p(nullptr), cb(nullptr) {

    }

    ~WeakPtr() {
        release();
    }

    WeakPtr(const SharePtr<T>& t) {
        if(t) {
            this->p = t.p;
            this->cb = t.cb;
            this->cb->weak_count++;
        }else {
            ::new(this)(WeakPtr);
        }
    }

    SharePtr<T> lock() {
        if (!cb || cb->strong_count == 0) {
            return SharePtr<T>();
        }
        return SharePtr<T>(*this);
    }

    void release() {
        if (cb) {
            --cb->weak_count;
            if (cb->weak_count == 0) {
                delete cb;
            }
            this->p = nullptr;
            this->cb = nullptr;
        }
    }

    friend class SharePtr<T>;

private:
    T* p;
    Count* cb;
};

测试一下

class Base {
public:
    ~Base(){
        printf("base die\n");
    }
};

class Derive : public Base , public EnableShareFromThis<Derive> {
public:
    ~Derive() {
        printf("Derive die\n");
    }
};

int main() {

    {
        auto p1 = SharePtr<Derive>(new Derive());

        {
            SharePtr<Derive> p2(p1); // 复制构造
            SharePtr<Derive> p3;
            p3 = p1; // 复制 operator=重载
        }

        {
            SharePtr<Base> pb(p1); // 复制构造 + 转型
            SharePtr<Base> pb2;
            pb2 = p1; // 复制 operator=重载 + 转型
        }

        {
            WeakPtr<Base> wb(p1);
            WeakPtr<Base> wb2;
            // wb2 = p1; // 我们还没写这个 懒得写了
        }

        {
            auto p = p1->share_from_this(); // share_from_this
        }

        /*
        {
            SharePtr<Base> bp;
            bp = p1;
            p1.reset(); // reset Derive指针
            // reset Base指针
            // 引用降为0 擦除原指针 Derive正常析构 导致EnableShareThis析构 导致WeakPtr析构
            // 引用变为 strong_count: 0 weak_count: 1
            // 最后析构 cb
            bp.reset();
        }
        */

        {
            WeakPtr<Derive> wd(p1);
            p1.reset(); // reset Derive指针
            // 现在strong_count == 0 weak_count == 1
            // 退栈析构wd ---> 析构 cb
        }

    }

    return 0;
}
Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.