C++11 标准库头文件模拟实现,无锁STL

3 篇文章 0 订阅

系列文章目录



前言

暂不考虑支持多线程
常用STL的简单实现,主要内容百行左右, 避免冗余代码


● 智能指针模板

SharedPtr

在这里插入图片描述

#include <assert.h>
#include <atomic>


template <class T>
class SharedPtr
{
public:
    typedef T element_type;

    explicit SharedPtr(T* ptr = nullptr) : m_ref(nullptr)
    {
        if (ptr)  m_ref = new reftype(ptr);
    }
    template <class Deleter>
    explicit SharedPtr(T* ptr, Deleter d) : m_ref(nullptr)
    {
        if (ptr) m_ref = new reftye_with_deleter<Deleter>(ptr, d);
    }
    SharedPtr(const SharedPtr& tocopy)
    {
        Acquire(tocopy.m_ref);
    }
    ~SharedPtr()
    {
        Release();
    }
    SharedPtr& operator=(const SharedPtr& tocopy)
    {
        if (this != &tocopy)
        {
            Release();
            Acquire(tocopy.m_ref);
        }
        return *this;
    }
    SharedPtr& operator=(T* ptr)
    {
        if (get() != ptr)
        {
            Release();
            if (ptr) m_ref = new reftype(ptr);
        }
        return *this;
    }
    T* get() const
    {
        return m_ref ? m_ref->m_ptr : nullptr;
    }
    void reset(T* ptr = nullptr)
    {
        Release();
        if (ptr) m_ref = new reftype(ptr);
    }
    template <class Deleter>
    void reset(T* ptr, Deleter d)
    {
        Release();
        if (ptr) m_ref = new reftye_with_deleter<Deleter>(ptr, d);
    }
    bool unique() const { return m_ref ? m_ref->m_count == 1 : true; }
    long use_count() const { return m_ref ? m_ref->m_count.load() : 0; }

    // test for pointer validity: defining conversion to unspecified_bool_type
    // and not more obvious bool to avoid implicit conversions to integer types
    typedef T*(SharedPtr<T>::*unspecified_bool_type)() const;
    operator unspecified_bool_type() const
    {
        if (m_ref && m_ref->m_ptr) return &SharedPtr<T>::get;
        else nullptr;
    }

    T& operator*() const
    {
        assert(m_ref && m_ref->m_ptr);
        return *( m_ref->m_ptr);
    }
    T* operator->() const
    {
        assert(m_ref && m_ref->m_ptr);
        return m_ref->m_ptr;
    }

private:
    struct reftype
    {
        reftype(T* ptr) : m_ptr(ptr), m_count(1) {}
        virtual ~reftype() {}
        virtual void delete_ptr() { delete m_ptr; }

        T*              m_ptr;
        std::atomic_int m_count;
    };
    template <class Deleter>
    struct reftye_with_deleter: public reftype
    {
        reftye_with_deleter(T* ptr, Deleter d) : reftype(ptr), m_deleter(d) {}
        virtual void delete_ptr() override
        {
            m_deleter(this->m_ptr);
        }
        Deleter m_deleter;
    };

    reftype* m_ref;

    void Acquire(reftype* ref)
    {
        m_ref = ref;
        if (ref) ref->m_count.fetch_add(1);
    }
    void Release()
    {
        if (m_ref)
        {
            if (! --m_ref->m_count )
            {
                m_ref->delete_ptr();
                delete m_ref;
            }
            m_ref = nullptr;
        }
    }
};

template <class T, class U>
bool operator==(const SharedPtr<T>& a, const SharedPtr<U>& b)
{
    return a.get() == b.get();
}
template <class T, class U>
bool operator!=(const SharedPtr<T>& a, const SharedPtr<U>& b)
{
    return a.get() != b.get();
}



//------------------------------------------------------------------------------
#include <iostream>
#include <stdio.h>
class TestSt
{
public:
    char buf[1024];
    char bux[4096];
public:
    TestSt() 
    { 
        printf("   const %p\n", this);
    };
    ~TestSt()
    {
        printf("~TestSt: %p\n", this);
    }
};

int main()
{
    std::atomic_int at;
    SharedPtr<TestSt> p_st(new TestSt());
    SharedPtr<TestSt> p_st2(p_st);

    SharedPtr<TestSt> p_st3(new TestSt());
    p_st3 = p_st2;
    std::cout << p_st.use_count() << " " << p_st2.use_count() << " " 
            << p_st3.use_count() << " " <<  std::endl;

}

● Vector

auto p = new T[N]; 申请空间,为每个元素p[i]调用T的构造函数
delete[] p; 先为每个元素调用析构函数,释放空间

