c++重点知识

一.智能指针

1.shared_ptr:

核心:引用计数机制  ,不要用地址传入方式,而是make_type

                               慎用智能指针的get方法

                     可以给shared_ptr指定删除器,方法为

#include<memory>
#include<iostream>
using namespace std;

void delete_func(int * p){
    cout<<"user deleter"<<endl;
    delete p;
}

int main(int argc, char ** argvs){
    {   //这种方法无法创建智能指针
        //shared_ptr<int> p = new int(3);
        //make_shared 无法指定删除器
        shared_ptr<int> p (new int(30),delete_func);
    }
    cout<<"main release" <<endl;
    return 0; 
}

  注意要点

 1.不要用一个原始指针创建多个智能指针

#include<memory>
#include<iostream>
using namespace std;

void delete_func(int * p){
    cout<<"user deleter"<<endl;
    delete p;
}

int main(int argc, char ** argvs){
    {   //这种方法无法创建智能指针
        //shared_ptr<int> p = new int(3);
        //make_shared 无法指定删除器
        int * raw_ptr = new int(30);
        //双重析构错误
        shared_ptr<int> p(raw_ptr);
        shared_ptr<int> q(raw_ptr);
    }
    cout<<"main release" <<endl;
    return 0; 
}
//错误信息
root@DESKTOP-7R6L4Q8:~/code/linux_c++/stl and etc# ./ptr
free(): double free detected in tcache 2
Aborted

2.不要在函数实参中创建shared_ptr

//函数参数的计算顺序由编译器决定,如果从右向左并且g产生异常,那么就造成
//内存泄漏
function(shared_ptr<int>(new int), g()); //有缺陷

3.注意如果一个对象要返回自己的this指针作为shared_ptr的参数,那么要使用enable_shared_from_this,否则双重析构

//正确
class A:public enable_shared_from_this<A>{
public:
    shared_ptr<A> my_(){
        return shared_from_this();
    }
};
//错误
class B{
public:
    shared_ptr<B> my_(){
        return shared_ptr<B>(this);
    }
};
int main(int argc, char ** argvs){
    {   
        auto p = make_shared<B>();
        cout<<p.use_count()<<endl;
        shared_ptr<B> p1 = p->my_();
        cout<<p.use_count()<<endl;
        cout<<p1.use_count()<<endl;
    }
    cout<<"main release" <<endl;
    return 0; 
}

运行结果

1
1
1
double free or corruption (out)
Aborted

使用A版本的enable_shared_from_this 后正常运行

4.避免循环引用:

#include <memory>
#include <iostream>
using namespace std;
//循环引用导致ap和bp的引用计数为2,在离开作用域之后,ap和bp的引用计数减为1,并不回减为0,导
//致两个指针都不会被析构,产生内存泄漏。
//解决的办法是把A和B任何一个成员变量改为weak_ptr,具体方法见weak_ptr章节。

// std::make_shared是c++11的一部分,但std::make_unique不是。它是在c++14里加入标准库的。
class A;
class B;
class A
{
public:
    std::shared_ptr<B> bptr;
    ~A()
    {
        cout << "A is deleted" << endl;
    }
};
class B
{
public:
    std::shared_ptr<A> aptr;
    ~B()
    {
        cout << "B is deleted" << endl;
    }
};
int main()
{
    {
        std::shared_ptr<A> ap(new A);
        std::shared_ptr<B> bp(new B);
        ap->bptr = bp;
        bp->aptr = ap;
    }
    cout << "main leave" << endl; // 循环引用导致ap bp退出了作用域都没有析构
    return 0;
}

2.unique_ptr

 独占的指针,nocopyable,只能通过move传递所有权

3.weak_ptr

一种不控制对象生命周期的智能指针

可以将weak_ptr传给shared_ptr,lock()方法

4.智能指针线程安全性

        1.安全:引用计数是安全的(几次赋值就传几次)

        2.不安全:多个线程共用一个智能指针(即传引用方式)

