C++11的特性

C++11教程

  • 整形提升的隐式转换

    • 5种标准整型:signed char、short int、int、long int、long long int
    • 等级
      • 长度越大的整型等级越高:long long int 高于 int
      • 长度相同时标准整型的等级 高于 扩展类型:long long int 高于 int64
    • 转换规则
      • 低级 转 高级
      • 有符号 转 无符号
  • 类模板与函数模板

    • c++11前不支持函数模板的默认参数,只支持类模板的默认参数。c++11后就支持了
    • 当所有模板参数都有默认参数时,函数模板的调用如同一个普通函数。
    • 但对于类模板而言,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随<>来实例化。
  • 字符串与数值类型的转换(头文件string)

    • 转为字符串:to_string
    • 转为数值:stoi、stof
    • 如果字符串的前半部分字符是数值类型,后半部不是,那么前半部分会被转换为对应的数值返回
  • 静态断言

    • 在编译阶段进行检测,且不需要引入头文件
      static_assert(sizeof(long) == 8, "错误, 不是64位平台...");
  • noexcept

    • c++11以前,在函数后面跟throw(),不带参数表示不抛出异常

    • c++11中,使用noexcept或者noexcept(true)表示不抛出异常

      #include <iostream>
      #include <string>
      using namespace std;
      struct MyException
      {
          MyException(string s) :msg(s) {}
          string msg;
      };
      double divisionMethod(int a, int b) noexcept
      {
          if (b == 0)
          {
              throw MyException("division by zero!!!");
              // throw 100;
          }
          return a / b;
      }
      
      int main()
      {
          try
          {	
              double v = divisionMethod(100, 0);
              cout << "value: " << v << endl;
          }
          catch (int e)
          {
              cout << "catch except: "  << e << endl;
          }
          catch (MyException e)
          {
              cout << "catch MyException: " << e.msg << endl;
          }
          return 0;
      }
      
  • auto的限制

    • 不能推导函数参数类型
      int func(auto a, auto b);//error
    • 不能推导 非静态非常量 的 成员变量
      class Test
      {
          auto v1 = 0;                    // error
          static auto v2 = 0;             // error
          const auto v3 = 0;              // error
          static const auto v4 = 10;      // ok
      }
      
    • 不能用来定义数组
      auto t3[] = {1,2,3,4,5}; // error, auto无法定义数组
    • 不能推导模板参数
  • nullptr

    • 在c中,NULL等价于0,这导致函数重载时,如果传入NULL,无法分辨是空指针还是0
    • 因此,c++中使用nullptr来表示空指针,NULL和0都表示数值0
  • lambda表达式也是c++11引入的

  • constexpr

    • 此前const有2重意思。区分 变量只读 与 常量:
      对于const int& b = a1; a1变了,b也会随之变化,因此b是只读的,但不是常量
    • 引入constexpr后:使用 const表示只读,使用 constexpr表示常量
    • 用于修饰函数时
      • 正常得到常量表达式函数
        constexpr int func1()
        {
            constexpr int a = 100;//函数内部不能出现非常量表达式
            return a;//返回值必须是常量
        }
        
      • 修饰模板函数
        如果模板函数实例化结果不满足常量表达式函数的要求,则 constexpr 会被自动忽略
      • 修饰构造函数:直接得到一个常量对象
        函数体必须为空、初始化列表的方式为各个成员赋值
  • 继承构造函数

    • using A::A; 在子类中这样写上,就相当于写了所有父类的构造函数,不用繁琐的在子类中重新定义和基类一致的构造函数了

    • using A::fun;在子类中这样写上,就可以调用被子类隐藏的父类同名函数

      #include <iostream>
      using namespace std;
      
      class A
      {
      private:
          int m;
      public:
          A(int m) {this->m = m;};
          ~A(){};
          void fun(int m)
          {
              cout<<"fun1"<<endl;
          }
          void fun(int m, int n)
          {
              cout<<"fun2"<<endl;
          }
      };
      
      class B: public A
      {
      public:
          using A::A;
          using A::fun;
          void fun(int m)
          {
              cout<<"child"<<endl;
          }
      
      };
      
      int main()
      {
          B b(3);
          b.fun(2);//调B的fun
          b.fun(2,2);//调A的fun2
          return 0;
      }
      
  • 可调用对象

      1. 普通函数
      1. 函数指针 int (*func)(int, double) = &print;
      1. 匿名函数
        [](int a, int b) -> int{
        	cout << a << " * " << b << " = " << a * b << endl;
        };
        
      1. 仿函数:重载了operator()的类对象
      1. 重载类型转换操作符,使其返回函数指针
      • 首先定义一个函数指针
        using func_ptrxx = void(*)(int, string);
      • 然后重载类型转换操作符operator type()
        operator func_ptrxx()
        {
            return print;//注意print函数必须是static的
        }
        
      1. 是类成员函数指针 或 类成员指针
      #include <iostream>
      using namespace std;
      using func_ptrxx = void(*)(int, string);
      struct Test
      {
          void yx(int a, string b) {cout<<"fun"<<endl;}
          int m;
      };
      
      int main(void)
      {
          void (Test::*func_ptr)(int, string) = &Test::yx;//类成员函数指针
          int Test::*obj_ptr = &Test::m;//类成员指针
          return 0;
      }
      
  • 可调用对象包装器:可以容纳除了类成员(函数)指针之外的所有可调用对象
    #include <functional>
    std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;

    // 绑定一个普通函数
    function<int(int, int)> f1 = add;
    // 绑定以静态类成员函数
    function<int(int, int)> f2 = T1::sub;
    // 绑定一个仿函数
    T2 t;
    function<int(int, int)> f3 = t;
    //绑定lambda表达式
    function<int(int, int)> f4 = [](int a, int b) -> int{
        cout << a << " * " << b << " = " << a * b << endl;
    };
    
    // 函数调用
    f1(9, 3);
    f2(9, 3);
    f3(9, 3);
    f4(9, 3);
    
  • 可调用对象包装器作为回调函数

    #include <iostream>
    #include <functional>
    using namespace std;
    class A
    {
    public:
        //常量左值引用可以引用右值
        // A(const function<void()> &f): callback(f)
        // {
        // }
        //右值引用可以引用右值,延长右值的声明周期
        // A(function<void()> &&f): callback(f)
        // {
        // }
        //传入的右值被调用移动构造来创建f,而不是拷贝构造
        A(function<void()> f): callback(f)
        {
        }
        void notify()
        {
            callback();
        }
    private:
        function<void()> callback;
    };
    
    int main(void)
    {   
        A a([](){
            cout<<"dd"<<endl;
        });//传入右值
        a.notify();
        return 0;
    }
    
  • 绑定器bind

    在传绑定的参数时,如果想传引用,必须使用ref包起来,如果想传const引用,使用cref
    void f(int& n1, int& n2, const int& n3)
    bind(f, n1, ref(n2), cref(n3));//ref传引用,cref传const引用

    • 绑定非类成员函数
      auto f = std::bind(可调用对象地址, 绑定的参数/占位符);

      #include <iostream>
      #include <functional>
      using namespace std;
      
      void callFunc(int x, int y, const function<void(int, int)>& f)
      {
          f(x,y);
      }
      void output(int x, int y) {cout << x / y << endl;}
      int main(void)
      {
          // 使用绑定器绑定可调用对象和参数
          auto f1 = bind(output, placeholders::_2, placeholders::_1);
          callFunc(2, 3, f1);
          return 0;
      }
      
    • 绑定类成员函数
      auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
      注意:对于非类成员函数,函数名就是函数的地址;
      但是,对于类成员函数,它的函数名表示成员函数相对于这个类的地址。
      function<void(int, int)> f1 = bind(&Test::output, &t, placeholders::_1, placeholders::_2);

  • 改进了friend语法

    • 声明一个类为另外一个类的友元时,不再需要使用class关键字,并且还可以使用类的别名
      friend class Tom; // C++98 标准语法
      friend Tom; // C++11 标准语法
    • 可以把类模板T声明成友元
      #include <iostream>
      using namespace std;
      
      template<class T>
      class B
      {
      public:
          friend T;
      private:
          int m=3;
      };
      
      class A
      {
      public:
          void fun() {cout<<b.m<<endl;}
      private:
          B<A> b;
      };
      
      int main()
      {
          A a;
          a.fun();
          return 0;
      }
      
  • 强类型枚举:加关键字class或者struct

    • 此前的枚举值会默认从0开始依次赋值,且枚举成员的名字全局可见,易冲突。
    • c++11中提出强类型枚举,使得各枚举值有自己的作用域,且无法隐式类型转换为int
      #include <iostream>
      using namespace std;
      enum class Color{red,black};/底层类型值默认为int
      enum class Name:char{nihao,hh};
      int main()
      {
      	//int res = Color::red;强类型枚举值不再能够作为整型使用了
          cout<<sizeof(Color::red)<<endl;//4
          cout<<sizeof(Name::nihao)<<endl;//1
          return 0;
      }
      
    • 对原来的枚举进行了扩展。使其也能指定底层类型。枚举成员的名字除了会自动输出到父作用域,也可以在枚举类型定义的作用域内有效。
      #include <iostream>
      using namespace std;
      
      enum Color:char{red,black};
      // enum Name:char{nh,black};//black重定义
      int main()
      {
          cout<<sizeof(red)<<endl;
          cout<<sizeof(Color::red)<<endl;
          return 0;
      }
      
  • POD类型

    • 分为2个类型:平凡的、标准布局的
      • 平凡的类(结构体)需要满足
        • 使用编译器生成的默认构造函数、析构函数、拷贝构造、移动构造、拷贝赋值、移动赋值
        • 不含虚函数、不含虚基类
      • 标准布局的类(结构体)需要满足
        • 非静态成员的访问权限一样
        • 派生类中有非静态成员,基类中包含静态成员(或基类没有变量)或者 基类有非静态成员,而派生类没有非静态成员。
        • 子类第一个非静态成员 不要是 父类类型的对象
        • 不含虚函数、不含虚基类
    • 判断是否为平凡类型:使用std::is_trivial的成员value来判断
      cout << "int: " << is_trivial<int>::value << endl;
    • 判断是否为标准布局类型:使用std::is_standard_layout的成员value来判断
      cout << "int: " << is_standard_layout<int>::value << endl;

