构造函数和初始化列表关系和区别,以及为什么有初始化列表,和它的好处
- 一、构造函数和初始化列表的关系和区别
- 二、为什么有初始化列表
- 三、使用初始化列表的好处
一、构造函数和初始化列表的关系和区别
百度百科这样定义初始化列表:与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。
1、关系:从定义中我们可以知道初始化列表也是配合构造函数使用的,也就是说,构造函数可以使用初始化列表,也可以不使用(简单理解为初始化列表是构造函数的配件,可以有,也可以没有,但是有些场合必须使用初始化列表,第三部分会说)。
2、区别:我们以前都认为构造函数是给对象初始化,其实严格来说构造函数体中的语句只能将其称为赋初值,而初始化列表才是真正的初始化。因为初始化是在一个变量(或对象)定义的时候赋予初始值才叫初始化。如何理解构造函数是赋初始值,下面为什么有初始化列表中的三种情况你可以感受到。
二、为什么有初始化列表
因为类中有下面三种成员变量时,必须使用初始化列表来初始化
1、引用成员变量,因为引用必须在定义的时候初始化,而构造函数体中的语句是赋值,所以应该使用初始化列表初始化
2、const修饰的成员变量,因为const修饰的成员变量也必须在定义的时候初始化,所以必须使用初始化列表
3、没有默认构造的自定义类型成员变量,因为使用构造函数一定不可避免的会调用自定义类型成员变量的默认构造,然而使用初始化列表可以避免使用默认构造函数,直接调用该成员变量的拷贝构造(下面例子会讲到这些)
下面举例说明以上三种情况,如下:声明了一个Time类和Date类,Date类中声明了一个Time类类型变量
class Time //声明一个Time类,类中有三个成员变量,和一个有参构造函数
{
public:
Time(int hour, int minuter, int second, int day)
{
_hour = hour;
_minuter = minuter;
_second = second;
}
private:
int _hour;
int _minuter;
int _second;
};
//声明一个Date类,类中有一个int类型成员变量、一个引用、一个const修饰、一个自定义类型
class Date
{
public:
Date(int year, int month, int day) //构造函数
{
_year = year;
_month = _month;
_day = day;
}
private:
int _year;
int& _month; //引用
const int _day; //const修饰
Time _t; //没有默认构造的自定义类型
};
int main()
{
Date d(2023, 11, 14);
return 0;
}
vs2022中执行上面代码,会出现以下错误,并给出错误原因
解决方法就是使用初始化列表,如下所示:
class Time //声明一个Time类,类中有三个成员变量,和一个有参构造函数
{
public:
Time(int hour, int minuter, int second, int day)
{
_hour = hour;
_minuter = minuter;
_second = second;
}
private:
int _hour;
int _minuter;
int _second;
};
//声明一个Date类,类中有一个int类型成员变量、一个引用、一个const修饰、一个自定义类型
class Date
{
public:
Date(int year, int month, int day, Time& t)
:_year(year), _month(month), _day(day), _t(t) //初始化列表,_t(t)这个调用的是拷贝构造,为什么会调用拷贝构造,可以看本文【链接】
{
}
private:
int _year;
int& _month; //引用
const int _day; //const修饰
Time _t; //没有默认构造的自定义类型
};
int main()
{
Time t(23, 43, 40); //实例化一个Time对象
Date d(2023, 11, 14, t);
return 0;
}
三、使用初始化列表的好处
当你了解了没有默认构造的自定义类型成员变量必须使用初始化列表的时候,初始化列表的好处也就出来了:可以减少一次默认构造的调用,当有大量对象的时候,使用初始化列表的好处还是很明显的,可以大大降低函数压栈带来的开销。所以我们能使用初始化列表尽量使用初始化列表。