1. 简单版本

在这里插入图片描述

new (m_values+i) T(val); 和 m_values[i] = val并不等价
当value_type包含虚函数时后者不能正确处理虚函数表指针,而前者却可以。
在这里插入图片描述
如图所示,new (m_values+i) T(val)可以正确处理虚表指针,而m_values[i] = val的_vptr.TestSt = 0x0

#include <stdlib.h>
#include <algorithm>

template <class T>
class Vector
{
private:
    T* m_values;
    size_t m_size, m_capacity;

public:
    typedef T value_type;
    typedef T* iterator;

    Vector(): m_values(nullptr), m_size(0), m_capacity(0) {}
    ~Vector()
    {
        for (size_t i = 0; i < m_size; i++)
        {
            m_values[i].~T();
        }
        free(m_values);
        m_values = nullptr;
        m_size = m_capacity = 0;
    }
    Vector(size_t n, const T& val): m_values(nullptr), m_size(n),m_capacity(n)
    {
        m_values =  (T*)malloc(sizeof(T) * m_capacity);
        for (size_t i = 0; i < n; i++)
        {
            new (m_values+i) T(val);
        }
        
    }
    Vector(const Vector<T>& tocopy)
    {
        m_values =  (T*)malloc(sizeof(T) * tocopy.m_capacity);
        m_size = tocopy.m_size;
        m_capacity = tocopy.m_capacity;
        for (size_t i = 0; i < m_size; i++)
        {
            new (m_values+i) T(tocopy.m_values[i]);
        }
    }
    Vector<T>& operator=(const Vector<T>& tocopy)
    {
        if (&tocopy == this) return *this;
        Vector<T> tmp(tocopy);
        std::swap(m_values, tmp.m_values);
        std::swap(m_size, tmp.m_size);
        std::swap(m_capacity, tmp.m_capacity);
        return *this;
    }
    value_type& operator[](size_t idx)
    {
        return m_values[idx];
    }
    void push_back(const T& val)
    {
        if (m_size + 1 > m_capacity)
        {
            m_capacity = m_capacity ? 2*m_capacity : 1;
            T* tmp = (T*)malloc(sizeof(T) * m_capacity);
            for (size_t i = 0; i < m_size; i++)
            {
                new (tmp+i) T(m_values[i]);
                m_values[i].~T();
            }
            free(m_values);
            m_values = tmp;
        }
        new (m_values+m_size) T(val);
        ++m_size;
    }
    void pop_back()
    {
        m_values[m_size].~T();
        // if ~T() _did_ throw, everything is OK,
        // but push_back Unable to handle throw
        --m_size;
    }
    void clear()
    {
        for (size_t i = 0; i < m_size; i++)
        {
            m_values[i].~T();
        }
        m_size = 0;
    }
    size_t size() { return m_size; }
    iterator begin()
    {
        return m_values;
    }
    iterator end()
    {
        return m_values + m_size;
    }
};

2. X

● 无锁栈

在这里插入图片描述

// lock_free_stack
template <class T, class Alloc=std::allocator<T>>
class lock_free_stack
{
private:
    struct Node
    {
        Node(const T& tocopy): m_data(tocopy), m_next(0) {}
        T m_data;
        Node* m_next = nullptr;
    };
    std::atomic<Node*> m_top;
    std::atomic_size_t m_size;
public:
    lock_free_stack(const lock_free_stack&) = delete;
    lock_free_stack& operator=(const lock_free_stack&) = delete;
    lock_free_stack(const lock_free_stack&&) = delete;
    lock_free_stack& operator=(const lock_free_stack&&) = delete;

    lock_free_stack()
    {
        m_top.store(nullptr);
        m_size.store(0);
    }
    T& top()
    {
        return m_top.load()->m_data	;
    }
    bool push(const T& tocopy)
    {
        Node* new_node = new (std::nothrow) Node(tocopy);
        if (!new_node) return false;
        new_node->m_next = m_top.load();
        while ( !m_top.compare_exchange_strong(new_node->m_next, new_node, std::memory_order_acq_rel) ) sched_yield();
        m_size.fetch_add(1);
        return true;
    }
    void pop()
    {
        // other threads spin
        Node* __always_top = nullptr;
        do
        {
            __always_top = m_top.load();
            if (!__always_top) return;
        } while ( !m_top.compare_exchange_strong(__always_top, __always_top->m_next, std::memory_order_acq_rel) );
        __always_top->m_next = nullptr;
        delete __always_top;
        m_size.fetch_sub(1);
    }
    size_t size()
    {
        return m_size.load();
    }
    ~lock_free_stack() = default;
};

● 一种高性能队列实现

在这里插入图片描述

