C++ 构造函数再探
如果成员为const或者引用,那么必须初始化,或者当成员属于某种类类型且该类类型没有定义默认构造函数时,必须将这个成员初始化。
随着构造函数一开始执行,初始化就完成了,我们初始化const或者引用类型的数据成员唯一的机会就是通过构造函数初始值,因此我们可以这样写构造函数
建议养成构造函数初始化的习惯!
成员初始化的顺序
构造函数初始化列表只说明用于初始化成员的值,而不限定初始化的具体执行顺序。
成员初始化顺序和他们在类定义中的顺序一致,第一个成员先被初始化,然后是第二个。构造函数初始值列表中初始化的前后位置关系不会影响实际的初始化顺序。
看起来是val初始化了j,然后再用j初始化i,事实上i先被初始化,因此这个初始值的效果是试图让未定义的j初始化i。
注意:最好令构造函数初始值的顺序和成员声明的顺序保持一致,而且尽可能避免使用某些成员初始化其他成员。
默认实参和构造函数
上面的构造函数只接受一个string实参其实和默认构造器差不多,只不过我们使用默认实参初始化bookNo。
委托构造函数
一个委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程。
除了第一个构造函数外,其他的构造函数都委托了他们的工作。
这里接受istream&的构造函数先委托了默认构造函数,然后默认构造函数委托了第一个构造函数,当这些受委托的构造函数执行完后,接着执行istream&构造函数体的内容。
当一个构造函数委托给另一个构造函数时,受委托的构造函数的初始值列表和函数体被依次执行,只是上面代码受委托的函数体为空,如果函数体包含代码的话,将先执行这些代码,然后控制权才会回到委托者的函数体。
默认构造函数的作用
注意:如果定义了其他构造函数,最好提供一个默认构造函数!
隐式的类类型转换
编译器只会自动的执行一步类型转换。
第一个combine试图完成字符串对string的转换,然后实现临时的string初始化Sales_data。
抑制构造函数定义的隐式转换
此时没有任何构造器能用于隐式的创建Sales_data对象。
上面两个运用都是错误的!
注意:explicit只能在类内声明构造函数时使用。
聚合类
一个类满足
1所有成员都是public
2没有定义任何构造函数
3没有类内初始值
4没有基类也没有virtual函数(我们后面会介绍)
这就是一个聚合类。
我们可以提供一个花括号括起来的成员初始值列表,并用它初始化聚合类的数据成员
这里初始值的顺序必须和声明顺序一致,否则就是错误的。
如果列表里的值少于聚合类成员,那么后面的成员自动执行值初始化。
显式的初始化有三个缺点:
1类的所有成员都是public
2将初始化的操作交给了用户而不是作者,这样用户可能会给一个错误的类型
3增加或减少一个成员,所有的初始化语句都要更新。