21天学会C++:Day12----初始化列表
· CSDN的uu们,大家好。这里是C++入门的第十一讲。
· 座右铭:前路坎坷,披荆斩棘,扶摇直上。
· 博客主页: @姬如祎
· 收录专栏:C++专题

目录
1. 初始化列表
1.1 引入
1.2 初始化列表
1.3 初始化列表的注意事项
1.4 初始化列表成员变量的初始化顺序
2. 同一表达式连续构造的优化
3. 静态成员变量与静态成员函数
4. 内部类
1. 初始化列表
1.1 引入
还记得我们之前在学习类和对象的时候是怎么给类的成员变量 "初始化" 的吗?我们是通过构造函数在函数体内部给成员变量赋值的。为什么说是赋值而不是说初始化呢?因为成员变量的初始化就不是在函数体内部完成的!而是一个叫初始化列表的东西中完成的!
我们定义了一个类A,在构造函数中我们对成员变量进行了多次赋值!以此来证明构造函数的函数体不是对成员变量进行初始化的地方。
class A
{
public:A(int x = 0, int y = 0){_x = x;_x = 5;_y = y;}
private:int _x;int _y;
};int main()
{A a(2, 3);return 0;
}
1.2 初始化列表
我们先来看看初始化列表的语法吧:
以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
比如下面的代码:
class A
{
public:A(int x = 0, int y = 0):_x(x),_y(y + x){}
private:int _x;int _y;
};
在括号里面也是可以写表达式的哦!
我们的C++祖师爷为什么要设计出初始化列表呢?那肯定是因为函数体内的赋值无法对所有类型的成员变量进行赋值。我们来看下面的代码:

我们看到类的成员变量如果有const 修饰的变量或者引用,在构造函数的函数体内部赋值就会直接报错了!根据报错提示,我们也能够隐隐猜到构造函数的函数体不是成员变量初始化的地方!
对于const修饰的变量 和引用都有一个共同的特性:在定义的时候必须初始化!我们又知道了初始化列表才是成员变量真正定义的地方!所以const 修饰的变量和引用就必须在初始化列表中初始化:这样的代码就没有什么问题啦!
class A
{
public:A(int x = 0, int y = 0):_x(x),_y(y){}
private:const int _x;int& _y;
};
1.3 初始化列表的注意事项
我们来看看初始化列表的注意事项:
1:每个成员变量在初始化列表中最多出现一次。
2:类中包含以下成员,必须放在初始化列表位置进行初始化: 引用成员变量 const成员变量 自定义类型成员,且该类没有默认构造函数时。
我们来看第一点:每个成员变量在初始化列表中最多出现一次。我们现在知道了初始化列表是成员变量定义的地方。如果一个成员变量在初始化列表中出现两次,那么这个变量不就是被定义了两次嘛!这当然是不被允许的!

第二点: 引用变量 与 const 修饰的成员变量为什么只能在初始化列表中初始化你应该已经明白了!那么没有默认构造函数的自定义类型为什么也必须要在初始化列表中初始化呢?
你还记得默认构造函数的定义嘛:默认构造函数就是不用传参就可以直接调用的构造函数。
来看下面的代码:我们定义了类A,自己写了带参的构造函数,那么编译器则不会为类A提供默认构造函数。我们又定义了类B,成员变量是自定义类型。我们尝试在B的构造函数中给成员变量赋值。
class A
{
public:A(int x, int y):_x(x),_y(y){}
private:int _x;int _y;
};class B
{
public:B(A a){_a = a;}private:A _a;
};
我们编译器直接报错了:类 A 不存在默认构造函数!为什么会报这个错误呢?我们知道初始化列表是成员变量定义的地方,那么类B的成员变量会在初始化列表中有类似这样的定义:A _a。即在定义的过程中他会去调用A的默认构造函数,但是因为 A 没有默认构造函数自然就会报错了!
也就是说:对于自定义类型的成员变量,如果该成员变量没有显示在初始化列表中初始化, 就会调用该自定义类型的默认构造函数。
那如果我们在初始化列表中显示初始化,就会调用对应的构造函数对自定义类型的成员变量进行初始化。

1.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();return 0;
}
A:输出 1 1
B:程序崩溃
C:编译不通过
D:输出 1 随机值
答案是D哦!初始化列表的初始化顺序并不是按照初始化列表中的书写顺序来的!而是按照成员变量的声明顺序来的!_a2先声明,那么就会先初始化_a2,因此_a2用还未初始化的_a1初始化就是随机值。所以说为了避免出现这样的错误,初始化列表的书写顺序最好与成员变量的声明顺序一致。
最后一点:还记得我们在类中给成员变量缺省值的操作嘛!那个缺省值其实就是给初始化列表初始化的!
2. 同一表达式连续构造的优化
大家觉得下面的代码有没有问题呢?
class A
{
public:A(int x):_x(x){}A(const A& a){_x = a._x;}private:int _x;
};int main()
{A a = 2;return 0;
}
你变异之后发现变异通过并且运行良好!这是为啥呢?整形可以直接赋值给自定义类型?其实不是的啦!这其中发生了隐式类型转化,C++98的语法:单参数的构造函数支持隐士类型转换。所以说 A a = 2;其实是用 2 这个整形调用了A的构造函数,构造了一个A的对象,然后在通过拷贝构造来构造 a 这个对象!
如果你不能理解我们可以拿一个内置类型来举例:
我们有一个整形变量直接将他赋值给一个double变量,看看编译器是怎么做的。

