• (C++进阶) C++11


    目录

    一 列表初始化

    二 变量类型推导

    三 右值引用

    1 右值引用概念

    1.1 左值与右值

    1.2 右值引用引用左值

     1.3 完美转发

    1.4 总结

    四  lambda表达式

    1、lambda表达式书写格式

    2、lambda表达式各部分说明

    3、lambda表达式的简单使用

    五  可变参数模板的使用

    1、示例一

    2、示例二

    六 包装器

    七 绑定

    八 线程库

    1、thread类

    2、RAII

    3、两个线程交替打印1-100


    一 列表初始化

    1、在C++98中,标准允许使用花括号{}对数组元素进行统一的列表初始值设定。对于一些自定义的类型,却无法使用这样的初始化。C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加

    2、多个对象想要支持列表初始化,需给该类(模板类)添加一个带有initializer_list类型参数的构造函数即。注意:initializer_list是系统自定义的类模板,该类模板中主要有三个方法:begin()end()迭代器以及获取区间中元素个数的方法size()

    1. int main()
    2. {
    3. //initializer_list,主要用于容器初始化
    4. auto il1 = { 10,50,90 };
    5. std::initializer_list<int> il2 = { 10,50,80 };
    6. cout << typeid(il1).name() << endl;
    7. vector<int> v = {1,2,3,4,5};
    8. list<int> lt = { 1,2,3,4,5 };
    9. mapint> m = { make_pair("sort",1),{"insert",2} };
    10. return 0;
    11. }

    二 变量类型推导

    1、C++11中,可以使用auto来根据变量初始化表达式类型推导变量的实际类型,可以给程序的书写提供许多方便。auto使用的前提是:必须要对auto声明的类型进行初始化,否则编译器无法推导出auto的实际类型decltype是根据表达式的实际类型推演出定义变量时所用的类型。

    1. int main()
    2. {
    3. auto pf = {1,2,3,4};
    4. decltype(pf) px; //创建一个和pf类型一致的变量
    5. cout <<"pf: " << typeid(pf).name() << endl;
    6. cout <<"px: " << typeid(px).name() << endl;
    7. int x = 1;
    8. double y = 1.0;
    9. //自动推导类型
    10. decltype(x*y) ret;
    11. cout << typeid(ret).name() << endl;
    12. return 0;
    13. }

    三 右值引用

    1 右值引用概念

    C++98中提出了引用的概念,引用即别名,引用变量与其引用实体公共同一块内存空间,而引用的底层是通过指针来实现的,因此使用引用,可以提高程序的可读性。 为了提高程序运行效率C++11中引入了右值引用 ,右值引用也是别名,但其只能对右值引用。

    1.1 左值与右值

    左值与右值是 C 语言中的概念,但 C 标准并没有给出严格的区分方式,一般认为: 可以放在 = 左边的,或者能 够取地址的称为左值,只能放在 = 右边的,或者不能取地址的称为右值 ,但是也不一定完全正确。
    (1)  不能简单地通过能否放在 = 左侧右侧或者取地址来判断左值或者右值,要根据表达式结果或变量的性质判断,比如上述:c 常量
    (2) 能得到引用的表达式一定能够作为引用,否则就用常引用。
    (3)C++11对右值进行了严格的区分 C 语言中的纯右值,比如: a+b, 100
    将亡值。比如:表达式的中间结果、函数按照值的方式进行返回。

    1.2 右值引用引用左值

    C++11 中, std::move() 函数 ,它 并不搬移任何东西 ,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义使用move后,原来的左值会被置空。

     1.3 完美转发

           完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数 所谓完美: 函数模板在向其他函数传递自身形参时,如果相应实参是左值,它就应该被转发为左值;如果相 应实参是右值,它就应该被转发为右值 。这样做是为了保留在其他函数针对转发而来的参数的左右值属性进 行不同处理(比如参数为左值时实施拷贝语义;参数为右值时实施移动语义)。
    1. void Fun(int& x) { cout << "左值引用" << endl; }
    2. void Fun(int&& x) { cout << "右值引用" << endl; }
    3. void Fun(const int& x) { cout << "const 左值引用" << endl; }
    4. void Fun(const int&& x) { cout << "const 右值引用" << endl; }
    5. template<typename T>
    6. void PerfectForward(T&& t)
    7. {
    8. Fun(std::forward(t));
    9. }
    10. int main()
    11. {
    12. PerfectForward(10);
    13. int a;
    14. PerfectForward(a);
    15. PerfectForward(std::move(a));
    16. const int b = 10;
    17. PerfectForward(b);
    18. PerfectForward(std::move(b));
    19. return 0;
    20. }

    1.4 总结

    右值引用出来以后,并不是直接使用右值引用去减少拷贝,提高效率。而是支持深拷贝的类,提供移动构造和移动赋值这时这些类的对象进行传值返回或者是参数为右值时,则可以用移动构造和移动赋值,转移资源,避免深拷贝,提高效率。
     

    1. int f(int i, int j)
    2. {
    3. return i + j;
    4. }
    5. //左值引用只能引用左值,不能引用右值(const左值可以引用右值)
    6. //右值引用不可以直接引用左值,可以引用move后的左值
    7. //左值引用使用场景:做参数、做返回值
    8. //移动构造
    9. //string(string&& s)
    10. //{
    11. // : _str(nullptr)
    12. // , _size(0);
    13. // , _capacity(0);
    14. // tihs->swap(s);
    15. //}
    16. //移动赋值
    17. //string& operator=(string&& s)
    18. //{
    19. // tihs->swap(s);
    20. // return *this;
    21. //}
    22. int main()
    23. {
    24. auto pf = { 1,2,3,4 };
    25. decltype(pf) px;
    26. int x = 1;
    27. double y = 1.0;
    28. //右值引用
    29. int&& rr1 = 100;
    30. int&& rr2 = x * y;
    31. int&& rr3 = f(x, y);
    32. int a = x;
    33. //int&& rr4 = x;
    34. int&& rr4 = std::move(x);
    35. return 0;
    36. }

    四  lambda表达式

    1、lambda表达式书写格式

    [capture-list] (parameters) mutable -> return-type { statement }
    lambda表达式底层被编译器转换成了仿函数

    2、lambda表达式各部分说明

    (1)  捕捉列表 ,该列表总是出现在 lambda 函数的开始位置, 编译器根据 [] 来判断接下来
    的代码是否为 lambda 函数 捕捉列表能够捕捉上下文中的变量供 lambda 函数使用
    (2)(parameters) :参数列表。与 普通函数的参数列表一致 ,如果不需要参数传递,则可以连同 () 一起省略
    (3)mutable :默认情况下, lambda 函数总是一个 const 函数, mutable 可以取消其常量性。使用该修饰符时,参数列表不可省略( 即使参数为空 )
    (4)->returntype :返回值类型 。用 追踪返回类型形式声明函数的返回值类型 ,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导
    (5){statement} :函数体 。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

    3、lambda表达式的简单使用

      lambda 函数定义中, 参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空
    因此 C++11 最简单的 lambda 函数为: []{} ; lambda 函数不能做任何事情。
    1. /*int main()
    2. {
    3. int a = 10, b = 20;
    4. //lambda表达式
    5. auto add = [](int x, int y)->int {return x + y; };
    6. cout << add(a,b) << endl;
    7. auto sub = [a, b]()->int {return a - b; };
    8. cout << sub() << endl;
    9. return 0;
    10. }*/
    11. int main()
    12. {
    13. int a = 10, b = 20;
    14. //标准写法
    15. //auto swap = [](int& x, int& y)->void {
    16. // int temp = x;
    17. // x = y;
    18. // y = temp;
    19. //};
    20. //swap(a,b);
    21. //利用捕捉列表(省略参数和返回值) 注意!!!!使用mutable后参数就算为空也不能省略
    22. //auto swap = [&a,&b]{
    23. // int temp = a;
    24. // a = b;
    25. // b = temp;
    26. //};
    27. //swap();
    28. //全捕捉
    29. auto swap = [&] {
    30. int temp = a;
    31. a = b;
    32. b = temp;
    33. };
    34. swap();
    35. return 0;
    36. }

    五  可变参数模板的使用

    1、示例一

    1. //终止函数
    2. void showListArg(){
    3. cout << endl;
    4. }
    5. //展开函数
    6. template <class T,class ...Args>
    7. void showListArg(T val,Args... args){
    8. cout << val << endl;
    9. showListArg(args...);
    10. }
    11. template <class ...Args>
    12. void showList(Args... args){
    13. showListArg(args...);
    14. }
    15. int main()
    16. {
    17. showList();
    18. showList(1,'A');
    19. showList(1, 'A', string("sort"));
    20. return 0;
    21. }

    2、示例二

    1. //终止函数
    2. void showList() {
    3. cout << endl;
    4. }
    5. //展开函数
    6. template <class T>
    7. void printArg(T val) {
    8. cout << val << endl;
    9. }
    10. template <class ...Args>
    11. void showList(Args... args) {
    12. int arr[] = {(printArg(args),0)...};
    13. cout << endl;
    14. }
    15. int main()
    16. {
    17. showList();
    18. showList(1,2,3,4);
    19. showList(1, 2,3,4,5,6,7,8);
    20. return 0;
    21. }

    六 包装器

    1. int f1(int a, int b)
    2. {
    3. return a + b;
    4. }
    5. struct Functor1
    6. {
    7. public:
    8. int operator() (int a, int b)
    9. {
    10. return a + b;
    11. }
    12. };
    13. class Plus
    14. {
    15. public:
    16. static int plusi(int a, int b)
    17. {
    18. return a + b;
    19. }
    20. double plusd(double a, double b)
    21. {
    22. return a + b;
    23. }
    24. };
    25. int main()
    26. {
    27. // 包装函数指针
    28. std::function<int(int, int)> ff1 = f1;
    29. cout << ff1(1, 2) << endl;
    30. // 包装仿函数
    31. std::function<int(int, int)> ff2 = Functor1();
    32. cout << ff2(1, 2) << endl;
    33. // 包装成员函数
    34. std::function<int(int, int)> ff3 = Plus::plusi;
    35. cout << ff3(1, 2) << endl;
    36. //std::function ff4 = &Plus::plusd;
    37. //cout << ff4(Plus(),1.1, 2.2) << endl;
    38. // 包装lambda表达式
    39. auto f5 = [](int a, int b){return a + b; };
    40. std::function<int(int, int)> ff5 = f5;
    41. cout << ff5(1, 2) << endl;
    42. }

    七 绑定

    1. //bind
    2. int Plus(int a, int b)
    3. {
    4. return a + b;
    5. }
    6. class Sub
    7. {
    8. public:
    9. int sub(int a, int b)
    10. {
    11. return a - b;
    12. }
    13. };
    14. int main()
    15. {
    16. std::function<int(int, int)> f1 = bind(Plus, placeholders::_1, placeholders::_2);
    17. cout << f1(1, 2) << endl;
    18. // 想把plus绑定成一个值+10
    19. std::function<int(int)> f2 = bind(Plus, 10, placeholders::_1);
    20. cout << f2(5) << endl;
    21. // 绑定固定的可调用对象
    22. std::function<int(int, int)> f3 = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);
    23. cout << f3(1, 2) << endl;
    24. std::function<int(int, int)> f4 = bind(&Sub::sub, Sub(), placeholders::_2, placeholders::_1);
    25. cout << f4(1, 2) << endl;
    26. return 0;
    27. }

    八 线程库

    1、thread

    1.1  线程对象可以关联一个线程,用来控制线程以及获取线程的状态
    1.2  当创建一个线程对象后,没有提供线程函数,该对象实际没有对应任何线程。

    1. //多线程(写法有问题,不推荐这样用)
    2. int main()
    3. {
    4. int n = 0;
    5. cin >> n;
    6. vector vThreads;
    7. vThreads.resize(n);
    8. mutex mtx;
    9. int N = 100;
    10. int x = 0;
    11. for (auto& th : vThreads)
    12. {
    13. th = thread([&mtx, &N, &x]
    14. {
    15. for (int i = 0; i < N; i++)
    16. {
    17. mtx.lock();
    18. cout << this_thread::get_id() << ":" << x << endl;
    19. ++x;
    20. this_thread::sleep_for(chrono::milliseconds(500));//500ms
    21. mtx.unlock();
    22. }
    23. });
    24. }
    25. for (auto& th : vThreads)
    26. {
    27. th.join();
    28. }
    29. return 0;
    30. }

    2、RAII

    1. // RAII
    2. namespace zsd
    3. {
    4. template<class Lock>
    5. class lock_guard
    6. {
    7. public:
    8. lock_guard(Lock& lock)
    9. :_lock(lock)
    10. {
    11. _lock.lock();
    12. cout << "加锁" << endl;
    13. }
    14. ~lock_guard()
    15. {
    16. _lock.unlock();
    17. cout << "解锁" << endl;
    18. }
    19. lock_guard(const lock_guard& lock) = delete;
    20. private:
    21. Lock& _lock;
    22. };
    23. }
    24. int main()
    25. {
    26. mutex mtx;
    27. {
    28. zsd::lock_guard lg(mtx);
    29. }
    30. printf("sssss");
    31. return 0;
    32. }

    3、两个线程交替打印1-100

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. //int main()
    7. //{
    8. // mutex mtx;
    9. // int n = 100;
    10. // //如果线程二迟迟没有创建好或者没有排到时间片,就会导致t1连续打印奇数,不符合交替打印要求
    11. // thread t1([&]()
    12. // {
    13. // int i = 1;
    14. // for (; i < n; i += 2)
    15. // {
    16. // unique_lock lock(mtx);
    17. // cout << i << endl;
    18. // }
    19. // });
    20. //
    21. // thread t2([&]()
    22. // {
    23. // int j = 2;
    24. // for (; j < n; j += 2)
    25. // {
    26. // unique_lock lock(mtx);
    27. // cout << j << endl;
    28. // }
    29. // });
    30. //
    31. // t1.join();
    32. // t2.join();
    33. //
    34. // return 0;
    35. //}
    36. int main()
    37. {
    38. mutex mtx;
    39. condition_variable cv;
    40. int n = 100;
    41. bool flag = true;
    42. //打印奇数
    43. thread t1([&]()
    44. {
    45. int i = 1;
    46. for (; i < n;)
    47. {
    48. unique_lock lock(mtx);
    49. cv.wait(lock, [&flag]()->bool {return flag; });
    50. cout << i << endl;
    51. i += 2;
    52. flag = false;
    53. cv.notify_one();
    54. }
    55. });
    56. //打印偶数
    57. thread t2([&]()
    58. {
    59. int j = 2;
    60. for (; j <= n; )
    61. {
    62. unique_lock lock(mtx);
    63. cv.wait(lock, [&flag]()->bool {return !flag; });
    64. cout << j << endl;
    65. j += 2;
    66. flag = true;
    67. cv.notify_one();
    68. }
    69. });
    70. t1.join();
    71. t2.join();
    72. return 0;
    73. }

  • 相关阅读:
    【Java成王之路】EE初阶第十五篇:(网络原理) 5
    Win10远程桌面连接黑屏
    AGI 远不止 ChatGPT!一文入门 AGI 通识及应用开发
    【LeetCode第362场周赛】8020.字符串转换 | 推导+矩阵快速幂+KMP | 困难
    Spring中各个jar包的作用
    二战字节跳动成功上岸,准备了小半年,要个28k应该不过分吧~
    Linux 串口应用编程
    CSP-J1 CSP-S1 第1轮 初赛 考前强化训练
    js中new做了什么
    深入浅出PyTorch中的nn.CrossEntropyLoss
  • 原文地址:https://blog.csdn.net/qq_57594025/article/details/126331743