文章目录
一、前言
在学构造函数的初始化列表之前也懂得构造函数的概念和使用方法
链接:构造函数的概念
使用?肯定是懂概念再自己慢慢尝试去使用了
假设我有一个类:
class Date
{
public:
Date (int year = 2000, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1();
Date d2(2024, 2, 20);
return 0;
}
可以看到我的类的构造函数是一个全缺省构造函数,实例化对象时可以不给参数,也可以指定参数
但我们可以想到一个问题,假设我在构造函数里面多次给值呢,算是多次初始化一个变量?但变量只能初始一次啊
这时就出现了歧义,例如:
Date (int year = 2000, int month = 1, int day = 1)
{
_day = 10;
_month = 2;
_year = year;
_month = month;
_day = day;
}
假设函数内部是进行初始化的
如果构造函数内前两行算是初始化两个变量了,那后面又初始化算什么?
最后我们只能认为假设失败,如果判断哪一个最先给值是初始化,后面的算赋值才行
但CPP祖师爷给出了另一个概念与方法叫做初始化列表
二、初始化列表的使用方法与注意点
1.使用方法
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
初始化列表是介于函数参数列表后面括号与函数体大括号之间的
例如:
Date (int year = 2000, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{
//因为上面初始化给值了,下面可以不写
}
冒号开始,逗号分割,成员变量后面跟括号给初始值或表达式
这样子就把想不明白的是不是多次初始给解决了
初始化列表中每个成员变量只能出现一次,因为每个变量也只有一次初始化
2.注意点
玩初始列表得注意一些小点:
1.、每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2、引用成员变量、const成员变量、自定义类型成员(且该类没有默认构造函数时)必须放在初始列表里初始
3、尽量使用初始列表初始化,不管是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
(对于一个类里面声明另一个类的对象,不走初始列表怎么来初始化?是否会自动调用在于另一个类有没有默认构造函数)
4、成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
这几个不难理解,第一点初始化只能一次是必然的
1.、每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2、引用成员变量、const成员变量、自定义类型成员(且该类没有默认构造函数时)必须放在初始列表里初始
引用成员变量:
引用类型必须进行初始,可以走初始化列表,当然可以给缺省值,只不过比较麻烦,需要全局的了来给缺省
const成员变量:
const成员变量也必须进行初始,const修饰的变量已经不能修改了,只能初始化的时候给一个固定的值
只会走初始列表,缺省值只不过是在不给值是默认那个值初始化,也是走初始化的
自定义类型成员:
自定义类型成员现阶段通常是另外一个类对象或结构体那些
如果不走初始化列表在构造函数体里面给值通常行不通,只能初始一次,再次给只能算是新开一个同名的类对象而已
对于没有默认构造函数的自定义类型成员如果不走初始化列表则会报错无法运行
成员变量的缺省值l例如:
假设M是全局变量
private:
int& a = M;
const int b = 10;
缺省值有时候挺好用的,编译器生成的默认构造函数,实例化对象时不给值就好用
但是上面的引用也看到一个弊端,我不可能每次实例化的对象都是指向同一个全局变量吧
(自定义类型成员使用缺省值的话,如果先建立一个全局的对象再当成缺省值则是最笨的方法)
3、尽量使用初始列表初始化,不管是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
自定义类型成员走初始化列表初始化很重要,这样可以走自定义类型的构造函数给数值
这种方法能灵活的使用自定义类型,不用因为缺省值而固定
4、成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
例如:
class Date
{
public:
Date (int year = 2000, int month = 1, int day = 1)
:_year(year)
,_month(month)
,_day(day)
{}
private:
int _day;
int _year;
int _month;
};
这里的初始化顺序是下面三个的声明,顺序为:_day、_year、_month
这样也可以看到初始顺序确实是声明的顺序
三、缺省值简单使用
这简单使用是根据类实例化对象时调用构造函数的玩法
简单看看就行了,假设:
Time类:
class Time
{
public:
Time(int s, int m)
:_s(s)
,_m(m)
{}
private:
int _s;
int _m;
};
Date类:
class Date
{
public:
Date(int year = 2000, int month = 1, int day = 1)
:_year(year)
, _month(month)
, _day(day)
{}
private:
int _day;
int _year;
int _month;
Time d = {1, 2};
};
以上代码能运行,根据类实例化对象的方法,如果只有一个参数时:
class Tmp
{
public:
Tmp (int num = 0)
:_num(num)
{}
private:
int _num;
};
int main()
{
Tmp t1(5);
Tmp t2 = 10;
return 0;
}
主函数main里面实例化对象的两种方法,都在走的构造函数,这是编译器优化过后的
其实第二种过程应该是先使用10建立一个临时对象,在这个临时对象基础上走拷贝构造初始t2
优化也是挺好的
可以看到:
当类构造函数参数列表是多个时,可以这样子:
Time d = {1, 2};
如果是当一个类里面的缺省值是,这个方法也挺好的
总结
初始化列表是介于函数参数列表后面括号与函数体大括号之间的
冒号开始,逗号分割,成员变量后面跟括号给初始值或表达式
初始化列表注意:
1.、每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2、引用成员变量、const成员变量、自定义类型成员(且该类没有默认构造函数时)必须放在初始列表里初始
3、尽量使用初始列表初始化,不管是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化。
(对于一个类里面声明另一个类的对象,不走初始列表怎么来初始化?是否会自动调用在于另一个类有没有默认构造函数)
4、成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
缺省值是当没有传值初始化时最后的初始值(后备隐藏能源启动!)
先知道构造函数的简单使用再懂得初始列表
加油吧少年