//  yqueue is an efficient queue implementation. The main goal is
//  to minimise number of allocations/deallocations needed. Thus yqueue
//  allocates/deallocates elements in batches of N.
//
//  yqueue allows one thread to use push/back function and another one
//  to use pop/front functions. However, user must ensure that there's no
//  pop on the empty queue and that both threads don't access the same
//  element in unsynchronised manner.
//
//  T is the type of the object in the queue.
//  N is granularity of the queue (how many pushes have to be done till
//  actual memory allocation is required).
//
// ALIGN is the memory alignment size to use in the case where we have
// posix_memalign available. Default value is 64, this alignment will
// prevent two queue chunks from occupying the same CPU cache line on
// architectures where cache lines are <= 64 bytes (e.g. most things
// except POWER). It is detected at build time to try to account for other
// platforms like POWER and s390x.
template <typename T, int N, size_t ALIGN = 64> 
class yqueue_t
{
  public:
    inline yqueue_t ()
    {
        _begin_chunk = allocate_chunk ();
        alloc_assert (_begin_chunk);
        _begin_pos = 0;
        _back_chunk = NULL;
        _back_pos = 0;
        _end_chunk = _begin_chunk;
        _end_pos = 0;
    }
    inline ~yqueue_t ()
    {
        while (true) {
            if (_begin_chunk == _end_chunk) {
                free (_begin_chunk);
                break;
            }
            chunk_t *o = _begin_chunk;
            _begin_chunk = _begin_chunk->next;
            free (o);
        }
        chunk_t *sc = _spare_chunk.xchg (NULL);
        free (sc);
    }
    inline T &front () { return _begin_chunk->values[_begin_pos]; }
    inline T &back () { return _back_chunk->values[_back_pos]; }
    inline void push ()
    {
        _back_chunk = _end_chunk;
        _back_pos = _end_pos;

        if (++_end_pos != N)
            return;

        chunk_t *sc = _spare_chunk.xchg (NULL);
        if (sc) {
            _end_chunk->next = sc;
            sc->prev = _end_chunk;
        } else {
            _end_chunk->next = allocate_chunk ();
            alloc_assert (_end_chunk->next);
            _end_chunk->next->prev = _end_chunk;
        }
        _end_chunk = _end_chunk->next;
        _end_pos = 0;
    }

    //  Removes element from the back end of the queue. In other words
    //  it rollbacks last push to the queue. Take care: Caller is
    //  responsible for destroying the object being unpushed.
    //  The caller must also guarantee that the queue isn't empty when
    //  unpush is called. It cannot be done automatically as the read
    //  side of the queue can be managed by different, completely
    //  unsynchronised thread.
    inline void unpush ()
    {
        if (_back_pos)
            --_back_pos;
        else {
            _back_pos = N - 1;
            _back_chunk = _back_chunk->prev;
        }
        if (_end_pos)
            --_end_pos;
        else {
            _end_pos = N - 1;
            _end_chunk = _end_chunk->prev;
            free (_end_chunk->next);
            _end_chunk->next = NULL;
        }
    }
    inline void pop ()
    {
        if (++_begin_pos == N) {
            chunk_t *o = _begin_chunk;
            _begin_chunk = _begin_chunk->next;
            _begin_chunk->prev = NULL;
            _begin_pos = 0;
            chunk_t *cs = _spare_chunk.xchg (o);
            free (cs);
        }
    }

  private:
    struct chunk_t
    {
        T values[N];
        chunk_t *prev;
        chunk_t *next;
    };
    static inline chunk_t *allocate_chunk ()
    {
        void *pv;
        if (posix_memalign (&pv, ALIGN, sizeof (chunk_t)) == 0)
            return (chunk_t *) pv;
        return NULL;
    }

    //  Back position may point to invalid memory if the queue is empty,
    //  while begin & end positions are always valid. Begin position is
    //  accessed exclusively be queue reader (front/pop), while back and
    //  end positions are accessed exclusively by queue writer (back/push).
    chunk_t *_begin_chunk;
    int _begin_pos;
    chunk_t *_back_chunk;
    int _back_pos;
    chunk_t *_end_chunk;
    int _end_pos;

    //  People are likely to produce and consume at similar rates.  In
    //  this scenario holding onto the most recently freed chunk saves
    //  us from having to call malloc/free.
    atomic_ptr_t<chunk_t> _spare_chunk;

    yqueue_t (const yqueue_t &) = delete;
    yqueue_t &operator= (const yqueue_t &) = delete;
    yqueue_t (yqueue_t &&) = delete;
    yqueue_t &operator= (yqueue_t &&) = delete;
};

● 无锁队列


总结

  • 15
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值