void fn(std::shared_ptr<int> &a){
    cout<<*(a.get())<<endl;
    a.reset();
}
void fn2(std::shared_ptr<int> &a){
    sleep(5);
    cout<<*(a.get())<<endl;
    a.reset();
}


int main()
{  
   shared_ptr<int> p(new int (0));
   std::thread t1(fn,std::ref(p));
   std::thread t2(fn2,std::ref(p));   
   t1.join();
   t2.join();
   cout<<"end"<<endl;
   return 0;
}

   这样会造成段错误                       

解决方式:改成传值即可

void fn(std::shared_ptr<int> a){
    cout<<*(a.get())<<endl;
    a.reset();
}
void fn2(std::shared_ptr<int> a){
    sleep(5);
    cout<<*(a.get())<<endl;
    a.reset();
}


int main()
{  
   shared_ptr<int> p(new int (0));
   std::thread t1(fn,p);
   std::thread t2(fn2,p);   
   t1.join();
   t2.join();
   cout<<"end"<<endl;
   return 0;
}

但是这时候每个线程依然是指向的同一个对象,多线程的并发性需要自己保证

二.右值引用

消除拷贝,提高性能

左值,表达式结束后任然存在的对象,右值是指表达式结束就不存在的对象

应用:具有大量堆内存的类

forward:仅仅将值的属性完美转发 例子:

#include <memory>
#include <iostream>
#include <thread>
#include<unistd.h>
using namespace std;

template<typename T>
void l_or_r(T &&t){
    cout<< " R be called"<<endl;
}

template<typename T>
void l_or_r(T & t){
    cout<< " L be called"<<endl;
}


template<typename T>
void test(T && t){
    l_or_r(t);//must L be called no matter t L or R
    l_or_r(std::move(t));//R be called no matter t L or R
    //forward 仅仅转发自己的左右值属性
    l_or_r(std::forward<T>(t));//depend on t,just pass the L or R
}


int main()
{  
    int a;
    test(a);
    test(8);
    return 0;
}
int &&a = 8;
    //oops error,a is a l value
    //int && b = a;
    //correct
    int && b = std::forward<int>(a);

三.多线程

1.主要函数

        join():等待线程结束

        get_id():返回线程的id号

        joinable():返回线程是否被detach()

        detach():允许当前线程独立运行,当进程被kill那么该线程也会被强制杀死

2.thread_local:申明为thread_local 的变量不论是否被申明为static都会在本地线程中持续存在

3.互斥量:

c++11的四种互斥量:a.std::mutex 独占的互斥量,不能递归使用

                                   b.std::timed_mutex:带计时功能的互斥量,不能递归使用

                                   c.std::recursive_mutex:递归互斥量,不带计时功能(允许同一个线程多次获取同一个互斥量),效率低,能够用普通互斥量代替,不建议使用

                                    d.std::recursive_timed_mutex:带计时功能的递归互斥量

lock_guard于unique_lock的使用和区别

        两者都可以实现自动加锁和解锁

        unique_lock更加灵活,可以实现临时的加锁和解锁,但性能略差于lock_guard

        tips:减少锁的粒度可以用大括号来限定unique_lock的范围

条件变量condition_variable

        条件变量使用流程:1.拥有条件变量的线程获取互斥锁2.阻塞直到条件满足3.某个线程使用notify_one或者notify_all唤醒线程,condition_variable需要与unique_lock来使用

std::call_once和std::once_flag的使用     

#include<thread>
#include<mutex>
#include<iostream>
using namespace std;
std::once_flag fg1;
void run(){
    cout<<" i can be called mamy times"<<endl;
    std::call_once(fg1,[](){
        cout<<"i be called once"<<endl;
    });
}

int main(){
    for(int i = 0;i < 4;++i){
        thread t(run);
        t.join();
    }
    return 0;   
}

4.异步操作  

std::async + std::future

#include<thread>
#include<mutex>
#include<iostream>
#include<future>
using namespace std;

