目录
一、前言
二、正文
1.初始化列表
1.1初始化的格式
1.2初始化列表的使用
1.2.1引用成员变量初始化
1.2.2const成员变量
1.2.3没有默认构造函数的成员变量必须在初始化列表初始化
2.成员变量声明处给缺省值
一、前言
前面我们已经用所学知识运算符重载写了一个日期计算器:https://blog.csdn.net/yiqingaa/article/details/143954136?
现在让我们开启C++知识新篇章——初始化列表吧
二、正文
1.初始化列表
- 之前我们实现构造函数时,初始化成员变量主要使用函数体内赋值,构造函数初始化还有一种方式,就是初始化列表,初始化列表的使用方式是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个“成员变量“后面跟一个放在括号中的初始值或表达式。
- 每个成员变量在初始化列表中只能出现一次,语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。
- 引用成员变量,const成员变量,没有默认构造的类类型变量,必须放在初始化列表位置进行初始化,否则会编译报错。
- C++11支持在成员变量声明的位置给缺省值,这个缺省值主要是给没有显示在初始化列表初始化的成员使用的。
- 尽量使用初始化列表初始化,因为那些你不在初始化列表初始化的成员也会走初始化列表,如果这个成员在声明位置给了缺省值,初始化列表会用这个缺省值初始化。如果你没有给缺省值,对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器,C++并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数,如果没有默认构造会编译错误。
- 初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持一致。
- 初始化列表总结:
- 无论是否显示写初始化列表,每个构造函数都有初始化列表;
- 无论是否在初始化列表显示初始化,每个成员变量都要走初始化列表初始化;
1.1初始化的格式
//这里我们假设定义一个Date类名 class Date { public://这个是我们常规使用函数体内赋值的方法实现构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//下面是我们用初始化列表实现构造函数Date():_year(1),_month(1),_day(1){} private:int _year;int _month;int _day; };
我们仔细观察不难得出初始化列表的格式是:
类名() :成员变量1(表达式1) ,成员变量2(表达式2) ,成员变量3(表达式3) ,成员变量4(表达式4) ... {} //值得注意的是初始化列表是{}上面定义的而非{}内定义//其次就是编译器运行逻辑循序是先运行{}上面的,再访问//{}里面的。
1.2初始化列表的使用
//这里我们假设定义一个Date类名 class Date { public://这个是我们常规使用函数体内赋值的方法实现构造函数Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//下面是我们用初始化列表实现构造函数Date():_year(1),_month(1),_day(1){} private:int _year;int _month;int _day; };
上面这串代码中展现了函数体内赋值和初始化列表实现构造函数的区别。但是你可能有些疑惑:这也没什么不同啊,都一样能运行为什么还需要了解初始化列表呢,不是多此一举吗?
其实,但看这幅图上的代码段确实看不出什么区别,但是其实有些特殊成员变量必须要用初始化列表进行初始化。
例如:1、引用成员变量/2、const成员变量/3、没有默认构造函数的成员变量
上面这三个特殊成员变量都必须用初始化列表初始化。
1.2.1引用成员变量初始化
#include<iostream> using namespace std; class Date { public:Date(int& x, int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day),_ref(x){cout << "Date()" << endl;} private:int _year;int _month;int _day;int& _ref;//引用成员变量初始化 }; int main() {int i = 0;Date d1(i);return 0; }
上面这两幅图是我们为引用成员变量使用初始的结果,那么如果我们在初始化列表中显示写引用成员变量会发生什么呢?
这里我们可以看出,如果我们有引用类成员变量,我们没有在初始化列表中为该成员变量初始化的话,编译器会报错(该成员必须初始化之类的警告)。这不可避免,因为C++设定就是如此。
这时你可能又有疑惑了,我非要使用初始化列表的方式初始化引用成员变量(_ref)吗?我偏使用原来的方法初始化(之前我们是使用函数体内赋值实现初始化的)那让我们看看用原来的办法行不行呢:
很遗憾,很明显这是行不通的,大家不必纠结为什么不可以,只要记住就行。毕竟人家C++是这么规定的。
1.2.2const成员变量
从这里可以看出,初始化列表中没有初始化const成员变量,下面报的错误和上面引用成员变量不在初始化列表中初始化是差不多的,因此const成员变量的使用规则都是和上面引用类成员都是大同小异的。大家可以和上面1.2.1引用变量进行类比。
1.2.3没有默认构造函数的成员变量必须在初始化列表初始化
#include<iostream> using namespace std; class Time { public:Time(int hour)//这里我们没有给hour缺省值,意味着这里我们必须手动传参才能完成Time类对象的初始化:_hour(hour)//即这里不是默认构造参数,默认构造函数可以概括为不需要传实参就可以用的构造函数就是默认构造函数{cout << "Time()" << endl;} private:int _hour; }; class Date { public:Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day),_t(1){cout << "Date()" << endl;} private:int _year;int _month;int _day;Time _t; //自定义类型Time }; int main() {int i = 0;Date d1;return 0; }
从这里看出当我们的没有默认构造函数的成员变量(_t)在初始化列表中初始化后程序正常运行了。那么如果_t没有在初始化列表中,完成初始化会出现什么情况呢?
我们在构造函数那一节讲过了自定义类型成员变量必须调用该成员变量的默认构造函数进行初始化,否则就会报错。(忘了的小伙伴可以回顾一下往期分享构造函数https://blog.csdn.net/yiqingaa/article/details/143490439?)
因此,从这里我们知道自定义类型成员变量必须调用该成员变量的默认构造函数进行初始化,那么你既没有默认构造函数又不想在初始化列表中进行初始化。你不报错,谁报错?
那么这个问题该怎么解决了呢?
- 如果该自定义类型成员变量在没有默认构造函数的前提下,必须在初始化列表中完成初始化。
- 如果该自定义类型成员变量,一开始就有默认构造函数,那么是否在初始化列表中完成初始化,完全看大家的心情。其实既然已经有默认构造函数了,是否在初始化列表中初始化已经无所谓了。
这个时候可能有小伙伴们要问了,如果该自定义类型成员既写了默认构造函数,又在初始化列表中初始化了,编译器会怎么做呢?
其实如果我们在初始化列表中已经对该自定义类型成员完成初始化了,那么默认构造函数的缺省值也就没用了,这就类似于我们有一个默认构造函数Date(int year=1,int year=1,int day=1)。现在我们定义实例化对象Date d1(2024,11,25);因为我们已经传参了,这个时候缺省值也就没用了或者说我们传的实参优先级更高。
2.成员变量声明处给缺省值
还记的上面我们写的初始化列表总结中:无论是否在初始化列表显示初始化,每个成员变量都要走初始化列表初始化;这句话吗?
其实在我们为成员变量初始化的时候,即使该成员变量没有出现在初始化列表中,这个成员变量依旧会走初始化列表。
在理解上面这句话之前,先让我们学习一个新的概念:给成员变量声明的地方给缺省值。
我们知道,当我们写出上面这些成员变量的时候,其实并不是定义(定义是需要开空间的)而只是声明,只有我们创建实例化成员,例如Date d1的时候这些成员才会有对应的空间(空间大小取决于各自成员变量的类型例如_year是int类型,那么它的_year的大小就是4个字节)d1的大小是所有成员变量按照结构体内存对齐原则决定的。
既然这只是声明,那我们给一个只是声明的成员给缺省值有什么意义呢?
其实这也是初始化列表的一部分。我们给声明的成员变量一个缺省值,当该成员变量没有显示在初始化列表中完成初始化的,编译器会按照所给缺省值为相应成员变量完成初始化(虽然我们的成员变量没有出现在初始化列表中,其实这个过程也是在初始化列表中完成的,即这个缺省值就是给初始化列表的缺省值)。
如图所示,即使我们const int类成员变量_n没有显示在初始化列表进行初始化,但是由于我们给他一个缺省值2,它依旧就完成了初始化,其实这个过程只是将缺省值代入到了初始化列表中(因为无论是否在初始化列表显示初始化,每个成员变量都要走初始化列表初始化;)
哪怕它不显示在初始化中,实际上还是在初始化列表中完成初始化的。
3.全篇总结
哈哈,懒画图了,直接把课件给你们扣下来了。
三、结语
今天的初始化列表就分享到这了 ,帅哥美女们我们下次再见~