• 读书笔记<高速上手C11 14 17>


    1.编译原理

    Gcc g++ 编译过程

    2.语言可用性的强化

    2.1常量

    1. nullptr

      nullptr 的出现主要是替换NULL,在C语言中NULL表示的是(void*(0)),而在C++ 中表示0,
      C++ 不允许直接将void* 隐式转换到其他类型

    // #define NULL 0
    // 导致C++ 重载特性发生混乱
    void test(char*);
    void test(int);
    test(NULL);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    因此C++ 引入nullptr 区分0 和 空指针, nullptr的类型是nullptr_t 可以隐式转换成任意类型的指针

    #include 
    #include 
    
    using namespace std;
    
    void test(int i){
    	cout <<__FUNCTION__ <<endl;	
    }
    void test(char *){
    	cout <<__FUNCTION__ <<endl;
    }
    int main(){
    	
    	if (is_same<decltype(NULL),decltype(0)>::value)
    		cout << "NULL == 0" <<endl;
    	if (is_same<decltype(NULL),decltype(void*(0))>::value)
    		cout << "NULL == void*(0)" <<endl;
    	if (is_same<decltype(NULL),decltype(nullptr_t)>::value)
    		cout << "NULL == nullptr_t" <<endl;	
    	
    	test(0);
    	test(nullptr);
    	// test(NULL);
    	return 0; 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    1. constexpr

      const 作为常量, 而constexpr 是修饰常量表达式,即在编译前可以确定表达式的值;

    constexpr int getNum(){
    	return 10;
    }
    const int num = 10;
    constexpr int sum = num * 2;
    int g_list[sum]={0};
    const len = num + 2;
    char arr_4 [len]; // 数组的长度必须是一个常量表达式
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.2 变量及其初始化

    1. if/switch 变量声明强化
      if 和 switch 中无法声明一个变量
    // C++ 17 中是允许的
    if ( const auto itr = std::find( vec.begin (), vec.end() , 3); 3 itr != vec . end () ) {
     * itr = 4;
    }
    
    • 1
    • 2
    • 3
    • 4
    1. 初始化列表
      std::initializer_list
      内置类型可以直接使用;
    class MagicFoo {
    public:
       vector<int> m_vec;
       // 初始化列表构造函数
       MagicFoo(initializer_list<int> list) {
           for (auto it = list.begin(); it != list.end(); ++it) {
               m_vec.emplace_back(*it);
           }
       }
    };
    int main()
    {
       MagicFoo magicFoo = { 1,2,3,4,5 };
       MagicFoo magicFoo2{ 1,2,3,4,5 };
       int data{10};
       return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. 结构化绑定
      std::tuple
    // C++17的支持
    #include 
    
    std::tuple<int, double, std::string> f() {
        return std::make_tuple(1, 2.0, "test");
    }
    int main()
    {
        auto [x, y, z] = f();
        cout << x << "-" << y << "-" << z << endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.3 类型推导

    1. auto
      auto 作为静态类型推导,不可以作为函数的参数
    // 在函数的para 有默认值时可以使用
    int test(auto data = 0) {
        return data;
    }
    test(10); 
    
    // auto 还不能用于推导数组类型
     auto arr = new auto (10);
     auto data[20] = arr;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. decltype 推导表达式的类型
    auto x = 10;
    decltype(x) y=20;
    
    
    • 1
    • 2
    • 3
    1. 尾返回类型推导
    // 
    template < typename R , typename T , typename U >
    R add ( T x , U y ) 
     	reurn x+y;
    /*
    	这样的写法并不能通过编译。这是因为在编译器读到 decltype(x+y) 时,x 和 y 尚未被定
    	义
    */
    decltype ( x + y ) add(T x, U y )
    
    // C++14 开始是可以直接让普通函数具备返回值推导
    // 尾返回类型(trailing return type)
    template <typename T , typename U >
    auto add ( T x , U y ) -> decltype(x+y) 
     	reurn x+y;
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. decltype(auto)
      decltype(auto) 主要用于对转发函数或封装的返回类型进行推导,无需显示的指定decltype的参数
    string getString();
    decltype(auto) get_string_test()
    	return getString();
    
    • 1
    • 2
    • 3

    2.4 控制流

    1. if constexpr
    template <typename T>
    auto print_type_info(const T& t) {
        if constexpr (is_integral_v<T>) {
            return t + 1;
        } else {
            return t + 0.01;
        }
    }
    int main()
    {
        cout << print_type_info(1) << endl;    // 2
        cout << print_type_info(1.0) << endl;     // 1.01
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 区间for迭代
    vector<string> t;
    for(string temp : t);
    for(auto temp : t);
    
    • 1
    • 2
    • 3

    2.5 模板

    C++ 模板能够将一切能够在编译器处理的事情在编译器处理,并且模板和泛型编程息息相关

    1. 外部模板
      模板在被使用的才会被编译器实例化,项目中每次使用到的模板在编译器被实例化,会造成代码的编译时间增加,
      C++11 引入外部模板,可以显示的通知编译器进行模板的实例化
    template class vector<bool>; // 强行实例化
    extern template class vector<double>; // 不在当前编译文件中实例化模板 
    
    • 1
    • 2
    1. 尖括号 “>”

    位运算符>>
    前期模板中的 vector 最后的>> 是需要程序员自己增加空格
    C11 之后vector 变得合法化了

    1. 类型别名模板
    // typedef  和 using
    using callback = std::function<void(string,int)>;
    typedef void(*callback)(string,int);
    
    • 1
    • 2
    • 3
    1. 默认模板参数
    template <class T = int, class TT = float>
    auto add(T x, TT y) ->decltype(x+y)
    	return x+y;
    
    • 1
    • 2
    • 3
    1. 变长参数模板
     template < typename Require , typename ... Args > 
     class Magic {
    public:
     	Magic(Args ...args){
    		std :: cout << sizeof ...( args ) << std :: endl ;
    	}	
     }
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5.1 递归模板函数

    template <typename T >
    void printf(T value) {
       std::cout << value << std::endl;
       
    } 
    template < typename T, typename ... Args >
    void printf(T value, Args ... args) {
       std::cout << value << std::endl;
       printf(args ...);
    
    }
    
    int main()
    {
       printf(1, 2, "123 ", 1.1);
       return 0;
    }
    
    // C17
    template < typename T0 , typename ... T >
    void printf ( T0 t0 , T ... t ) {
       std :: cout << t0 << std :: endl ;
       if constexpr ( sizeof ...( t ) > 0) printf ( t ...) ;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    5.2 初始化列表展开

    template < typename T , typename ... Arg >
    void printf ( T t ,  Arg... args ) {
       std :: cout << value << std :: endl ;
       return std :: initializer_list <T >{([&] {
       	std :: cout << args << std :: endl ;
       	}() , value ) ...};
    }
    print (1 , 2.1 , "123 ");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5.3 折叠表达式

    // c17
    template < typename ... T >
    auto sum ( T ... t ) {
       return ( t + ...) ;
    }
    sum (1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.6 面向对象

    1. 委托构造
    // 委托构造 简化代码
    class Base {
    public:
        int m_val;
        int m_data;
        Base() :m_val(m_val) {
        }
        Base(int data) : Base() {
            m_data = data;
        }
        Base(int val, int data) : Base(data) {
            m_val = val;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    1. 继承构造
    // 继承构造
    class Base {
    public:
        int m_val;
        std::string m_data;
        Base() {
            m_val = 1;
        }
        Base(std::string data) :Base() {
            m_data = data;
        }
    };
    
    class Subclass :public Base {
    public:
        using Base::Base;
    
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 显式虚函数重载 override final virtual
    struct Base {
        virtual void foo();
    };
    
    struct SubClass : Base {
        virtual void foo() override;
    };
    
    
    struct  BaseFinal {
    	// 禁止被重载
        virtual void foo() final;
    };
    // 禁止被继承
    struct SubClassFinal final : BaseFinal {
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 显式禁用默认函数
    class Magic {
    public:
        Magic() = default;
        Magic& operator=(const Magic&) = delete;
        Magic(Magic&) = default;
        Magic(Magic&&) = default;
        ~Magic() = default;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    1. 强类型枚举
    enum class new_enum : unsigned int {
        value1,
        value2,
        value3 = 100,
        value4 = 200
    };
    
    template <typename T>
    std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value,
        std::ostream>::type& stream, const T& e) {
    
        return stream << static_cast<typename std::underlying_type<T>::type>(e);
    }
    
    
    int main()
    {
        cout << new_enum::value4 << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3 语言运行期的强化

    3.1 Lambda表达式

    捕获列表 mutable(可选)->返回值类型{ 函数体};
    捕获列表 值捕获= 引用捕获& 变量捕获 空捕获列表
    C14 表达式捕获

       auto important = std::make_unique<int>(1);
       auto add = [v1 = 1, v2 = std::move(important)](int x, int y)->int{
           return x + y + v1 + (*v2);
       };
    
       cout << add(3, 4) << endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    泛型 Lambda(C14)
    auto add = [](auto x,auto y){ return x+y; }

    3.2 函数对象包装器

    lambda 表达式的本质是一个和函数对象类型相似的类类型的对象,当捕获列表为空的时候还可以转换为函数指针进行传递

    1. std::function
    // C++11 std::function 是一种通用、多态的函数封装
    using foo = std::function; // using foo = void(int);
    void functional(foo f) {
        return f(1);
    }
    
    int main()
    {
        auto f = [](int value) {
            cout << value << endl;
        };
    
        functional(f);
        f(1);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    1. std::bind/std::placeholder
    int foo(int a, int b) {
        return a + b;
    }
    
    int main()
    {
        auto bindFoo = std::bind(foo, std::placeholders::_1, 2);
        cout << bindFoo(1) << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. 右值引用
    /*
    左值 右值的纯右值 将亡值 右值
    左值
    	表达式后依然存在的持久对象
    右值
     	表达式结束后不存在的临时对象
    纯右值
        字面量和匿名的临时对象 lambda表达式都是纯右值
    将亡值
        即将被销毁,却能够被移动的值
     */
    void reference(string& str) {
        cout << "左值" << endl;
    }
    void reference(string&& str) {
        cout << "右值" << endl;
    }
    
    int main()
    {
        string lv1 = "string";
        string&& rv1 = std::move(lv1);
        const string& lv2 = lv1 + lv1;
        string&& rv2 = lv1 + lv1;
        reference(rv2);    // 左值
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    4.移动语义

    class A {
    public:
        int* m_ptr;
        A() :m_ptr(new int(1)) {
            cout << "构造" << "  " << m_ptr << endl;
        }
        A(A& a) :m_ptr(new int(*a.m_ptr)) {
            cout << "拷贝" << "  " << m_ptr << endl;
        }
        A(A&& a) :m_ptr(a.m_ptr) {
            cout << "移动" << "  " << m_ptr << endl;
        }
        ~A() {
            cout << "析构" << "  " << m_ptr << endl;
        }
    private:
    
    };
    // 防止编译器优化
    A return_rvalue(bool test) {
        A a, b;
        if (test)
            return a;
        return b;
    }
    
    int main()
    {
        A obj = return_rvalue(false);
        cout << obj.m_ptr << endl;
        cout << *obj.m_ptr << endl;
        return 0;
    }
    
    /*
    *  首先会在 return_rvalue 内部构造两个 A 对象,于是获得两个构造函数的输出;
    *  函数返回后,产生一个将亡值,被 A 的移动构造(A(A&&))引用,从而延长生命周期,并将这个右值
    *  中的指针拿到,保存到了 obj 中,而将亡值的指针被设置为 nullptr,防止了这块内存区域被销毁
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    5.完美转发
    完美转发就是在传递参数的时候保持原来的参数类型

    void reference(int& v) {
        cout << "左值" << endl;
    }
    
    void reference(int&& v) {
        cout << "右值" << endl;
    }
    template <typename T>
    void pass(T&& v) {
        reference(v);
        reference(std::move(v));
        reference(std::forward<T>(v));
        cout << endl << endl;
    }
    int main()
    {
        pass(1);
        int v = 10;
        pass(v);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.容器

    4.1 std::array 和 std::forward_list

    引入arrayforward_list 能够让代码变得更现代化,并且使代码更加安全,同时还能够友好的使用标准库中的容器算法, 使用array 的时候要兼顾C风格的接口

    forward_list 底层是由单向链表进行实现;不提供size接口;

    4.2 无序容器

    有序容器map/set* 这些元素内部通过红黑树进行实现,
    无序容器中的元素是不进行排序的,内部通过Hash 表实现,在不关心容器内部元素顺序时,能够获得显著的性能提升
    unordered_map/unordered_mutimap/unordered_set/unordered_multiset

    4.3 元组 tuple

    元组的三个核心的函数
    1.make_tuple: 构造元组
    2.get : 获得元组某个位置的值
    3.tie: 元组拆包

    #include 
    
    auto getStudent(int id) {
        if (id == 0) {
            return std::make_tuple(3.8, 'A', "张三");
        }
        if (id == 1) {
            return std::make_tuple(4.8, 'B', "李四");
        }
        return std::make_tuple(0.0, 'N', "null");
    };
    
    int main()
    {
    
        auto student = getStudent(0);
        cout << "data: "
            << "  GPA: " << get<0>(student)
            << "  成绩: " << get<1>(student)
            << "  姓名: " << get<1>(student)
            << endl;
        double gpa;
        char grade;
        string name;
    
        std::tie(gpa, grade, name) = getStudent(1);
        cout << "data: "
            << "  GPA: " << gpa
            << "  成绩: " << grade
            << "  姓名: " << name
            << endl;
        return 0;
    
    
    }
    
    // 合并元组
    auto newTuple = tuple_cat(getStudent(1),move(t));
    // 遍历元组
    template <typename T>
    auto tupleLen(T &tpl){
    	return std::tuple_size<T>::value;
    }
    
    for(int i=0;i!=tupleLen(newTuple);++i){
    	cout << tuple_index(i,newTuple)<<endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    5.指针

    5.1 RAII 与引用计数

    • 引用计数是为了防止内存泄漏,
    • 在构造函数中申请内存,在析构函数中释放,也就是RAII 资源获取即初始化
    • C11引入智能指针,使用引用计数的想法,让程序中不在需要关注手动释放内存

    5.2 shared_ptr

    shared_ptr 内部使用引用计数,当引用计数为0的时候,就会将对象自动删除
    // 初始化的两种方式
    std::shared_ptrstd::string s1(new string(“123”));
    std::shared_ptrstd::string s2 = std::make_sharedstd::string(“1234”);
    // 获取对象的原始指针
    string* origin = s2.get();
    // 减少引用计数
    s2.reset();
    // 获取对象被引用的个数
    s2.use_count();

    5.3 unique_ptr

    //std::unique_ptr是一种独占的智能指针,禁止其他智能指针与其共享同一个对象,从而保证代码的安全
    std::unique_ptr<int> p = std::make_unique<int>(10);      // c14
    // 移动权限
    auto data = std::move(p);
    // make_unique_ptr 实现机制
    template <typename T, typename ...Args>
    std::unique_ptr<T> my_make_unique(Args&& ...args) {
        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.4 weak_ptr

    // shared_ptr 循环引用问题
    // 循环引用
    
    struct A;
    struct B;
    
    struct B {
        using Ptr = std::shared_ptr<B>;
        A::Ptr _ptr;
        ~B() {
            cout << "~B()" << endl;
        }
    };
    
    struct A {
        using Ptr = std::shared_ptr<A>;
        B::Ptr _ptr;
        ~A() {
            cout << "~A()" << endl;
        }
    };
    
    int main()
    {
    
        A::Ptr a = std::make_shared<A>();
        B::Ptr b = std::make_shared<B>();
        a->_ptr = b;
        b->_ptr = a;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    在这里插入图片描述
    // 解决这个问题的办法就是使用弱引用指针std::weak_ptr,弱引用不会引起引用计数增加
    在这里插入图片描述

    6.正则表达式

    6.1 正则表达式简介

    普通字符
    特殊字符
    限定符

    6.2 std::regex 及其相关

    7.线程与并发

    7.1 thread

    #include 
    void foo() {
        cout << "test thread" << endl;
    }
    
    int main()
    {
        std::thread t(foo);
        t.join();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    7.2 std::mutex, std::unique_lock

    #include 
    #include 
    
    #define LOCK_GURAD(x) std::lock_guard<decltype<x>> lk(x)
    
    // 发生异常 锁没有被释放
    void someOperator(const string& message) {
        static std::mutex mtx;
        LOCK_GURAD(mtx);
    }
    // unique_lock 
    
    std::mutex mtx;
    void block_area() {
        std::unique_lock<std::mutex> lock(mtx);
        
        lock.lock();
        lock.unlock();
    }
    
    
    int main()
    {
        std::thread t(block_area);
        t.join();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    7.3 std::future, std::packaged_task

    /*
    * std::future 提供了一个访问异步操作结果的途径;
    * 问题: 试想我们的主线程A 希望开辟一个线程B 去执行某个我们预期的任务,并返回一个结果
    * 解决方案: 线程A 中启动任务B,执行完成后发送一个事件执行结果保存在某个全局变量中,当线程A
    *   想要执行结果时,调用一个线程等待的函数获取结果
    * 
    * std::future简化此流程,可以用来获取异步任务的结果,是一种线程同步的方法
    * std::packaged_task 可以用来封装任何可以调用的目标,从而实现异步调用
    * 
    */
    
    
    int main()
    {
        
        std::packaged_task<int()> task([]() {return 7; });
        std::future<int> result = task.get_future();
        std::thread(std::move(task)).detach();
        std::cout << "waiting...";
        result.wait();
        cout << "Done :" << result.get() << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    7.4 std::condition_variable

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    /*
     std::condition_variable 是为了解决死锁而生的,当互斥锁的引入而不够实现场景功能时
     问题: 线程A 可能等待某个条件为真才能继续执行,而一个忙等待循环中可能会导致其他所有线程都
        无法进入临界区使条件为真就会发送死锁,
     解决方法: condition_variable实例被创建出现主要就是用于唤醒等待线程从而避免死锁
     std::condition_variable的notify_one和notify_all 就是为了解决此问题
    
    */
    // 
    
    
    int main()
    {
        // 生产者的数量
        std::queue<int> producedNums;
        std::mutex m;
        std::condition_variable condVar;
        // 结束标志
        bool done = false;
        // 通知标志
        bool notified = false;
    
    
        // 生产者线程
        std::thread producer([&] {
            for (size_t i = 0; i < 5; ++i) {
                std::this_thread::sleep_for(std::chrono::seconds(1));
                std::unique_lock<std::mutex> lk(m);
                cout << "producing  " << i << endl;
                producedNums.emplace(i);
                notified = true;
                condVar.notify_one();
                
            }
            done = true;
            notified = true;
            condVar.notify_one();
            });
        
        // 消费者线程
        std::thread consumer([&]() {
            std::unique_lock<std::mutex> lk(m);
            while (!done) {
            
                while (!notified) {
                    // 循环避免虚假唤醒
                    condVar.wait(lk);
                    
                }
                while (!producedNums.empty()) {
                    cout << " cosuming   " << producedNums.front() << endl;
                    producedNums.pop();
                }
                notified = false;
            }
            });
    
        producer.join();
        consumer.join();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    8. 文件系统

    8.1 std::filesystem

    9. 其他

    9.1 新类型

    cout << sizeof(long long int) << endl; // 8

    9.2 noexcept 的修饰和操作

    // noexcept 修饰完一个函数之后能够起到封锁异常扩散的功效,如果内部产生异常,外部也不会触发
    void divid(int a,int b) {
        if (b == 0)
            throw std::invalid_argument("0");
    }
    
    void test() noexcept {
        return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    9.3 字面量

    1. 原始字符串字面量 去除转义限制
      std::string str = R"(C\User\Desktop)";
    2. 自定义字面量
    /*
        
      自定义字面量支持四种字面量
      * 整形字面量:重载时必须使用 unsigned long long、const char *、模板字面量算符参数
      * 浮点型字面量:重载时必须使用 long double、const char *、模板字面量算符
      * 字符串字面量:必须使用 (const char *, size_t) 形式的参数表
      * 字符字面量: 参数只能是 char, wchar_t, char16_t, char32_t 这几种类型
    
    */
    
    std::string operator"" _wow1(const char* wow1, size_t len) {
        return std::string(wow1) + "wooooooow, amazing";
    }
    std::string operator"" _wow2(unsigned long long i) {
        return std::to_string(i) + "wooooooow, amazing";
    }
    
    int main()
    {
        auto str = "abc"_wow1;
        auto num = 1_wow2;
        cout << str << endl;
        cout << num << endl;
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    C20

  • 相关阅读:
    C++中的多态和虚函数及多态原理
    【Go语言】Gin 框架教程
    Pandas中Concat与Append_Python数据分析与可视化
    数据标注行业中的“睁一只眼闭一只眼”
    一文概览NLP句法分析:从理论到PyTorch实战解读
    海外服务器AS4837和AS4134S线路含义?怎么测试?
    flask和fastapi的区别以及demo实现
    Linux内嵌汇编
    vue3基础语法(二)
    基于Raft解决“幽灵复现”问题
  • 原文地址:https://blog.csdn.net/guuci/article/details/126633234