int get_a_value(){
    this_thread::sleep_for(5s);
    return 30;
}

int main(){
    //创建一个未来可以取值的整数
    future<int> result = std::async(get_a_value);
    cout<<"one called"<<endl;
    //get方法阻塞等待
    cout<<result.get()<<endl;//be 30
    cout<<"tow called"<<endl; 
    return 0;   
}

std::package_task,将func与future打包起来

四.std::bind

        绑定函数与参数,同时也可以运用std::placeholders来绑定成员函数

        

#include<iostream>
#include<mutex>
#include<functional>

using namespace std;
class fun_c{
public:
    void mem_fn(){
        cout<<"this is mem_fn"<<endl;
    }
};
int main(){
    auto a = new fun_c();
    //绑定一个成员变量,std::placeholders给mem_func占一个位
    auto func_bind = std::bind(&fun_c::mem_fn,std::placeholders::_1);
    func_bind(a);
    return 0;
}

五.线程池

thread_pool.h

void getNow(timeval *tv);
int64_t getNowMs();

#define TNOW getNow()
#define TNOWMS getNowMs()

class CZY_ThreadPool{
protected:
    struct TaskFunc{
        TaskFunc(uint64_t expireTime)
            :expire_time_(expireTime)
        {}
        std::function<void()> func_;
        int64_t expire_time_ = 0;
    };
    typedef shared_ptr<TaskFunc> TaskFuncPtr;
public:
    CZY_ThreadPool();

    //虚析构函数,基类的析构函数必须是虚函数
    virtual ~CZY_ThreadPool();

    
    bool init(size_t num);

    size_t getThreadNum(){
        std::unique_lock<std::mutex> lock(mutex_);
        return therads_.size();
    }

    size_t getJobNum(){
        std::unique_lock<std::mutex> lock(mutex_);
        return tasks_.size();
    }

    void stop();

    bool start();

    //可变模板参数

    template<class Func,class ...Args>

    auto exec(Func && f,Args &&... args) ->std::future<decltype(f(args...))>
    {
        return exec(0,f,args...);
    }

    template<class Func,class ...Args>
    
    //exec重载,支持计时操作
    auto exec(int64_t timeoutMs,Func && f,Args&& ... args) ->std::future<decltype(f(args...))>
    {

        int64_t expireTime = (timeoutMs == 0) ? 0 : TNOW + timeoutMs;

        //自动推导返回值类型
        using retType = decltype(f(args...));
        
        //将函数的参数完美转发即可
        //这样可以做到所有的都是无参数的统一形式
        auto task = std::make_shared<std::packaged_task<retType()>>(std::bind(std::forward<F>(f),std::forward<Args>(args)...));

        TaskFuncPtr fPtr = std::make_shared<TaskFunc> (expireTime);

        fPtr->func_ = [](){
            (*task)();
        };

        //互斥访问队列
        std::unique_lock<std::mutex> lock(mutex_);

        tasks_.push(fPtr);

        condition_.notify_one();

        return task->get_future();
    }

    bool waitForAllDone(int millsecond = -1);

protected:
    bool get(TaskFuncPtr &task);

    bool isTerminate(){
        return bterminate_;
    }

    void run();

protected:
    std::mutex mutex_;
    vector<std::thread *> therads_;
    std::condition_variable condition_;
    size_t thread_num;
    bool bterminate_;
    std::queue<TaskFuncPtr> tasks_;
    std::atomic<int> atomic_ {0};
};

int gettimeofday(struct timeval &tv)
{
#if WIN32
    time_t clock;
    struct tm tm;
    SYSTEMTIME wtm;
    GetLocalTime(&wtm);
    tm.tm_year   = wtm.wYear - 1900;
    tm.tm_mon   = wtm.wMonth - 1;
    tm.tm_mday   = wtm.wDay;
    tm.tm_hour   = wtm.wHour;
    tm.tm_min   = wtm.wMinute;
    tm.tm_sec   = wtm.wSecond;
    tm. tm_isdst  = -1;
    clock = mktime(&tm);
    tv.tv_sec = clock;
    tv.tv_usec = wtm.wMilliseconds * 1000;

    return 0;
#else
    return ::gettimeofday(&tv, 0);
#endif
}

