1.类的成员的初始化和赋值之间的差异
在类的构造函数如果对类的成员使用了初始值列表,则是对这些成员实行了初始化,在这之前,类还未为这些成员初始化;如果在构造函数的函数体内对成员进行赋值,则是对成员进行了赋值,在这之前,类已为这些成员进行了初始化。如下:
Sales_data::Sales_data(const string &s,unsigned cnt,double price):
bookNo(s),units_sold(cnt),revenue(cnt*price){
}
//在这样的例子中,使用了初始化列表对成员进行了初始化,则编译器执行的是
//程序员定义的初始化。
Sales_data::Sales_data(const string &s,unsigned cnt,double price){
bookNo = s;
Units_sold = cnt;
Revenue = cnt * price;
}
//在这样的构造函数中,则是编译系统先为成员执行默认初始化,然后再在函数
//体中执行赋值。
2.构造函数的初始值必不可少的情况
在const和引用类型的成员中,要求必须要有初始值(const和引用类型的变量在声明时就必须要进行初始化),所以,在有这样成员的类中就不能使用赋值的方式,而只能使用初始化列表的方式进行构造函数的构建。
Class ConstRef{
Public:
ConstRef(int ii);
Private:
Int I;
Const int ci;
Int &ri;
}
ConstRef::ConstRef(int ii):i(ii),ci(ii),ri(i){…}//初始化列表方
//式初始化const类型和引用类型的数据,正确。
//错误事例
ConstRef::ConstRef(int ii){
I = ii;//正确,不是上述两种情况之一
Ci = ii;//错误,const的变量,不能赋值,要求初始化
Ri = I;//错误,是引用变量,需要用列表初始化,不能赋值。
}
Note:
1.如果成员是const、引用,或者属于某种未提供默认构造函数的类类型,则必须通过构造函数初始值列表为这些成员提供初始值。在很多类中,初始化和赋值的区别事关底层效率问题,更重要的是,一些数据必须被初始化。
2.成员初始化的顺序与成员的声明顺序有关,而与初始化列表中的顺序无关。这样有时候在初始化列表中使用了后声明的成员来初始化先声明的成员就会出错。如:
int i;
int j ;
ConstRef(int ii):j(i),i(i){…}
这样就相当于先用还没有初始化的i来初始化j,就会造成错误。所以,一般避免使用成员来初始化另一个成员,也尽量使用构造函数中的参数来初始化成员。
3.默认实参和构造函数
如果一个构造函数为所有参数都提供了默认实参,则它实际上也定义了默认构造函数。
4.默认构造函数的作用
当对象被默认初始化或值初始化时自动执行默认构造函数。
默认初始化的情形:
在块作用域内不使用初始值定义一个非静态变量或数组:
{//块内
Int I;//默认初始化,不可直接使用
Int i[10];//默认初始化,不可直接使用
}
当一个类本身含有类类型的成员且使用合成的默认构造函数时。
当类类型的成员没有在构造函数初始值列表中显示地初始化时。
值初始化地情况:
数组初始化过程中,提供地初始值数量少于数组的大小时。
Int i[10] = {1,2,3};//剩余的使用零进行值初始化
不使用初始值定义一个局部静态变量时。
Int i;//值初始化为零
通过书写 类名()这样的表达式显示地请求值初始化时。
Class A;
A a = A();//显示地使用T()请求初始化
有时候,类的某些数据成员缺少默认构造函数,形如:
Class NoDefault{
Public:
NoDefault(const std::string&);
//还有其他成员,但是没有其他的构造函数。
}
这样的情况下,NoDefault就没有默认构造函数。
所以,如果定义了其他构造函数,那么最好也提供一个默认构造函数。
Note:注意分清以下两种情况:
A obj();//声明的是一个函数而非对象
A obj;声明的是对象