手撕代码

1、阻塞队列/线程安全队列

实现:

注意问题:

  1、防止虚假唤醒的lambda表达式需要传入this,也就是调用变量

  2、lambda表达式的函数体不可以使用自己写的判断队列满或者空函数

template <typename T>
class block_queue
{
public:
    block_queue(int max_size = 1000)
    {
        if (max_size <= 0)
        {
            exit(-1);
        }

        m_max_size = max_size;
        m_size = 0;
        m_array = new T[m_max_size];
        m_front = -1;;
        m_back = -1;
    }
    block_queue(const block_queue&) = delete;

    ~block_queue()
    {
        std::unique_lock<std::mutex> ulk(m_mutex);
        if (m_array != NULL)
            delete[] m_array;
    }

    /*
        往队列添加元素,需要将所有使用队列的线程先唤醒
        当有元素push进队列,相当于生产者生产了一个元素
        若当前没有线程等待条件变量,则唤醒无意义
    */
    bool push(const T &item)
    {
        std::unique_lock<std::mutex> ulk(m_mutex);

        /*
        while (m_size >= m_max_size)
            m_cond.wait(ulk);
        */

        m_cond.wait(ulk, [this]() {
            return m_size < m_max_size;
        });

        //将新增数据放在循环数组的对应位置
        m_back = (m_back + 1) % m_max_size;
        m_array[m_back] = item;
        m_size++;

        m_cond.notify_all();
        return true;
    }
    //pop时,如果当前队列没有元素,将会等待条件变量
    bool pop(T &item)
    {
        std::unique_lock<std::mutex> ulk(m_mutex);

        /*
        //多个消费者的时候,这里要是用while而不是if
        while (m_size <= 0)
            m_cond.wait(guard);
        */
        m_cond.wait(ulk, [this]() {
            return m_size > 0;
        });

        //取出队列首的元素,这里需要理解一下,使用循环数组模拟的队列
        m_front = (m_front + 1) % m_max_size;
        item = m_array[m_front];
        m_size--;
        m_cond.notify_all();
        return true;
    }

    //返回队首元素
    bool front(T &value)
    {
        std::unique_lock<std::mutex> ulk(m_mutex);
        if (0 == m_size)
        {
            //ulk.unlock();
            return false;
        }
        value = m_array[m_front];
        m_mutex.unlock();
        return true;
    }

    //返回队尾元素
    bool back(T &value)
    {
        std::unique_lock<std::mutex> ulk(m_mutex);
        if (0 == m_size)
        {
            //ulk.unlock();
            return false;
        }
        value = m_array[m_back];
        return true;
    }


    int size()
    {
        int tmp = 0;
        std::unique_lock<std::mutex> ulk(m_mutex);
        tmp = m_size;
        return tmp;
    }

    int max_size()
    {
        int tmp = 0;
        std::unique_lock<std::mutex> ulk(m_mutex);
        tmp = m_max_size;
        return tmp;
    }

    //判断队列是否满了
    bool full()
    {
        std::unique_lock<std::mutex> ulk(m_mutex);
        if (m_size >= m_max_size)
        {
            return true;
        }
        return false;
    }
    //判断队列是否为空
    bool empty()
    {
        std::unique_lock<std::mutex> ulk(m_mutex);
        if (0 == m_size)
        {
            return true;
        }
        return false;
    }

private:
    std::mutex m_mutex;
    std::condition_variable m_cond;

    int m_max_size;
    int m_size;
    T* m_array;
    int m_front;
    int m_back;
};
View Code

 2、unique_ptr智能指针

  C++11中又引入了unique_ptr,他的实现思路非常简单粗暴,就是防拷贝,既然多个智能指针指向同一资源会导致问题那就干脆不让你这样做,这样问题自然也就解决了。C++11中也是非常推荐使用这种智能指针,因为其比起shared_ptr和auto_ptr来说较为稳定,不会导致严重的错误但是其也存在缺陷,就是在需要拷贝的场景下他没有办法使用。

