系列文章目录
欢迎大家订阅我的《计算机底层原理》、《深度解析C++》系列专栏、我会持续为大家输出优质文章,能够帮助到大家就是对我最大的鼓励!😊
目录
前言
这篇文章主要为大家讲解C++当中的初始化列表,这部分知识作为C++当中最基础的部分,虽然不难但是很容易被同学们忽略,C++毕竟是贴近操作系统底层的语言,很多语法内容是需要我们去理解的,不能单纯地死记硬背,例如为什么初始化列表就一定高效呢?有些变量为什么一定要使用初始化列表呢?再比如内联函数为什么可以节约系统开销呢?这些知识点你真的理解了吗?如果我们一味地依靠记忆学习编程的话,将会非常的吃力,各位小伙伴可以去翻阅一下我之前的文章,相信大家一定会有收获!
一、什么是初始化列表
1.语法结构
初始化列表是在构造函数对类的成员变量进行初始化的工具,初始化列表是在C++中用于在构造函数中初始化成员变量的一种机制。它是在构造函数的参数列表后,构造函数体之前的部分,通过冒号(:)引入的。
cppClassName::ClassName(parameters) : member1(value1), member2(value2), ... { // 构造函数体 }
2.为什么要使用初始化列表
1.效率:
初始化列表允许直接初始化成员变量,这个过程会在对象创建的过程直接完成,如果我们在构造函数的函数体当中进行赋值操作的话效率不如直接使用初始化列表,因为对象的创建通过调用new这个关键字来创建,首先对象创建好之后,new关键字会为这个对象分配空间,并且自动调用构造函数进行对这个对象的初始化,然后返回这个对象的地址。
可是如果我们使用初始化列表的话,在对象创建的那一刻对象的成员变量就完成了初始化的操作,效率大大提升。那么为什么会这样呢?其实很好理解初始化列表定义在了构造函数函数体之前,所以初始化列表对这个对象进行初始化的时候,准确地来说这个时候构造函数还没有调用,而且初始化列表的初始化顺序取决于变量在类当中的声明顺序,和初始化列表当中的初始化顺序无关。这一点非常重要!
如果文字的力量不够深刻的话,下面我是用代码来为大家进行解读,当我们创建一个对象的时候,对象的成员变量需要被赋予初始值,在C++当中我们可以使用构造函数来执行这一步操作,我们有两种方法,一是构造函数体内逐一赋值,二是使用初始化列表那么为什么使用初始化列表就一定更高效呢?请看代码。
cppclass MyClass { public: int x; double y; MyClass(int a, double b) { x = a; y = b; } };
如果我们定义了这样一段代码,那么进行对象初始化的时候就会进行如下操作:
1)利用new关键字进行内存的分配
2)new这个关键字会自动调用构造函数来为对象当中的字段进行初始化
但是当我们使用初始化列表的时候
cppMyClass(int a, double b) : x(a), y(b) {}
这种方式的好处在于直接创建对象的时候就已经完成了初始化,就像我刚才所说的一样,对象创建好的一瞬间就已经完成了初始化,就不需要再调用构造函数进行逐一初始化了,这里大家可能会有一个误区认为初始化列表是构造函数的一部分,这是大错特错的!!!它们两个虽然在语法结构上定义在了一起,但是其实它们是两个独立的过程,初始化列表对对象进行初始化的时候,构造函数还没有真正地开始执行呢!所以我们可以思考一下,我们不需要调用构造函数就能够为对象进行初始化了,你说这是不是高效呢?
2.成员常量变量和引用
对于某些情况下的成员变量,例如常量或者引用,必须使用初始化列表进行初始化,不能够在构造函数的函数体内进行赋值初始化。
1.常量成员变量
常量成员变量是在声明的时候就被赋予初始值的,并且一旦被赋值就不能够在被修改,所以自然而然地也就不能够再函数体内进行赋值运算,因为一旦进入构造函数体常量成员变量就不能够再被修改。
cppx; MyClass(int a) { // 错误:常量成员变量不能在构造函数体内赋值 x = a; } };
2.引用成员变量
引用成员变量必须再构造函数的初始化列表当中进行初始化,一旦引用被初始化,它将一直引用同一个对象,无法在构造函数体内重新赋值为另一个对象,如果我们尝试在构造函数体内对引用变量进行赋值,编译器就会报错。
cppclass MyClass { public: int& y; // 错误:引用成员变量必须在初始化列表中初始化 MyClass(int a) { y = a; } };
总结一下,常量和应用之所以只允许使用初始化列表进行初始化就是因为编译器不允许程序员在构造函数体内对他们进行操作,因为这些成员都不允许被修改,如果允许程序员在构造函数体内对他们进行操作的话,很有可能会引发很多的语义错误、和不必要的麻烦,所以编译器强制规定这些成员必须使用初始化列表进行初始化,不允许程序员对他们在构造函数体内进行额外的危险操作。
3.对象成员
大家可以观察下面的代码,对象成员必须使用初始化列表进行初始化,因为如果我们不在初始化列表当中对这个对象和进行初始化的话,那么Date类的构造函数会自动去调用_t 的默认构造函数来进行初始化,但是这个时候Time类没有默认构造函数,编译器就会报错引发未定义行为。所以初始化对象成员要么自动调用默认构造函数,如果没有默认构造函数的话,就必须使用初始化列表进行初始化
这里有一很重要的一点,就是默认构造函数是什么?可能很多的小伙伴不明白,C++当中的默认构造函数一共有三类一类是我们不写编译器自动为我们生成的,另一类是无参构造函数再一类是全缺省参数的构造函数都叫做默认构造函数,当我们显示地定义了构造函数编译器就不会为我们生成默认构造函数了,除非我们自己写一个默认构造函数,所以这个时候对象成员初始化的时候如果要依靠编译器自动调用它的默认构造函数,他必须有默认构造函数才可以。如果没有的话那么就必须在初始化列表当中进行初始化才可以。
cppclass Time { public: int _hour; int _minute; int _second; // 构造函数 Time(int hour, int minute, int second) : _hour(hour), _minute(minute), _second(second) { // 构造函数体 } }; class Date { private: Time _t; public: // 使用初始化列表调用 Time 类中的构造函数 Date(int hour, int minute, int second) : _t(hour, minute, second) { // 构造函数体 } };
二、使用场景和实例
1.常规成员变量初始化
cppprivate: int _a; double _b; public: // 使用初始化列表初始化成员变量 Example(int a, double b) : _a(a), _b(b) { // 构造函数体 } };
2.对象成员变量初始化
cppclass Time { public: int _hour; int _minute; int _second; // 构造函数 Time(int hour, int minute, int second) : _hour(hour), _minute(minute), _second(second) { // 构造函数体 } }; class Date { private: Time _t; public: // 使用初始化列表调用 Time 类中的构造函数 Date(int hour, int minute, int second) : _t(hour, minute, second) { // 构造函数体 } };
3.成员常量或者引用初始化
cppclass Example { private: const int _constantValue; int& _referenceValue; public: // 构造函数初始化列表 Example(int constantValue, int& referenceValue) : _constantValue(constantValue), _referenceValue(referenceValue) { // 构造函数体 } };
三、注意事项
1、成员变量初始化顺序:
初始化列表中的成员变量初始化顺序与它们在类中声明的顺序有关,而不是出现在初始化列表中的顺序。
2、避免使用未初始化的成员变量:
在初始化列表中确保所有成员变量都被初始化,避免使用未初始化的成员变量,尤其是对于常量和引用成员。
3、常量成员变量:
常量成员变量必须在初始化列表中进行初始化。
4、引用成员变量:
引用成员变量必须在初始化列表中与某个对象绑定。初始化列表是C++中一个重要的概念,尤其在构造函数中的使用,它可以提高代码的效率,同时确保对象在被创建时具有正确的初始状态。
总结
C++当中的初始化列表就为大家讲解到这里,希望读完这篇文章大家已经能够彻底搞懂初始化列表到底是怎么一回事了,希望大家读我的文章能够所有收获!