简记:POD类型被划分为2种,一种是平凡的,一种是标准布局的。如果一个类使用的构造析构移动拷贝赋值都是默认的,且没有虚函数,也没有虚基类,那么就是平凡的类型…

  • 非受限联合体

    • 之前的联合体不允许有静态成员、不允许有引用类型的成员、不允许有非POD类型的成员
    • 现在,只要不是引用类型都可以作为联合体的数据成员
  • placement new的使用 ClassName* ptr = new (定位的内存地址)ClassName;

    使用placement new,我们可以反复动态申请到同一块堆内存,这样可以避免内存的重复创建销毁,从而提高程序的执行效率

  • 智能指针
    weak_ptr与shared_ptr一起使用,unique_ptr不能生成weak_ptr
    weak_ptr有失效检测
    传递智能指针时都按值传递,不要按引用传递
    头文件 #include <memory>

    • std::shared_ptr:共享的智能指针

      • 共享智能指针的初始化方式

        #include <iostream>
        #include <memory>
        using namespace std;
        
        int main()
        {
            int *a = new int;
            shared_ptr<int> ptr1(a);//通过构造函数初始化,后面参数还能再写一个删除器
            cout<<ptr1.use_count()<<endl;//1
        
            shared_ptr<int> ptr2(ptr1);//通过拷贝构造初始化
            cout<<ptr1.use_count()<<endl;//2
            cout<<ptr2.use_count()<<endl;//2
        
            shared_ptr<int> ptr3(move(ptr1));//通过移动构造初始化
            cout<<ptr1.use_count()<<endl;//0
            cout<<ptr2.use_count()<<endl;//2
            return 0;
        }
        
      • 使用make_shared 完成内存对象的创建的同时,将其初始化给智能指针
        make_shared<数据类型>(构造函数的参数列表)

        #include <iostream>
        #include <memory>
        using namespace std;
        
        class A
        {
        public:
            A(int m):m(m)
            {
                cout<<m<<endl;
            };
        private:
            int m;
        };
        
        int main()
        {
            shared_ptr<int> ptr1 = make_shared<int>(10);
            shared_ptr<A> ptr2 = make_shared<A>(20);//20
            return 0;
        }
        
      • reset的使用
        重置ptr1,使其不指向任何对象:ptr1.reset()
        重置ptr2,使其指向新的对象,并删除之前的对象:ptr2.reset(其它指针)
        重置ptr3,使其指向新对象,并使用自定义的删除器来删除之前的智能指针:ptr3.reset(newPtr, CustomDeleter())

        #include <iostream>
        #include <memory>
        using namespace std;
        
        int main()
        {
            shared_ptr<int> ptr1 = make_shared<int>(10);
            shared_ptr<int> ptr2 = ptr1;
            shared_ptr<int> ptr3 = ptr2;
        
            cout<<ptr1.use_count()<<endl;//3
            cout<<ptr2.use_count()<<endl;//3
            cout<<ptr3.use_count()<<endl;//3
        	//指向新的对象,引用计数减少了
            int *b = new int;
            ptr3.reset(b);
            cout<<ptr1.use_count()<<endl;//2
            cout<<ptr2.use_count()<<endl;//2
            cout<<ptr3.use_count()<<endl;//1
        	//使用自定义的删除器
            ptr2.reset(b, [](int * p){
                delete p;
                cout<<"已释放"<<endl;
            });
        
            return 0;
        }
        
      • get()获取原始指针
        int *a = ptr1.get();

        //实际使用时一般不用get,而是直接用ptr1->来操作原始对象
        #include <iostream>
        #include <memory>
        using namespace std;
        class A
        {
        public:
            void fun(){cout<<m<<endl;}
            int m=5;
        };
        int main() 
        {
            shared_ptr<A> ptr1 = make_shared<A>();
            ptr1->m = 3;
            ptr1->fun();
            return 0;
        }
        
      • 对于数组指针,需要指定删除器
        使用shared_ptr管理动态数组时需要自定义删除器,因为它的默认删除器不支持数组对象
        或者指定删除器为default_delete<int[]>() 即可删除数组

        #include <iostream>
        #include <memory>
        using namespace std;
        
        class A
        {
        public:
            A(){cout<<"构造"<<endl;}
            ~A(){cout<<"析构"<<endl;}
        };
        int main()
        {
            A* a = new A[5];
            shared_ptr<A> ptr1(a, [](A* ptr){
                delete[] ptr;
            });
            //等价于
            //shared_ptr<A> ptr1(a, default_delete<A[]>());
            return 0;
        }
        
    • std::unique_ptr:独占的智能指针

      • 和共享智能指针的使用差不多,只不过不允许复制(其删除了拷贝构造函数),而且指定删除器的时候需要给出删除器的类型
        #include <iostream>
        #include <memory>
        using namespace std;
        class A
        {
        public:
            A(){cout<<"构造"<<endl;}
            ~A(){cout<<"析构"<<endl;}
        };
        int main()
        {
            A* a = new A[5];
            //函数指针 返回类型(*指针变量名)(参数列表)
            using func_ptr = void(*)(A*);
            unique_ptr<A, func_ptr> ptr1(a, [](A* ptr){
                delete[] ptr;
            });
            //等价于
            // unique_ptr<A, default_delete<A[]>> ptr1(a, default_delete<A[]>());
            return 0;
        }
        
      • 上述代码中使用lambda表达式写的删除器没有捕获外部变量,因此会转换成函数指针,但是改为捕获外部变量就会报错,需要使用可调用对象包装器。
        A* a = new A[5];
        //使用可调用对象包装器来处理即可
        unique_ptr<A, function<void(A*)>> ptr1(a, [&](A* ptr){
            delete[] ptr;
        });
        
    • std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视shared_ptr的

      • 初始化:通过共享指针、其它弱引用指针

        #include <iostream>
        #include <functional>
        #include <memory>
        using namespace std;
        class A
        {
        public:
            A(){cout<<"构造"<<endl;}
            ~A(){cout<<"析构"<<endl;}
        };
        int main()
        {
            shared_ptr<A> ptr1 = make_shared<A>();
            weak_ptr<A> w_ptr1 = ptr1;//隐式类型转换
            weak_ptr<A> w_ptr2(w_ptr1);
        
            cout<<"引用计数"<<w_ptr2.use_count()<<endl;//1
            cout<<"w_ptr2所监测的共享指针管理的那个原始对象是否已释放"<<w_ptr2.expired()<<endl;//0
        
            //通过弱引用指针来创建共享指针
            shared_ptr<A> ptr2 = w_ptr2.lock();
            cout<<"引用计数"<<w_ptr2.use_count()<<endl;//2
            w_ptr2.reset();//不再监测
        
            return 0;
        }
        
      • 使用shared_from_this()返回共享智能指针

        struct Test : public enable_shared_from_this<Test>
        {
            shared_ptr<Test> getSharedPtr()
            {
                return shared_from_this();
            }
        };
        
    • 使用场景

      • 当需要在多个函数中共享同一个资源,可以使用共享指针来管理。在所有函数都使用完该资源后,共享指针引用计数变为0,就会自动释放所管理的资源。
      • 如果需要自定义某指针释放时的一系列操作,那么就使用共享指针来管理,并指定删除器。
      • 提前释放:直接令智能指针为nullptr,那么会自动释放,不会导致重复释放
  • chrono库

    • 时间间隔duration:记录时间长度
      头文件:<chrono>
      #include <chrono>
      #include <iostream>
      using namespace std;
      int main()
      {
          //duration的使用
          chrono::duration<int, ratio<6,2>> h(2);//3s为一个单位,h为2个单位//ratio默认为1
          cout<<h.count()<<endl;//count()返回h表示几个单位,表示2个单位
          //其它时间间隔
          chrono::milliseconds h1 = h;//把h转为以ms为单位,即2*3000
          cout<<h1.count()<<endl;//6000ms就是6000个单位
          //计算
          chrono::seconds h2{2};//2s 可以采用初始化列表的方式赋值
          chrono::milliseconds res = h1-h2;//6000ms-2s
          cout<<res.count()<<endl;//4000
      
          //不同ratio的duration之间的运算
          chrono::duration<double, ratio<9, 7>> d1(3);
          chrono::duration<double, ratio<6, 5>> d2(1);
          chrono::duration<double, ratio<3, 35>> d3 = d1 - d2;//ratio<最大公约数,最小公倍数>
      }
      
    • 时钟clocks (在头文件chrono的命名空间chrono里)
    • 时间点time point
      • 系统时钟 system_clock
        当前系统时间点: system_clock::time_point today = system_clock::now()
        time_point转time_t类型:time_t tm = system_clock::to_time_t(today);
        time_t类型转time_point:system_clock::time_point ss = system_clock::from_time_t(tm);
      • 固定时钟(用于记录程序耗时) steady_clock
        steady_clock::time_point start = steady_clock::now();
        注意,时间点差值结果是纳秒的duration:
        auto dt = last - start;
        cout << "总共耗时: " << dt.count() << "纳秒" << endl;
      • 更高精度的high_resolution_clock,使用方法与steady_clock一样
      • 转换函数
        • duration_cast
          nanoseconds res = steady_clock::now()-m_alivetime;
          //使用duration_cast将纳秒转换成毫秒
          milliseconds millsec = duration_cast<milliseconds> (res);
          
        • time_point_cast
  • 多线程

    • 创建线程对象:thread(任务函数名,任务函数的参数);//函数名就是函数地址
      如果任务函数是类成员函数,那么第2个参数需要传 实例对象的地址,否则新线程无法影响到主线程的类实例。 thread t(&A::fun2, &a, 函数参数);

      对于非类成员函数,函数名就是函数地址,因此 thread t2(fun)即可
      对于类成员函数,它的函数名表示成员函数相对于这个类的地址,因此函数名要加&

      • 如果任务函数的参数是引用类型,那么这边参数传过去时要用ref来进行引用传递
        #include <iostream>
        #include <thread>
        void f2(int& m) {
            m++;
            std::cout << "ref:: m= " << m << std::endl;//11
        }
        int main()
        {
            int m = 10;
            //std::thread m_T1(f2, m);//报错
            std::thread m_T1(f2, std::ref(m));
            m_T1.join();
            std::cout << "m_T1:: m= " << m << std::endl;//11
        }
        
    • 获取当前线程id:this_thread::get_id()

    • 线程休眠:

      • 休眠一段时间:this_thread::sleep_for(chrono::seconds(1));
      • 休眠至某时间点:this_thread::sleep_until(now + sec);
    • 让出cpu变为就绪态:this_thread::yield();

    • 等待线程t结束才继续执行:t.join();

    • 线程分离:线程执行完会自己释放资源,不再由thread对象管理:t.detach();

    • 不能复制线程资源,只能移动:thread t2 = move(t1);//t1此后没用了

      #include <iostream>
      #include <thread>
      using namespace std;
      void thread_fun1(int m)
      {
          cout<<"xx:"<<m<<endl;
          int i=5;
          while (i--)
          {
              cout<<i<<endl;
              this_thread::sleep_for(chrono::seconds(1));//休眠1s
          }
      }
      int main()
      {
          thread t1(thread_fun1, 5);
          thread t2 = move(t1);//t1没用了
          t2.join();
          cout<<"结束"<<endl;
          return 0;
      }
      

    C语言提供的线程库

  • call_once的使用
    头文件 <mutex>

    //要保证该对象可以被多个线程同时访问到
    once_flag g_flag;
    //把这个对象传入。后面是 执行一次的函数与其函数参数
    call_once(g_flag, do_once, 19, "luffy");
    
  • 互斥锁mutex

    • 创建锁:mutex g_num_mutex;

    • 加锁:g_num_mutex.lock();

      • 如果无法保证多个锁的上锁顺序,可以使用std::lock(mutex1, mutex2,…)来同时上锁多个,这样不会因为线程调用顺序导致死锁。
      • std::lock有个对应的 会自动调用解锁的版本:std::lock_guard
    • 尝试加锁(不阻塞):g_num_mutex.try_lock();

    • 解锁:g_num_mutex.unlock();

    • c++11新增了哨兵锁,析构时解锁:lock_guard lock(g_num_mutex);

    • unique_lock析构时也自动解锁,同时还能手动释放锁

      lock_guardunique_lock
      构造函数中获取锁,析构函数中释放锁构造函数中获取锁,析构函数中释放锁
      将拷贝构造函数和赋值运算符重载函数delete掉了,也就是说lock_guard将 不能作为函数参数传递或者函数的返回值将拷贝构造函数和赋值运算符重载函数delete掉了,但新增了带右值引用的拷贝和赋值,可以在函数传参或者参数返回中使用该锁
      使用了智能指针的思想,出作用域自动释放锁。提供了lock()和unlock()方法使得能够与条件变量搭配使用
    • 递归互斥锁允许一个线程多次获取同一个互斥锁:recursive_mutex

    • 超时锁:timed_mutex g_mutex;

      chrono::seconds timeout(1);
      g_mutex.try_lock_for(timeout)//返回true表示抢到锁了
      
    • 读写锁
      c++14引入了限时读写锁shared_timed_mutex,该锁有普通的排他性锁定lock(),也有共享锁定lock_shared()

      //创建限时读写锁
      std::shared_timed_mutex stmtx;
      //读
      void ReadThread()
      {
      	stmtx.lock_shared();//共享锁定
      	//读取数据...
      	stmtx.unlock_shared();
      }
      //写
      void WriteThread()
      {
      	stmtx.lock();//排他锁定
      	//写数据...
      	stmtx.unlock();
      }
      

      同时,c++14还引入了shared_lock包装器用于包装带lock_shared的锁,使得不用手动释放读写锁。shared_lock的lock是共享锁定的,unique_lock的lock是排他锁定的,因此,一般像下面这样使用

      //创建限时读写锁
      std::shared_timed_mutex stmtx;
      //读
      void ReadThread()
      {
      	std::shared_lock<std::shared_timed_mutex> lock(stmtx);
      	//读取数据...
      }
      //写
      void WriteThread()
      {
      	std::unique_lock<std::shared_timed_mutex> lock(stmtx);
      	//写数据...
      }
      

    c++17引入了读写锁shared_mutex,与shared_timed_mutex类似,只不过少了几个限时的函数。

  • c++11提供了2种条件变量

    • condition_variable
      头文件<condition_variable>
      • 需要使用unique_lock锁,而不能用lock_guard锁,因为该锁只会在析构时释放
      • 条件变量:condition_variable m_notFull;
      • 阻塞
        • 无条件阻塞:
          m_notEmpty.wait(locker);
        • 带条件阻塞:等价于while(m_queue.size() ==m_maxSize){m_notFull.wait(locker);}
          m_notFull.wait(locker, [this]() {return m_queue.size() !=m_maxSize;});
      • 唤醒:m_notEmpty.notify_one();
    • condition_variable_any
      • 传入wait的不是unique_lock锁,而是普通的互斥锁mutex m_mutex;

        #include <iostream>
        #include <thread>
        #include <mutex>
        #include <functional>
        #include <condition_variable>
        using namespace std;
        class A
        {
        public:
            void xiaofei()
            {
                while (1)
                {
                    //使用条件变量,必须用这个锁
                    unique_lock<mutex> locker(m_mutex);
                    //相当于while(!后面的条件){wait}
                    not_empty.wait(locker, [this](){
                        return m > 0;//这里是不阻塞的条件
                    });
                    m--;//消费
                    not_full.notify_all();//唤醒生产者
                    cout<<"消费,当前m="<<m<<endl;
                    this_thread::sleep_for(chrono::seconds(1));//线程休眠
                }  
            }
            void shengchan()
            {
                while (1)
                {
                    unique_lock<mutex> locker(m_mutex);
                    not_full.wait(locker, [this](){
                        return m < maxnumber;
                    });
                    m++;//生产
                    not_empty.notify_all();//唤醒消费者
                    cout<<"生产,当前m="<<m<<endl;
                    this_thread::sleep_for(chrono::seconds(1));
                } 
            }
        private:
            int m=3;//资源
            int maxnumber=5;//资源上限
            mutex m_mutex;//锁
            condition_variable not_empty;//条件变量
            condition_variable not_full;
        };
        int main()
        {
            A a;
            thread t1(&A::xiaofei, &a);
            // thread t1(std::bind(&A::xiaofei, &a));
            thread t2(&A::shengchan, &a);
        
            t1.join();
            t2.join();
        
            return 0;
        }
        
  • 多线程异步操作
    头文件#include <future>

    • promise类
      • 创建promise
        promise<int> pr;
      • 将其作为函数参数传给线程的函数
        thread t1([](promise<int> &p) {
        	//任务函数经过一系列操作,得到结果100,将结果存入promise对象
            p.set_value(100);//这里修改了promise存储的value值,因此需要传引用
        }, ref(pr));//当任务函数参数是引用类型时,这里需要使用ref包起来
        
      • 从promise对象中取出future对象
        future<int> f = pr.get_future();
      • 从future对象中取出子线程任务函数中返回的值
        int value = f.get();//阻塞,直到取得数据
      • 区分set_value 和 set_value_at_thread_exit
        • set_value :在子线程执行期间就把值返回,因此f.get会提前取得数据解除阻塞
        • set_value_at_thread_exit:子线程执行结束后才返回,则主线程是在子线程结束后才能解除f.get阻塞
    • packaged_task
      • 创建packaged_task对象:创建方法类似可调用对象包装器
        packaged_task<int(int)> task([](int x) { return x += 100; });
      • 把packaged_task对象的引用作为任务函数传入线程
        thread t1(ref(task), 100);
      • 从packaged_task对象中取出future对象
        future<int> f = task.get_future();
    • async
      #include <iostream>
      #include <thread>
      #include <future>
      using namespace std;
      
      int main()
      {
          // 1.调用函数直接创建线程执行任务(即使用默认策略launch::async)
          future<int> f = async([](int x) {
              //...
              return x += 100;
          }, 100);
          cout << "子线程返回值: " << f.get() << endl;
      
          //2.使用策略launch::deferred,不创建新线程
          future<int> f2 = async(launch::deferred, [](int x){
              //...
              return x+= 100;
          }, 200);
          cout << "子线程返回值: " << f2.get() << endl;
          return 0;
      }
      
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值