实现:

  1、禁止拷贝构造

  2、进制复制构造

  3、可以实现移动语义

 1 template<typename T>
 2 class My_unique_ptr
 3 {
 4 private:
 5     T* _ptr;
 6 public:
 7     My_unique_ptr(const T* ptr) : _ptr(ptr) {}
 8     My_unique_ptr(const My_unique_ptr<T>&) = delete;
 9     My_unique_ptr& operator=(const My_unique_ptr<T>&) = delete;
10     ~My_unique_ptr()
11     {
12         if (_ptr)
13         {
14             delete _ptr;
15             _ptr = nullptr;
16         }
17     }
18     T& operator*()
19     {
20         return *_ptr;
21     }
22     T* operator->()
23     {
24         return _ptr;
25     }
26 };

3、实现shared_ptr智能指针

  为了弥补unique_ptr不能拷贝的缺陷,C++11中还引入了shared_ptr,他的实现思路是引用计数,通过计数的方式来实现多个智能指针共同管理一个资源。shared_ptr在其内部,给每个资源都维护了着一份计数,用来记录该份资源被几个对象共享。在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减一。如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

  相较于unique_ptr,它弥补了不能拷贝的缺陷,但是因为需要保证多线程并发时的线程安全问题,所以对于计数操作要进行加锁,所以导致其效率相对来说会低一些,并且还存在循环引用的问题,所以大部分情况下如果不需要进行拷贝,都会使用unique_ptr,需要拷贝时才使用shared_ptr.

实现:

  1、引用计数实现

  2、独特地析构函数

  3、允许拷贝构造

  4、允许复制构造(注意:需要先释放掉原来所指向地对象)

 1 template<typename T>
 2 class mysharedptr {
 3 private:
 4     T* _ptr;
 5     int* _pcount;
 6     std::mutex* _pmtx;
 7     //加锁保证线程安全
 8     void add_ref_count() {
 9         _pmtx->lock();
10         ++(*_pcount);
11         _pmtx->unlock();
12     }
13     void release() {
14         bool flag = false;
15         _pmtx->lock();
16         if (--(*_pcount) == 0) {
17             if (_ptr) {
18                 delete _ptr;
19                 _ptr = nullptr;
20             }
21             delete _pcount;
22             _pcount = nullptr;
23             flag = true;
24         }
25         _pmtx->unlock();
26         if (flag) {
27             delete _pmtx;
28             _pmtx = nullptr;
29         }
30     }
31 
32 public:
33     mysharedptr(T* ptr) : _ptr(ptr), _pcount(new int(1)), _pmtx(new std::mutex) {}
34     mysharedptr(mysharedptr<T>& sp) : _ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){
35         add_ref_count();
36     }
37     mysharedptr<T>& operator=(mysharedptr<T>& sp) {
38         if (this != &sp ) {
39             release();        //自己原来所指向的对象的引用计数减1
40             _ptr = sp._ptr;
41             _pcount = sp._pcount;
42             _pmtx = sp._pmtx;
43 
44             add_ref_count();
45         }
46         return *this;
47     }
48     ~mysharedptr() {
49         release();
50     }
51 
52     T& operator*() {
53         return *_ptr;
54     }
55     T* operator->() {
56         return _ptr;
57     }
58 
59     T* get() const {
60         return _ptr;
61     }
62     size_t use_count() const {
63         return *_pcount;
64     }
65 };
View Code

 四、数字字符串相加(lc415)

 1 string addStrings(string num1, string num2) {
 2     int len1 = num1.size(), len2 = num2.size();
 3     int len = max(len1, len2);
 4     vector<int> reg(len, 0);
 5     int i1 = len1 - 1, i2 = len2 - 1, index = len - 1, pre = 0;
 6     string ans;
 7     while (i1 >= 0 || i2 >= 0 || pre != 0)
 8     {
 9         int x = i1 >= 0 ? num1[i1] - '0' : 0;
10         int y = i2 >= 0 ? num2[i2] - '0' : 0;
11         int cur = x + y + pre;
12         ans.push_back('0' + cur % 10);
13         pre = cur / 10;
14         i1--;
15         i2--;
16     }
17     reverse(ans.begin(), ans.end());
18     return ans;
19 }

 

posted @ 2021-07-30 11:33  Mr-xxx  阅读(332)  评论(0编辑  收藏  举报