• C++11一些零碎的知识点介绍


    目录

    一.列表初始化

    1.{}初始化

    2.initializer_list

    二.变量类似推导(声明)

    1.auto

    2.decltype

    3.nullptr

    三.范围for

    四.STL容器新增

    1.unordered_set和unordered_map

    2.array

    3.forward_list 

    五.可变参数模板

    1.递归函数方式展开参数包

    2.逗号表达式展开参数包

    3.STL中的emplace


    前言:这里都是C++11的一些相对比较零碎或者简单的知识点,而一些重要的知识点(比如右值引用、lambda表达式、智能指针等之后再介绍)

    C++11第2篇:C++11重要知识点介绍_糖果雨滴a的博客-CSDN博客

    一.列表初始化

    1.{}初始化

            在C++98中,可以用{}对数组或者结构体元素进行统一的列表初始值设定比如:

    1. struct Point
    2. {
    3. int _x;
    4. int _y;
    5. };
    6. int main()
    7. {
    8. Point p = { 1, 2 };
    9. int array1[]{ 1, 2, 3, 4, 5 };
    10. int array2[5]{ 0 }
    11. return 0;
    12. }

            C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。

    1. struct Point
    2. {
    3. int _x;
    4. int _y;
    5. };
    6. int main()
    7. {
    8. int x1 = 1;
    9. int x2{ 2 };
    10. int array1[]{ 1, 2, 3, 4, 5 };
    11. int array2[5]{ 0 };
    12. Point p{ 1, 2 };
    13. // C++11中列表初始化也可以适用于new表达式中
    14. int* pa = new int[4]{ 0 };
    15. return 0;
    16. }

            而C++11支持这一特性,最主要的是可以应用在各种容器中,例如:

    1. int main()
    2. {
    3. vector<int> v = { 1,2,3,4 };
    4. list<int> lt = { 1,2 };
    5. // 这里{}会先初始化构造一个pair对象
    6. map dict = { {"erase", "删除"}, {"insert", "插入"} };
    7. // 使用大括号对容器赋值
    8. v = {10, 20, 30};
    9. return 0;
    10. }

    2.initializer_list

            这个就是容器可以使用{}进行赋值的C++11中新增加的{}的类型。

            这个一般是作为构造函数的参数,C++11对STL中的不少容器都增加了intializer_list作为参数的构造函数,这样就初始化容器对象更方便了,也可以作为operator=(赋值运算符重载)的参数。

            让自己模拟实现的vector也支持{}初始化和赋值:

    1. namespace hb
    2. {
    3. template<class T>
    4. class vector {
    5. public:
    6. typedef T* iterator;
    7. vector(initializer_list l)
    8. {
    9. _start = new T[l.size()];
    10. _finish = _start + l.size();
    11. _endofstorage = _start + l.size();
    12. iterator vit = _start;
    13. for (auto e : l)
    14. *vit++ = e;
    15. }
    16. vector& operator=(initializer_list l) {
    17. vector tmp(l);
    18. std::swap(_start, tmp._start);
    19. std::swap(_finish, tmp._finish);
    20. std::swap(_endofstorage, tmp._endofstorage);
    21. return *this;
    22. }
    23. private:
    24. iterator _start;
    25. iterator _finish;
    26. iterator _endofstorage;
    27. };
    28. }

    二.变量类似推导(声明)

    1.auto

            用于实现自动类型推断。要求必须进行显示初始化,让编译器将定义对象的类型设置位初始化值的类型。

    1. int a = 10;
    2. auto p = &a;

    2.decltype

            可以将变量的类型声明为表达式指定的类型。

    1. int x = 1;
    2. double y = 2.2;
    3. decltype(x * y) ret; // ret的类型就是double

    3.nullptr

            NULL被定义成的是字面量0,这样可能会带来一些问题,因为0既能表示指针常量,又能表示整型常量。因此为了更清晰和安全的角度考虑,C++11新增了nullptr,用来表示空指针。

    三.范围for

            范围for是一种遍历的方式,并且代码很简洁,底层是基于STL的迭代器。只要那个容器支持迭代器,就一定支持范围for。

    1. int main()
    2. {
    3. vector<int> v{ 1, 2, 3, 4, 5 };
    4. for(auto e : v)
    5. {
    6. cout << e << ' ';
    7. }
    8. return 0;
    9. }

    四.STL容器新增

    1.unordered_set和unordered_map

            这两个基于哈希表实现的容器是C++11中新出的两个很有用处的容器,具体使用已经在前两个博客中介绍了。

            C++ 哈希表及unordered_set + unordered_map容器_糖果雨滴a的博客-CSDN博客

    2.array

            这个新增容器用处不是很大,就是一个静态数组。不过相对于正常创建的静态数组,使用这个容器访问会更加安全,一旦越界就会报错。不过vector同样越界会报错,因此我们一般使用的都是vector,很少会用array。

    3.forward_list 

            这个新增容器也是如此,用处并不大。forward_list就是单链表,而list是双链表,forward_list只不过比list要少存储一个指针,能减少一点存储占用。

    五.可变参数模板

    1. // Args是一个模板参数包,args是一个函数形参参数包
    2. // 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
    3. template <class ...Args>
    4. void ShowList(Args... args)
    5. {}

            上面的参数args前面有省略号,所以它就是一个可变模板参数,我们把带省略号的参数称为”参数包“,它里面包含了0到N个模板参数。

            我们无法直接获取参数包args中的每个参数,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模板参数的一个主要特点。因此如何展开可变模板参数是最大的难点。

            语法是不支持使用args[i]这样方式获取可变参数的。

    因此我们有这样几种方法:

    1.递归函数方式展开参数包

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

            这个就是通过递归的方式,每次让参数包中的第一个类型进入模板T中,然后剩下的参数进入参数包中,以进行下一次递归,而进入模板T中的就可以得到了。而如果只剩下一个参数时,这时这个参数会进入T中,而参数包就变成了0,如果不去实现上面的那个只有T的函数,就会导致报错,因为参数包中已经没有参数了,无法进行递归了。

            

    1. void ShowList()
    2. {}

            上面那个函数也可以写成这样,这时当参数包为0时再次递归就会进入到这个函数,也可以结束。

    2.逗号表达式展开参数包

    1. template <class T>
    2. void PrintArg(T t)
    3. {
    4. cout << t << " ";
    5. }
    6. //展开函数
    7. template <class ...Args>
    8. void ShowList(Args... args)
    9. {
    10. int arr[] = { (PrintArg(args), 0)... };
    11. cout << endl;
    12. }
    13. int main()
    14. {
    15. ShowList(1);
    16. ShowList(1, 'A');
    17. ShowList(1, 'A', std::string("sort"));
    18. return 0;
    19. }

            这种方式是通过PrintArg函数,先进入到函数中,得到每一个参数包中的值,然后因为我们不知道参数包中的类型是什么,所以我们通过逗号表达式的方法,将每一个数都变成0,然后存入arr中,这样就不会因为不知道类型而直接将值存入数组导致报错。

            也可以写成这样:

    1. template <class T>
    2. void PrintArg(T t)
    3. {
    4. cout << t << " ";
    5. return 0;
    6. }
    7. //展开函数
    8. template <class ...Args>
    9. void ShowList(Args... args)
    10. {
    11. int arr[] = { PrintArg(args)... };
    12. cout << endl;
    13. }
    14. int main()
    15. {
    16. ShowList(1);
    17. ShowList(1, 'A');
    18. ShowList(1, 'A', std::string("sort"));
    19. return 0;
    20. }

            这里可以不用逗号表达式,而是让上面的函数直接返回0。

    3.STL中的emplace

    1. template <class... Args>
    2. void emplace_back (Args&&... args);

            因为有了参数包和万能引用(万能引用在接下来的右值引用中介绍)等,就有了emplace的出现。而相对于原本的push_back,insert等,emplace的接口又有什么优势呢?

            emplace系列的接口对于右值,是直接构造,而push_back则需要先构造,再进行移动构造。

            emplace的效率要稍微高那么一点,不过并不是很大。而用法上几乎一样。

    1. int main()
    2. {
    3. std::list< std::pair<int, bit::string> > mylist;
    4. mylist.emplace_back(10, "sort");
    5. mylist.emplace_back(make_pair(20, "sort"));
    6. mylist.push_back(make_pair(30, "sort"));
    7. mylist.push_back({ 40, "sort"});
    8. return 0;
    9. }
  • 相关阅读:
    会员营销中,数字会员模式如何打造差异化会员服务
    Windows C++ 使用WinAPI实现RPC
    【深入浅出Spring原理及实战】「源码调试分析」深入源码探索Spring底层框架的的refresh方法所出现的问题和异常
    JS 数组操作的利器:splice() 和 slice() 方法详解
    MySQL多表查询操作
    springmvc-websocket 403 错误
    RHCSA-Day2 --- 命令、目录结构、vi编辑器
    中间件环境搭建配置过程解读
    关于运行flutter app 运行到模拟器出现异常提示
    大数据学习问题记录
  • 原文地址:https://blog.csdn.net/qq_60750110/article/details/126958459