前言:这个知识点的细节比较多,且有些细节不太容易理解,要做好准备哟👻
Ⅰ.构造函数的不完美😭
初始化列表,顾名思义,用列表一样的格式将其初始化。
🤔奇怪啊,构造函数的不是可以初始化嘛?为什么C++还要引入初始化列表?
唉,因为这个构造函数它在某些方面不太好使啊!
在介绍初始化列表之前,我先来把构造函数的缺陷指出来,从而让我们意识到:世界需要初始化列表。
关于构造函数的不完美,这里列出2点:
1.当自定义类型成员没有默认构造函数时,会出现一种很尴尬的场面:
class Time
{
public:
Time(int time)
{
_time = time;
}
private:
int _time;
};
class Date
{
public:
Date(int day, int time)
{
_t = time;
_day = day;
}
private:
int _day;
Time _t; //默认构造函数会调用自定义类型的Time(int time)
};
int main()
{
Date d1(1,1);
return 0;
}
为啥尴尬?
示例化d1时,会调d1的默认构造函数(编译器自己生成的),它对于内置类型 _year不做处理,
但对于自定义类型 _t,会调用它的构造函数,此时我们自己写了,编译器不再自动生成默认的。
然而,这个Time是需要传参的,可d1并没有传参过去。
所以会编译失败。
2.没法给const常量、引用类型的成员变量初始化。
举个const常量的例子。
const常量,在声明的同时,必须初始化。
int main() {
int a = 0;
const int b; //没初始化
return 0;
}
它就必须得写成这样:
const int b = 0(初始值);
而构造函数没法让const常量初始化:
class Date {
public:
Date(int year, int month, int day) {
this->_year = year;
this->_month = month;
this->_day = day;
}
private:
const int _year; //假如_year设定为2023,那它又该在哪初始化呢?
int _month; //这里是声明,是不可以初始化的!
int _day;
};
int main() {
return 0;
}
在Date构造函数里是没法初始化变量的。
因为类就是一张造房子的图纸,它实际并没开空间!
而初始化一个变量,是需要开空间,来保存值的。
Ⅱ.初始化列表✨
1.格式
构造函数是在函数体内初始化的,而初始化列表是在体外。
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,
每个"成员变量"后面跟一个放在括号中的初始值或表达式。
be like this:
Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
{}
或
Date(int month, int day)
:_year(2023)
,_month(month) //用几个参数就传几个
,_day(day)
{}
或
Date()
:_year(2023)
, _month(1)
,_day(1)
{}
2.用初始化列表 解决 构造函数的问题
刚刚我们指出,自定义类型成员(前提是没有默认构造函数)、const常量成员,都没法被构造函数初始化。
现在换上初始化列表试试:
a. 自定义类型成员
class Time
{
public:
Time(int time)
{
_time = time;
}
private:
int _time;
};
class Date
{
public:
Date(int day, int time)
:_t(time) //自定义类型用初始化列表
{
_day = day;
}
private:
int _day;
Time _t;
};
int main()
{
Date d1(1,1);
return 0;
}
成功初始化:
b. const常量成员
class Date {
public:
Date(int month, int day)
:_year(2023)
{
this->_month = month;
this->_day = day;
}
private:
const int _year;
int _month;
int _day;
};
int main() {
Date d1(1, 1); //d1、d2、d3的_year都是2023
Date d2(2,2);
Date d3(3,3);
return 0;
}
成功初始化:
3.说明
1.每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
这区别于构造函数,构造函数体内可以多次赋值。
//构造函数✅:
class Date {
public:
Date(int year, int month, int day){
_year = year;
_month = month;
_day = day;
_year = 2000; //多次赋值
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(1,1); //d1初始化的结果为2000-1-1,而非2023-1-1
return 0;
}
//初始化列表❎,编译报错:
class Date {
public:
Date(int year, int month, int day)
:_year(year)
,_month(month)
,_day(day)
,_year(2000) //多次赋值
{}
private:
int _year;
int _month;
int _day;
};
2.类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量 const成员变量 自定义类型成员(且该类没有默认构造函数时)
3.尽量使用初始化列表初始化。
其实,无论是用初始化列表,还是函数体内初始化,对于内置类型都是无所谓的。
但对于自定义类型成员变量,一定要使用初始化列表初始化。不然连编译都通过不了。
4.初始化列表中的初始化顺序:
为成员变量在类中的声明次序,
与其在初始化列表中的先后次序无关。
class Date {
public:
Date()
:_month(1) //不是这个次序
,_day(1)
,_year(2023)
{
}
private: //看的是这里声明的次序
int _year;
int _month;
int _day;
};
不过,还是建议 声明的顺序和初始化列表的顺序 保持一致,不易错🥰
初始化列表就讲到这里。
可以发现,初始化列表 并不是 来取代构造函数的,
它是来辅助构造函数,让初始化变量 更灵活好用的。