这个道理和 A a = 2;是一样的撒!所以在这一个表达式中调用了构造函数和拷贝构造函数!于是编译器看不下去了!他将这个表达式优化成了直接调用构造函数来构造 a 这个对象。编译器是容忍不了一个表达式出现两个构造函数的调用的!
那你可能不相信我的解释,你说:有没有可能并没有一个临时对象被构造出来,而本来就是用 2 直接构造a这个对象。那我们来看看这个代码:A& a = 2; 报错:非常量引用的初始值必须为左值,也就是说,在给 a 赋值的时候,a引用的是一个右值,自然就报错了。

可是我们如果用的是const的引用呢?发现就没有报错了!我们知道临时对象具有常性,加上const才能对其进行引用。但是这也并不能说明通过2构造了一个A的对象哇!你可能会说a就是引用的2这个右值嘛!
OK,那很巧,C++中有一个关键字能够废除这种隐式类型转换:explicit。如果真如你所有说 a 能够引用 2 这个右值,那么废除 这种隐式类型转换也不会报错吧!

很遗憾他报错了!那么说明这种隐式类型转换是真实存在的!
3. 静态成员变量与静态成员函数
假设我们现在有这样一个需求,需要你统计出一个类实例化出了多少个对象!你激动的说到,这个我会呀!对象实例化不是都要调用构造函数吗,对象销毁不是都要调用析构函数嘛,那么我只要维护一个全局变量count,然后再构造函数里对count++,析构函数里面对count--,不就大功告成了吗?是的你很厉害,也很聪明!
int _count;class A
{
public:A(int x = 0):_x(x){_count++;}A(const A& a){_x = a._x;_count++;}~A(){_count--;}private:int _x;
};
可是有一天,来了一个程序员,看到了这个count,这是干什么用的?一下就给你把count给改了!显然不行!
原因还是在与全局变量无法得到有效的封转保护!于是我们的静态成员变量他来了!静态成员变量可以理解为全局变量的封转,使得变量更好被维护。静态成员变量应该怎么初始化呢?很简单:

class A
{
public:A(int x = 0):_x(x){_count++;}A(const A& a){_x = a._x;_count++;}~A(){_count--;}private:int _x;static int _count;
};
静态成员变量属于一个类,不单独属于任何一个对象!静态成员变量是所有对象共享的。访问静态变量的时候只要突破类域和访问权限就可以直接访问。可以通过类域,也可以通过对象访问。

但是吧静态成员变量设置为共有又有一点不太好!因此我们需要提供一个函数来访问静态成员变量。

我们提供了一个普通的成员函数,发现必须需要有对象才能访问这个函数!但是我们的需求是随时访问这个函数,因此我们将这个函数变为静态函数!静态函数没有this指针,不能访问非静态的成员变量(因为函数访问普通的成员变量就是通过this指针访问的嘛)。通过对象或者类名都可以直接访问。前提是有权限哦!

4. 内部类
内部类 概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。
内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。
特性:
1. 内部类可以定义在外部类的public、protected、private都是可以的。
2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。
3. sizeof(外部类) = 外部类,和内部类没有任何关系。
内部类C++用的不多,Java喜欢用内部类。内部类供大家了解一下吧!

