前言:这个知识点的细节比较多,且有些细节不太容易理解,要做好准备哟👻
Ⅰ.构造函数的不完美😭
初始化列表,顾名思义,用列表一样的格式将其初始化。
🤔奇怪啊,构造函数的不是可以初始化嘛?为什么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都是2023Date 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-1return 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;
};
不过,还是建议 声明的顺序和初始化列表的顺序 保持一致,不易错🥰
初始化列表就讲到这里。
可以发现,初始化列表 并不是 来取代构造函数的,
它是来辅助构造函数,让初始化变量 更灵活好用的。