6.6 聚合初始化
顾名思义,聚合(aggregate)就是多个事物聚集在一起。这个定义包括混合类型的聚合:像struct和class等。数组就是单一类型的聚合。
初始化聚合往往既冗长又容易出错。而C++中聚合初始化(aggregate initialization)却变得很方便而且很安全。当产生一个聚合对象时,要做的只是指定初始值就行了,然后初始化工作就由编译器去承担了。这种指定可以用几种不同的风格,它取决于正在处理的聚合类型。但不管是哪种情况,指定的初值都要用大括号括起来。比如一个内建类型的数组可以这样定义:
如果给出的初始化值多于数组元素的个数,编译器就会给出一条出错信息。但如果给的初始化值少于数组元素的个数,那将会怎么样呢?例如:
这时,编译器会把第一个初始化值赋给数组的第一个元素,然后用0赋给其余的元素。注意,如果定义了一个数组而没有给出一列初始值时,编译器并不会去做初始化工作。所以上面的表达式是将一个数组初始化为零的简洁方法,它不需要用一个for循环,也避免了“偏移1位”错误(它可能比for循环更有效,这取决于编译器)。
数组还有一种叫自动计数(automatic counting)的快速初始化方法,就是让编译器按初始化值的个数去决定数组的大小:
现在,如果决定增加另一个元素到这个数组上,只要增加一个初始化值即可,如果以此建立我们的代码,只需在一处作出修改即可,这样,在修改时出错的机会就减少了。但怎样确定这个数组的大小呢?用表达式sizeof c/sizeof*c(整个数组的大小除以第一个元素的大小)即可算出,这样,当数组大小改变时它不需要修改[1]。
因为结构也是一种聚合类型,所以它们也可以用同样的方式初始化。因为C风格的struct的所有成员都是public型的,所以它们的值可以直接指定。
如果有一个这种struct的数组,也可以用嵌套的大括号来初始化每一个对象。
这里,第三个对象被初始化为零。
如果struct中有私有成员(典型的情况就是C++中设计良好的类),或即使所有成员都是公共成员,但有构造函数,情况就不一样了。在上例中,初始值被直接赋给了聚合中的每个元素,但构造函数是通过正式的接口来强制初始化的。这里,构造函数必须被调用来完成初始化,因此,如果有一个下面的struct类型:
必须指示构造函数调用,最好的方法像下面这样:
这样就得到了三个对象和进行了三次构造函数调用。只要有构造函数,无论是所有成员都是公共的struct还是一个带私有成员的class,所有的初始化工作都必须通过构造函数来完成,即使正在对一个聚合初始化。
下面是多构造函数参数的又一个例子:
注意:这看起来就好像对数组中的每个对象都调用显式的构造函数。
[1]在本书的第2卷中(可以在http://www.BruceEckel.com上免费获得),我们将会看到:通过使用模板可以更方便地决定一个数组的大小。