void getNow(timeval *tv)
{
#if TARGET_PLATFORM_IOS || TARGET_PLATFORM_LINUX

    int idx = _buf_idx;
    *tv = _t[idx];
    if(fabs(_cpu_cycle - 0) < 0.0001 && _use_tsc)
    {
        addTimeOffset(*tv, idx);
    }
    else
    {
        TC_Common::gettimeofday(*tv);
    }
#else
    gettimeofday(*tv);
#endif
}

int64_t getNowMs()
{
    struct timeval tv;
    getNow(&tv);

    return tv.tv_sec * (int64_t)1000 + tv.tv_usec / 1000;
}

thread_pool.cpp

#include "thread_pool.h"

CZY_ThreadPool::CZY_ThreadPool()
    :thread_num(1),bterminate_(false)
{}

CZY_ThreadPool::~CZY_ThreadPool(){
    stop();
}

bool CZY_ThreadPool::init(size_t num){
    std::unique_lock<mutex> lock(mutex_);
    if(!therads_.empty()){
        return false;
    }
    thread_num = num;
    return true;
}

void CZY_ThreadPool::stop(){
    {
        std::unique_lock<mutex> lock(mutex_);
        bterminate_ = true;
        condition_.notify_all();
    }

    for(size_t i = 0;i < therads_.size() ; ++i){
        //将所有未悬挂的任务运行
        if(therads_[i]->joinable()){
            therads_[i]->join();
        }
        delete therads_[i];
        therads_[i] = nullptr;
    }
    std::unique_lock<mutex> lock(mutex_);
    //保留capacity,重置size,调用析构
    //这里的threads都被设置成了nullptr
    therads_.clear();
}

bool CZY_ThreadPool::start(){
    std::unique_lock<mutex> lock(mutex_);
    if(!therads_.empty()){
        return false;
    }
    for(int i = 0; i < thread_num ; ++i){
        //这里都是推入的run函数,run函数阻塞读任务并且运行
        therads_.push_back(new thread(&CZY_ThreadPool:: run,this));
    }
    return true;
}

bool CZY_ThreadPool::get(TaskFuncPtr& task){
    std::unique_lock<std::mutex> lock(mutex_);

    //条件变量自动加锁,释放锁
    if(tasks_.empty()){
        //把condition的条件写在wait里,这样可以避免
        condition_.wait(lock,[this] {return bterminate_ || !tasks_.empty();});
    }

    if(bterminate_){
        return false;
    }

    if(!tasks_.empty()){
        task = std::move(tasks_.front());
        tasks_.pop();
        return true;
    }
    return false;
}

void CZY_ThreadPool::run(){
    while(!isTerminate()){
        TaskFuncPtr task;
        bool ok = get(task);
        if(ok){
            ++atomic_;
            try{
            if(task->expire_time_ != 0 && task->expire_time_ < TNOWMS){
                //超时任务,打印日志
            }
            else{
                //func是用用户传入的执行函数
                task->func_();
            }
            }
            catch(...){
                
            }
            --atomic_;
            std::unique_lock<std::mutex> lock(mutex_);
            if(atomic_ == 0 && tasks_.empty()){
                condition_.notify_all();
            }

        }
    }
}

bool CZY_ThreadPool::waitForAllDone(int millssecond){
    std::unique_lock<std::mutex> lock(mutex_);
    if(tasks_.empty()){
        return true;
    }
    if(millssecond < 0){
        condition_.wait(lock,[this] {return tasks_.empty();});
        return true;
    }
    else{
        return condition_.wait_for(lock, std::chrono::milliseconds(millssecond), [this] { return tasks_.empty(); });
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值