一、认识构造函数
当创建一个类类型对象时,类通过一个或者几个特殊的成员函数来控制对象的初始化,这种函数就是构造函数。它的任务就是用来初始化类对象的成员的,所以当创建类对象或者类对象被创建就会调用构造函数。
构造函数的几个特点:
1.函数名和类名必须一样,没有返回值。
2.当没有显式的定义构造函数时,系统会自己生成默认的构造函数。
3.构造函数可以重载。
1 class Date 2 { 3 public: 4 Date() 5 { } 6 Date(int day) 7 { 8 _year = 1949; 9 _month = 10; 10 _day = day; 11 } 12 void print() 13 { 14 cout << _year << "-" << _month << "-" << _day << endl; 15 } 16 17 private: 18 int _year=1990; 19 int _month; 20 int _day; 21 };
在上面的代码中,定义了一个简单的Date类类型,可以看到有显式的给出了构造函数,第一个是没有参数列表且函数不做任何事的,还有一个是有一个整型参数day的,就是当我传了一个day参数,则在函数内部把它的year和month初始化为1994和10。这样的两个构造函数就构成了重载,因为能够重载,所以在写构造函数的时候要保证只有一个缺省的构造函数。参数列表为空或者参数全缺省称为缺省构造函数。
当不传参的定义一个Date类型对象,会调用显式定义的缺省构造函数,在没有初始化列表的情况下采取类内初始化或默认初始化,上面的程序中,如果不传参,那么构建的对象的_year成员为1990,另外两个值为随机值。
牢记:
我们在没有显式的定义构造函数时,系统会自动生成一个默认构造函数。当我们定义了一些其他的构造函数时,这个类就将没有默认构造函数。所以当我们显式的定义了其他构造函数,最好把默认构造函数也显式的定义一遍。这样也有好处,就是系统生成的默认构造函数有可能执行错误的操作或者无法完成类成员的初始化(例如:有一个成员是类类型的对象且它没有缺省的构造函数)。
当我们定义的默认构造函数并不需要干什么事情,只是因为上面的情况才显式的定义它,那么此时的默认构造函数等同于系统生成的默认构造函数,那么我们可以这么定义:
Date() = default;
因为在新标准中,如果需要系统默认的行为,就可以通过在参数列表后加上=default来使编译器生成构造函数。
二、初始化列表
如下图所示,在冒号和花括号之间的代码部分称为构造函数的初始值列表,它的作用是给创建的对象的某些成员赋初值。这种是在构建对象的时候的初始化,是在对象创建成功之前完成的,和在函数体内赋值是不一样的,函数体内赋值是你的对象成员都已经创建好后对成员进行的赋值。
那么,可以看到,这种初始化并不是必须的。但是在以下几种情况时是必须进行初始化的:
1.成员是const类型。
2.成员是引用类型。
3.有一个成员是类类型的对象(且它没有缺省的构造函数)
1 class Time 2 { 3 public: 4 Time( ) 5 { 6 7 } 8 9 private: 10 int _hour; 11 }; 12 class Date 13 { 14 public: 15 Date(int year=1990,int month=1,int day=1) 16 :_year(year), _month(month), _day(day), t(10) 17 { } 18 void print() 19 { 20 cout << _year << "-" << _month << "-" << _day << endl; 21 } 22 private: 23 int _year=1990; 24 int _month; 25 int _day; 26 Time t; 27 };
解释:
1.对于const和引用类型,必须要进行初始化,所以他们必须在初始化列表中进行初始化。
2.当类类型成员有缺省的构造函数时,在创建对象的时候体统会默认调用,因为不用传参。当你的构造函数不是缺省的,如果不在初始化列表中进行调用构造函数,系统就无法知道怎么调用t的构造函数,那么就无法创建t了。
如上代码中,需要在参数列表中调用t的构造函数才不会出错。
成员初始化顺序:
在上面的初始列表中,每个成员只能出现一次,因为一个变量多次初始化是无意义的。
还有重要的一点,初始化列表的顺序并不限定初始化的执行顺序。成员的初始化顺序是与类中定义的顺序保持一致。可以看看下面的初始化列表:
在这里的意思是想要用1来初始化_month,再用_month初始化_year。但其实是_year被先初始化,而此时你的_month并没有初始化,所以,最后的结果是_year是一个随机值。
所以,最好让构造函数初始值的顺序与成员声明的顺序保持一致。