C++11 - 1 - Initializer_list

发布于:2022-12-17 ⋅ 阅读:(272) ⋅ 点赞:(0)

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

内置类型数组:

  • 三种选择:= {},{},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

结构体/类:

  • 三种初始化:构造函数,{}拷贝构造,={}拷贝构造
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};

结构体/类数组:

  • 一种构造函数:{{}}
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}

二,隐式类型转化:

内置类型转化:

整型提升:

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

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

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

char a = 1;
short b = 2;
int c = 3;

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

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

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

int a = 5;
float b = 2;			//1,赋值时临时变量int 2被提升为float型
cout<< (a/b)<< endl;	//2,计算时a先被提升为float型,存储在临时变量中

整型截断:

  • 整型提升是编译器隐式帮我们处理的,整型截断也是
  • 当我们为变量赋的值超过其内存可容纳范围时,发生整型截断:
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
*/

隐式拷贝构造:

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

三,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);			//通过明确元素个数+元素值实现初始化
  • c++11中vector<>新增的初始化方式:
vector<int> v5{1, 2, 3, 4, 5};	//类似数组int arr[] = {1,2,3,4,5}初始化
  • 其实从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. 进阶功能:由于含有元素个数,所以可以作为中间临时变量为其他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"));

自定义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);
	}
}