初始化列表

初始化列表的概念及使用

C++98中,标准允许使用花括号”{}”对数组元素进行统一的集合初始值设定。比如:

1
2
int arr[5] = {0};
int arr[] = {1,2,3,4};

C++11中,这种初始化的方法,被扩展到了集合(列表)中。

总结初始化方法如下:
1) 等号加上赋值表达式,如 int a = 3+4;
2) 等号 加上花括号的 初始化列表, 如 int a = {3+4};
3) 圆括号式的表达式列表(expression list), 如 int a = (3+4);
4) 花括号式的初始化列表 , 如 int a{3+4}

其中,第3、4中方式也可用于获取堆内存 new操作符中,如下:

1
2
int * i = new int(5);
double *d = new double(1.5f);

标准模板库中容器对初始化列表的支持源自这个头文件中initialize_list的类模板的支持。
只需要包含这个头文件,并且声明一个以initialize_list模板类为参数的构造函数,同样可以使得自定义的类使用列表初始化。

利用初始化列表,重载operator[],operator= 以及使用辅助的数组。例子如下:

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <vector>
#include <initializer_list>
using namespace std;

class Mydata{
public:
Mydata& operator[](initializer_list<int> l){
for(auto i=l.begin();i!=l.end();++i)
idx.push_back(*i);
return *this;
}
Mydata& operator=(int v){
if(idx.empty()!=true)
{
for(auto i = idx.begin();i!=idx.end();++i)
{
d.resize((*i>d.size())?*i:d.size());
d[*i-1] = v;
}
idx.clear();
}
return *this;
}
void print(){
for(auto i=d.begin();i!=d.end();++i)
cout<<*i<<" ";
cout<<endl;
}
private:
vector<int> idx; //辅助数组,用于记录index
vector<int> d;
};

int main(){
Mydata d;
d[{2,3,5}] = 7; //将第2,3,5位设为7
d[{1,4,5,8}] = 4; //第1,4,5,8位设为4
d.print(); //4 7 7 4 7 0 0 4
}

此外,初始化列表还可以用于函数返回的情况,但是返回一个初始化列表,通常会导致构造一个临时变量

1
2
3
vector<int>Func(){
return {1,3};
}

防止类型收窄

使用列表初始化还有一个最大优势是 可以防止类型收窄。

类型收窄 一般是指一些可以使得数据变化或者精度丢失的隐式类型转换。

可能导致 类型收窄的典型情况如下:

  1. 以浮点数隐式地转换为整型 比如: int a = 1.2

  2. 从高精度的浮点数转化为低精度的浮点数,比如: 从long double 隐式地转为 double。或者从double 转为 float。 这种精度降低,都可以视为类型收窄

  3. 从整型转为浮点数。如果整型数大到无法使用浮点数精确表达,也可以视为类型收窄

  4. 从整型,转为较低长度地整型。 比如:unsigned char = 1024; 1024是不能被8位地unsigned char 容纳的

使用初始化列表,是不能容许类型收窄的情况出现的。会编译通不过。

使用自定义初始化列表

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Warriors
{
public:
Warriors(const initializer_list<string>& members)
{
for (auto& data : members)
{
players.emplace_back(data);
}
}

vector<string> players;

void print()
{
for (auto itm : players)
std::cout << itm << endl;
}
};