C++11之列表初始化
发展历史
C++11是C++的第⼆个主要版本,并且是从C++98起的最重要更新。它引⼊了⼤量更改,标准化了既有实践,并改进了对C++程序员可⽤的抽象。在它最终由ISO在2011年8⽉12⽇采纳前,⼈们曾使⽤名称“C++0x”,因为它曾被期待在2010年之前发布。C++03与C++11期间花了8年时间,故⽽这是迄今为⽌最⻓的版本间隔。从那时起,C++有规律地每3年更新⼀次。
列表初始化
(注意区分列表初始化和初始化列表的概念区别,这俩并非一个东西)
C++98传统的{}
C++98中⼀般数组和结构体可以⽤{}进⾏初始化。
struct Point
{
int _x;
int _y;
};
int main()
{
int array1[] = { 1, 2, 3, 4, 5 };
int array2[5] = { 0 };
Point p = { 1, 2 };
return 0;
}
C++11中的{}
• C++11以后想统⼀初始化⽅式,试图实现⼀切对象皆可⽤{}初始化,{}初始化也叫做列表初始化。
• 内置类型⽀持,⾃定义类型也⽀持,⾃定义类型本质是类型转换,中间会产⽣临时对象,最后优化 了以后变成直接构造。
• {}初始化的过程中,可以省略掉=
• C++11列表初始化的本意是想实现⼀个⼤统⼀的初始化⽅式,其次他在有些场景下带来的不少便 利,如容器push/inset多参数构造的对象时,{}初始化会很⽅便
int main()
{
// C++98⽀持的
int a1[] = { 1, 2, 3, 4, 5 };
int a2[5] = { 0 };
Point p = { 1, 2 };
// C++11⽀持的
// 内置类型⽀持
int x1 = { 2 };
// ⾃定义类型⽀持
// 这⾥本质是⽤{ 2025, 1, 1}构造⼀个Date临时对象
// 临时对象再去拷⻉构造d1,编译器优化后合⼆为⼀变成{ 2025, 1, 1}直接构造初始化d1
// 运⾏⼀下,我们可以验证上⾯的理论,发现是没调⽤拷⻉构造的
Date d1 = { 2025, 1, 1};
// 这⾥d2引⽤的是{ 2024, 7, 25 }构造的临时对象
//C++11后支持多参数的隐式类型转换
const Date& d2 = { 2024, 7, 25 };//临时对象具有常性,所以定义d2要带有const
// 需要注意的是C++98⽀持单参数时类型转换,也可以不⽤{}
Date d3 = { 2025};//C++11
Date d4 = 2025;//C++98
// {}初始化的,可以省略掉=
Point p1 { 1, 2 };
int x2 { 2 };
Date d6 { 2024, 7, 25 };
const Date& d7 { 2024, 7, 25 };
//vector为例
vector<Date> v;
v.push_back(d1);
v.push_back(Date(2025,1,1));
//比起有名对象和匿名对象传参,{}传参更有性价比
v.push_back({2025,1,1});
//map为例
map<string,string> dict;
dict.insert({"xxx","yyy"});
return 0;
}
补充:
-
匿名对象和临时对象的区别:匿名对象是我们手动创建的,临时对象是编译器生成的,共同特点是都具有常性,都只能给给const引用,不能给普通引用。
-
这俩转换成汇编指令后都一样
int x1 = {2}; int x2 = 2;
迭代器和范围for也是一样的道理。
-
//注意这个虽然形态一样,但不是列表初始化 vector<int> v1={1,2,3,4}; vector<int> v2={11,12,13,14,15,16,17,18,19,20};//可以支持任意多个参数
要支持列表初始化,要有对应参数的构造。而vector可以支持任意多个参数,vector没有全缺省的构造,而是通过C++11的initializer_list支持的。
initializer_list是C++11专门提供的一个类模版。
如果把花括号的列表直接给给一个对象,就会将其识别为initializer_list
initializer_list提供的成员有:
size,begin,end。也就是size和迭代器,还有构造。
initializer_list的底层
initializer_list<int> il1 = {11,12,13,14,15,16,17,18,19,20};
int a1[] = {11,12,13,14,15,16,17,18,19,20};//类似于这个
原理类似:都是在栈上开一段空间,然后把常量数据给拷贝过来。然后initializer_list还有两个指针指向这段空间。
而且通过监视窗口可以看到il1和a1的地址是很接近的。
所以再聊回来vector,vector是怎么支持这个任意个参数初始化的呢?
本质也可以理解成是隐式类型转换。
//拷贝+拷贝构造+优化
vector<int> v1 = {11,12,13,14,15,16,17,18,19,20};
//构造
vector<int> v2({11,12,13,14,15,16,17,18,19,20});
其中,下面这行,也就是实参传参给了形参initializer_list<value_type> il
const vector<int>& v3 = {11,12,13,14,15,16,17,18,19,20};//加const才可以
同样的,也可以去掉赋值符号:
vector<int> v1{11,12,13,14,15,16,17,18,19,20};
const vector<int>& v3{11,12,13,14,15,16,17,18,19,20};
//对比于下面这条,下面的有圆括号,上面的没有圆括号。
//vector<int> v2({11,12,13,14,15,16,17,18,19,20});
最终结果都是构造。
// STL中的容器都增加了⼀个initializer_list的构造
vector (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());
list (initializer_list<value_type> il, const allocator_type& alloc = allocator_type());
map (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());
// ...
map的:
//initializer_list + {}pair初始化隐式类型转换
map<string,string> dict = {{"xxx","yyy"},{"sort","zzz"}};
总之,就是一切皆可列表初始化。