1.4 统一的类成员初始化语法与std::initializer_list<T>

假设类A有一个类型为int数组的成员变量,在C++98/03标准中,如果我们要在构造函数中对这个成员变量进行初始化,则需要这样写:

对于字符数组,我们可能就要在构造函数中使用 strcpy、memcpy 这一类函数了;再者,如果数组元素足够多,初始值又没什么规律,则这种赋值代码会有很多行。但是,如果arr是一个局部变量,则我们在定义arr时可以使用如下语法对其进行初始化:

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

既然在C++98/03标准中局部变量数组支持这种语法,那么为什么在类成员变量语法中不支持这种语法呢?这是旧语法不合理的一个地方。因此在C++11语法中,对类成员变量也可以使用这种语法进行初始化:

可以看出,新语法比旧语法更加简洁。

在Java这类语言中定义一个类时,可以为其成员变量设置一个初始值,语法如下:

但在C++98/03标准中,对类的成员必须使用static const修饰,而且类型必须是整型(包括bool、char、int、long等),这样才能使用这种初始化语法:

在C++11标准中就没有这种限制了,我们可以使用花括号(即{})对任意类型的变量进行初始化,而且不用是static类型:

当然,在实际开发中,建议还是将这些成员变量的初始化统一写到构造函数的初始化列表中,方便阅读和维护代码。

综上所述,在C++11标准中,无论是局部变量还是类变量,使用花括号({})初始化的语法被统一起来,写法也变得简洁。

那么这种语法是如何实现的呢?如何在自定义类中也支持这种花括号呢?这就需要用到C++11新引入的对象std::initializer_list<T>了。它是一个模板对象,接收一个自定义参数类型 T,T既可以是基础数据类型(如编译器内置的 bool、char、int等),也可以是自定义的复杂数据类型。为了使用 std::initializer_list<T>,需要包含头文件,下面是一个例子:

在以上代码中自定义了一个类A,为了让A的构造函数和append方法同时支持花括号语法,给这两个方法同时设置了一个参数 integers,参数的类型均为 std::initializer_list<int>,程序执行结果如下:

再来看一个例子,网上某C++JSON库支持采用如下语法创建一个JSON对象:

那么,这个 json::array 方法是如何实现的呢?这利用 std::initializer_list<T>也很容易实现,首先在花括号中有两个元素{"currency","USD"}和{"value",42.99},且这两个元素的值不一样,前者是两个字符串类型,后者是一个字符串类型和一个浮点类型。因此,我们可以创建两个构造函数分别支持这两种类型的构造函数,构造的对象类型为jsonNode;然后创建一个 JSON 对象,实现其 array 方法,该方法接收一个参数,参数类型为std::initializer_list<jsonNode>。完整的代码如下:

程序执行结果如下:

希望读者通过上面两个例子理解 std::initializer_list<T>的使用场景。std::initializer_list<T>除了提供了构造函数,还提供了三个成员函数,这和 stl其他容器的同名方法用法一样: