• C++11 - 1 - Initializer_list


    c++

    前言:

    Vue框架:从项目学Vue
    OJ算法系列:神机百炼 - 算法详解
    Linux操作系统:风后奇门 - linux

    列表初始化:

    • C++11引入了通过{}初始化变量,包括内置类型和自定义类以及STL。本质其实是通过新增的STL容器initializer_list新增了许多STL的构造函数以及编译器为内置类型赋值。

    一,用法:

    内置类型:

    • 四种选择:=,= {},{},指针初始化
    int a = 1;			//c++98
    int a = {1};		//c++11
    int a{1};			//c++11
    
    int *p = new int(5);	//98
    int *p = new int{5};	//11
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    内置类型数组:

    • 三种选择:= {},{},new
    //未规定数组长度:
    int arr1[] = {1, 2, 3, 4, 5};	//98
    int arr1{1, 2, 3, 4, 5};		//11
    
    //规定数组长度,未初始化者均为0
    int arr2[5] = {0};
    int arr2[5]{0};
    
    //指针初始化
    int *p = new int[5]{1, 2, 3, 4};	//11
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    结构体/类:

    • 三种初始化:构造函数,{}拷贝构造,={}拷贝构造
    struct Point{
    	int x, y;
    	Point(int _x, int _y){
    		x = _x;
    		y = _y;
    	};
    };
    
    Point p1(1, 1);
    Point p2 = {1, 1};
    Point p3{1, 1};
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结构体/类数组

    • 一种构造函数:{{}}
    struct Point{
    	int x, y;
    	Point(int _x, int _y){
    		x = _x;
    		y = _y;
    	};
    };
    
    Point *p = new Point[3]{{1,1}, {2,2}, {3,3}};
    //注意,内部小括号不可以省略为{1, 1, 2, 2, 3, 3}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    二,隐式类型转化:

    内置类型转化:

    整型提升:
    提升到int:
    • cpu对于整型的加法器是基于int的32位进行的,

      任何大小小于int的类型变量在与int运算时,

      会先发生整型提示,先提升到32位,再进入加法器运算

    char a = 1;
    short b = 2;
    int c = 3;
    
    cout<< (a+c) << (b+c) <<endl;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    提升到浮点数:
    • 另一方面,当int等整型与精度更高的浮点型数运算时

      整型会首先被转换为浮点型的x.000000

      再携带小数位与浮点数float / double进行计算

    int a = 5;
    float b = 2;			//1,赋值时临时变量int 2被提升为float型
    cout<< (a/b)<< endl;	//2,计算时a先被提升为float型,存储在临时变量中
    
    • 1
    • 2
    • 3
    整型截断:
    • 整型提升是编译器隐式帮我们处理的,整型截断也是
    • 当我们为变量赋的值超过其内存可容纳范围时,发生整型截断:
    char a = 190;
    //signed char的范围为 -2^7 ~ 2^7-1
    /*
    整型截断过程:
    190源码:0000 0000 0000 0000 0000 0000 1011 1110
    190补码:0000 0000 0000 0000 0000 0000 1011 1110
    截断后8位赋予变量a
    a补码:1011 1110
    a源码:1100 0010
    a真实值:-66
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    隐式拷贝构造

    • 上述的整型提升/整型截断属于编译器隐式处理,但是这和cpp中的类/c++11中我们今天将的Initializer_list关系不大,下面来看看类的隐式拷贝构造
    • 创建一个自定义了构造函数和拷贝构造的类:
    class A{
    	private:
    		int a;
    	public:
    		A(int _a){a = _a}
    		void operator=(A _a){
    			a = _a.a;
    		}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 分析两种初始化对象写法调用的是哪种构造函数:
    A a1(1);		//直接构造
    A a2 = 2;		//先通过值2直接构造一个A对象,再通过a2的拷贝构造复制该对象值
    A a3{2};		//先通过值2直接构造一个A对象,再通过a3的拷贝构造复制该对象值
    
    • 1
    • 2
    • 3
    • 也就是说,两种写法= {},都调用了两种构造函数,借助了临时变量完成拷贝构造
    • 常见的隐式构造:
    string s("hello");		//直接构造
    string s = "world";		//隐式:直接构造+拷贝构造
    string s{"world"};		//隐式:直接构造+拷贝构造
    
    • 1
    • 2
    • 3

    三,Initializer_list:

    背景&定义:

    背景:
    • 98中的STL缺陷:vector<>为什么不能像数组一样实现不定长初始化?
    • c++98中vector初始化的四种方式:
    vector<int> v1;					//默认初始化
    
    vector<int> v2(v1);				//拷贝已有的vector
    
    int a[] = {1, 2, 3, 4, 5};
    vector<int> v3(a, a+sizeof(a));	//通过数组地址+数组元素个数实现初始化
    
    vector<int> v4(7, 0);			//通过明确元素个数+元素值实现初始化
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • c++11中vector<>新增的初始化方式:
    vector<int> v5{1, 2, 3, 4, 5};	//类似数组int arr[] = {1,2,3,4,5}初始化
    
    • 1
    • 其实从98支持的后两种方式:数组地址+数组元素个数 / 明确元素个数+元素值,

      就可以看出来,提供给vector<>元素个数,vector<>就能实现类似数组初始化的效果

    • c++11中的{}初始化其实也是通过编译器隐式构造了中间临时变量Initializer_list,

      告知vector<>元素个数后,实现了类似数组初始化的功能

    定义:
    • initializer_list:本质亦为STL容器,可以理解为一个标明长度的数组
    • STL中的源码:
    template<class _E>
    class initializer_list
    {
    	public:
      		typedef _E value_type;
      		typedef const _E& reference;
      		typedef const _E& const_reference;
      		typedef size_t size_type;
      		typedef const _E* iterator;
      		typedef const _E* const_iterator;
    	private:
      		iterator _M_array;
      		size_type _M_len;
    
      		// The compiler can call a private constructor.
      		constexpr initializer_list(const_iterator __a, size_type __l)
      			: _M_array(__a), _M_len(__l) { }
    
    	public:
      		constexpr initializer_list() noexcept
      			: _M_array(0), _M_len(0) { }
    
      		constexpr size_type size() const noexcept { return _M_len; }
      		constexpr const_iterator begin() const noexcept { return _M_array; }
      		constexpr const_iterator end() const noexcept { return begin() + size(); }
    };
    
    • 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
    • 功能:
      1. 基本:含有元素值,以及元素个数
      2. 进阶功能:由于含有元素个数,所以可以作为中间临时变量为其他STL提供初始化构造函数所需内容

    使用列表初始化不定长STL

    • 以vector / map 演示:
    //vector:
    vector<int> v{1, 2, 3, 4, 5};
    
    //map:
    map<string, string> m{{"1", "I"}, {"2", "II"}, {"4", "IV"}};
    
    //pair + map:
    pair<string, string> p = {"5", "v"};
    m.insert({"6", "VI"});		//map的insert()和erase()
    m.insert(makepair("10", "X"));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    自定义STL支持initializer_list:

    • 经过编译器隐式类型转化的举例,以及initializer_list的原理和使用过程,

      下面我们自己实现一个基于initializer_list实现列表初始化的类

    • 其实就是实现一下基于initializer_list的构造函数:

    //迭代器版本:
    template <class T>
    list(initializer_list<T> ilt){
    	initializer_list<T>::iterator it = ilt.begin();
    	while(it != ilt.end()){
    		push_back(*it);
    		it++;
    	}
    }
    //范围for版本:
    template <class T>
    list(initializer_list<T> ilt){
    	for(auto i : ilt){
    		push_back(i);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 相关阅读:
    线程的深度剖析
    ESP 特权隔离机制—案例研究
    解决Redis缓存穿透(缓存空对象、布隆过滤器)
    Docker基本管理
    自学Python第二十五天- Pipenv 虚拟环境和包管理工具
    C++ vector使用数组进行初始化
    韩语图片文字如何转为纯文本?
    Git的安装和使用以及VScode配置Git的快速使用
    04 后端增删改查【小白入门SpringBoot + Vue3】
    vue2构建一个后端权限管理模板
  • 原文地址:https://blog.csdn.net/buptsd/article/details/126869821