相关文章:
21天学会C++:Day12----初始化列表
CSDN的uu们,大家好。这里是C入门的第十一讲。 座右铭:前路坎坷,披荆斩棘,扶摇直上。 博客主页: 姬如祎 收录专栏:C专题 目录 1. 初始化列表 1.1 引入 1.2 初始化列表 1.3 初始化列表的注意事项 1.…...
OpenAI开发系列(二):大语言模型发展史及Transformer架构详解
全文共1.8w余字,预计阅读时间约60分钟 | 满满干货,建议收藏! 一、介绍 在2020年秋季,GPT-3因其在社交媒体上病毒式的传播而引发了广泛关注。这款拥有超过1.75亿参数和每秒运行成本达到100万美元的大型语言模型(Large …...
Gson - 一个Java序列化/反序列化库
官网 GitHub - google/gson: A Java serialization/deserialization library to convert Java Objects into JSON and back 项目简介 一个Java序列化/反序列化库,用于将Java对象转换为JSON和返回JSON。 Gson is a Java library that can be used to convert Java…...
6-1 汉诺塔
汉诺(Hanoi)塔问题是一个经典的递归问题。 设有A、B、C三个塔座;开始时,在塔座A上有若干个圆盘,这些圆盘自下而上,由大到小地叠在一起。要求将塔座A上的圆盘移到塔座B上,并仍按同样顺序叠放。在…...
Linux之initd管理系统(海思、ZYNQ、复旦微)添加密码登录验证
设置root用户密码:passwd命令设置密码,即修改/etc/passwd文件 一、串口提示输入用户名密码方法 修改 /etc/inittab 方法一: 增加: ::askfirst:-/bin/login 注释: #::respawn:/sbin/getty -L ttyS000 115200 vt…...
怎么更改代理ip,代理ip如何切换使用?
我们要如何使用HTTP代理,对它进行切换使用呢? 如果你购买了青果网络的HTTP代理,可以在文档这边获取使用方法: 可以在这里调试: 也可以在这里选择key提取。 如果有的朋友们想利用利用python,每隔30秒使用API…...
【C++从0到王者】第三十三站:AVL树
文章目录 前言一、AVL 树的概念二、AVL树的实现1. AVL树的结点定义2. AVL树的插入之插入部分3. AVL树的插入之平衡因子的改变4. AVL树的插入之左旋5. AVL树的左旋抽象图6.AVL树的右旋抽象图7. AVL树的双旋8. AVL树的右左双旋9. AVL树的右左双旋的本质10. AVL树的左右双旋11. AV…...
手机机型响应式设置2
window.screen.height:屏幕高度 window.innerHeight:视口高度(去除浏览器头尾的高度) document.body.clientHeight:内容高度 vh:网页视口高度的1/100 vw:网页视口宽度的1/100 vmaxÿ…...
uni-app 之 解决u-button始终居中问题
uView中u-button始终居中问题如何解决的简单方法? 1:给该元素margin-right: 0;可以达到向右靠齐; 2:给该元素的父元素设置float: right image.png <u-button style"width: 50px; margin-left: 0;" plain"t…...
Python日期处理库:掌握时间的艺术
💂 个人网站:【工具大全】【游戏大全】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 日期和时间在计算机编程…...
JOSEF约瑟 智能电流继电器KWJL-20/L KWLD26 零序孔径45mm 柜内导轨式安装
KWJL-20智能电流继电器 零序互感器: KWLD80 KWLD45 KWLD26 KWJL-20 一、产品概述 KWJL-20系列智能剩余电流继电器(以下简称继电器)适用于交流电压至660V或更高的TN、TT、和IT系统,频率为50Hz。通过零序电流互感器检测出超过…...
NLP技术如何为搜索引擎赋能
目录 1. NLP关键词提取与匹配在搜索引擎中的应用1. 关键词提取例子 2. 关键词匹配例子 Python实现 2. NLP语义搜索在搜索引擎中的应用1. 语义搜索的定义例子 2. 语义搜索的重要性例子 Python/PyTorch实现 3. NLP个性化搜索建议在搜索引擎中的应用1. 个性化搜索建议的定义例子 2…...
演唱会没买到票?VR直播为你弥补遗憾
听说周杰伦开了演唱会?没买到票的人是不是有着大大的遗憾呢?很多时候大型活动、演唱会都会因为场地限制而导致很多人未能有缘得见,而且加上票价成本高,“黄牛票”事件频出,我们的钱包受不住啊!!…...
myabtis的缓存级别
文章目录 MyBatis缓存的区别是什么作用范围方面有哪些差异生命周期数据进行了存储缓存的优缺点 MyBatis缓存的区别是什么 MyBatis 提供了一级缓存和二级缓存,这两者的主要区别在于其作用范围和生命周期。 一级缓存:一级缓存是 SqlSession 级别的缓存。…...
gin框架再探
Gin框架介绍及使用 | 李文周的博客 (liwenzhou.com) lesson03_gin框架初识_哔哩哔哩_bilibili 1.路由引擎 //路由引擎 rgin.Default() 2.一些http请求方法 get post put delete等等 遇到什么路径,执行什么函数 r.GET("/hello",func{做你想做的事返回…...
经典算法-----约瑟夫问题(C语言)
目录 前言 故事背景 约瑟夫问题 环形链表解决 数组解决 前言 今天我们来玩一个有意思的题目,也就是约瑟夫问题,这个问题出自于欧洲中世纪的一个故事,下面我们就去通过编程的方式来解决这个有趣的问题,一起来看看吧!…...
代码随想录 动态规划Ⅴ
494. 目标和 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - ,然后串联起所有整数,可以构造一个 表达式 : 例如,nums [2, 1] ,可以在 2 之前添加 ,在 1 之前添加 - …...
驱动DAY9
驱动文件 #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #include <linux/fs.h> #include <linux/io.h> #include <linux/device.h> #incl…...
03贪心:摆动序列
03贪心:摆动序列 376. 摆动序列 局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值。 整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列。…...
javascript获取元素在浏览器中工作区域的左、右、上、下距离,或带滚动条的元素在页面中的大小
//获取元素在包含元素框中的大小 //第1个函数为获取元素在包含元素中左内边框的距离 function getELementLeft(element){//获取元素在包含元素左边距离var actualeftelement.offsetLeft;//获取元素的上级包含元素var currentelement.offsetParent;//循环到一直没有包含元素whil…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
