Hello,everybody!构造函数的内容比较多,语法还有些复杂。我分成了两篇文章进行讲解,大家在看过构造函数(上)后再来看这篇文章更容易理解哟!
1.初始化列表的格式
类似这种格式,在初始化列表中第一行用冒号开头,剩下的用逗号开头。初始化列表结束后,后面才是函数体。
初始化列表也是初始化类中成员的一种方式,就目前这个例子感觉在初始化列表中初始化与在函数体中初始化没啥区别。咱们还无法窥探其中的奥妙。
不过我先给大家一个建议:
能用初始化列表,就用初始化列表,用不了的时候再考虑用函数体初始化。
下面开始介绍初始化列表独特的地方:
1.其实初始化列表才是类中成员定义的地方,初始化列表优先于函数体运行,等程序走到函数体时,类中的所有成员都已经被定义过了。这就意味着初始化后不可被修改的成员和定义时必须初始化的成员等只能通过初始化列表初始化。例如被const修饰过的成员和引用等。
被const修饰过的x就不能通过函数体初始化,因为x在初始化列表中已经定义并初始化过了(尽管我们没有写出来)。在函数体中做的工作是赋值,修改。
还有一个就是引用,因为引用要求在定义的时候必须初始化。
看过以上两个例子可以知道,就算初始化列表中什么都不写,它也会帮我们把类中的成员都定义好,然后我们在函数体中给成员修改,赋值。如果在初始化列表中直接完成初始化,就省去了在函数体中修改数据这一步骤,程序效率相对来说要高一点。
另外要补充一点:
既然初始化列表是成员定义的地方,那么同一成员变量只能被定义一次,不能被多次定义。
2.当类中的成员变量有自定义类型时
在上文中,我们提到:引用成员变量和const成员变量只能在初始化列表中初始化。
这里补充最后一条:自定义类型成员也只能在初始化列表中初始化。
在构造函数(上)中,我们提到:对于编译器默认生成的构造函数,在对象实例化后对于内置类型不做处理,对于自定义类型回去调用它的默认构造函数。
在这个例子中,Date类型的成员中有一个A类型,在对象实例化后编译器直接调用了A的默认构造函数看似好像没啥问题,实际上和前两行的黑体字表达的意思有所出入。
注意:在Date类型中,我们已经把默认构造函数写出来了,这个构造函数不是编译器默认生成的为什么还会回去调用A中的默认构造函数呢?
实际上是"大道同归"。编译器默认生成的构造函数也好,自己写的构造函数也好,在调用函数体前,都会先运行初始化列表,在初始化列表中不管我们写与不写,都会帮我们定义好A _aa。在定义的过程中就相当于对象实例化,自然就会调用A中的默认构造函数!
那问题来了!如果A中没有默认构造函数呢?比如,我把缺省参数去掉:
这时我们就需要在Date中构造函数的初始化列表中手动给A _aa初始化,否则会因为A中没有可以调用的默认构造函数而报错。
3.初始化列表与函数体搭配运用
除了引用成员变量,const成员变量和类成员变量,其他成员变量的初始化既可以在初始化列表中,也可以在函数体中,但建议在初始化列表中,原因在上文已经介绍的很清楚了。
虽然初始化列表的功能很强大,但也有它无法完成的任务,这时就需要和函数体配合使用:
如果需要动态开辟空间,在初始化列表中可以完成。但是空间是否开辟成功需要进行检查,初始化列表就无法胜任了,需要搭配函数体使用。再比如一个数组的初始化需要用到循环语句,初始化列表也是无法完成的!