目录
一、前言
二、 初始化的概念区分
三、初始化列表 (重点)
💦初始化列表的概念理解
💦初始化列表的注意事项
四、共勉
一、前言
在之前的博客学习中,我们已经学习了【C++】的六大默认成员函数 ,想必大家已经对构造函数已经比较熟悉了,可是大家是否遇到过,在构造函数后面跟了一个冒号,这个问题让我很是困惑,于是就有了这篇博客。
接下来,我将详细的讲解关于构造函数后面" : "的故事 -------- 初始化列表
二、 初始化的概念区分
在了解初始化列表------" : "之前,我们首先需要知道两个重要的知识:
1. 构造函数是干嘛的?
答: 用于初始化类中的成员变量
2. 什么是初始化?
答: 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值
接下来再来看一段代码:class Date { public: //构造函数 Date(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; };
上面这个Date类是我们之前写过的,这里有一个它的有参构造函数,虽然在这个构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化。构造函数体中的语句只能将其称为【赋初值】,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
class Date { public: Date(int year = 2022, int month = 5, int day = 24) { _year = year; _year = 2023; //第二次赋值 _year = 2024; //第三次赋值 _month = month; _day = day; } private: int _year; int _month; int _day; };
既然构造函数体的语句只能称作为赋初值,现在,可否有一种方式进行初始化呢?即初始化列表初始化。
⭐总结
1️⃣:我们之前写的构造函数其实并不是对成员变量进行初始化而是进行【赋初值】。
2️⃣:如果想要对成员变量进行初始化,需要用到-----------初始化列表
三、初始化列表 (重点)
💦初始化列表的概念理解
【初始化列表】:以一个冒号“ :”开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式
例如如下代码:class Date { public: //构造函数: -->初始化列表初始化 Date(int year = 2023, int month = 11, int day = 2) :_year(year) , _month(month) , _day(day) {} private: int _year; int _month; int _day; };
当然,我可以在初始化列表初始化,也可以在大括号内进行赋值:
Date(int year = 2023, int month = 11, int day = 2) :_year(year) , _month(month) { _day = day; }
💦初始化列表的注意事项
【初始化列表】的注意事项:
1️⃣:初始化列表可以认为就是对象成员变量定义的地方
2️⃣:每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
3️⃣:类中包含以下成员,必须放在初始化列表位置进行初始化:
- 引用成员变量
- const成员变量
- 自定义类型成员(该类没有默认构造函数)
(1)先前我们都知道引用的变量和const变量只能在定义时初始化,而普通的变量在定义时不强求初始化,所以我们就不能按照如下的方式操作:
⭐成员变量为const和引用的时候-----正确的代码为:
class Time { public: // 构造函数 Time(int hour = 12,int min = 11,int s = 10):_hour(hour),_min(min),_s(s) {} void Printf() { cout << "hour为:" << _hour << endl; cout << "min为:" << _min << endl; cout << "s为:" << _s << endl; } private: // 定义时不强求初始化,后面可以再赋值修改 int _hour; // 声明 // 只能再定义的时候初始化 const int _min; int& _s; }; int main() { Time t1; t1.Printf(); return 0; }
(2)自定义类型成员(该类没有默认构造函数)同样也得在初始化列表进行初始化:
class A { public: A(int x) //非默认构造函数,因为要主动传参 :_x(x) {} private: int _x; }; class Date { public: //在初始化列表对自定义类型 _aa 进行初始化 Date(int a) :_aa(a) {} private: A _aa; };
注意这里的条件,一定得是没有默认构造函数的自定义类型成员才得在初始化列表进行初始化,而默认构造函数简单来说就是不需要传参的函数
4️⃣:成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
看下面这道题:class A { public: A(int a) :_a1(a) , _a2(_a1) {} void Print() { cout << _a1 << " " << _a2 << endl; } private: int _a2; int _a1; }; int main() { A aa(1); aa.Print(); }
A、输出1 1 B、程序崩溃 C、编译不通过 D、1 随机值
答案:D
解析:注意我成员变量在类中声明次序就是其在初始化列表中的初始化顺序,既然_a2先声明,则必然进入初始化列表要先执行, _a2(_a1) 。意思是说拿_a1去初始化_a2,不过此时的_a1还是随机值,自然_a2即为随机值,随后执行:_a1(a)。拿a初始化_a1,所以输出的值为1和随机值。
四、共勉
以下就是我对【C++】构造函数后面冒号“:”的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对C++ 类和对象的理解,请持续关注我哦!!!