C++ 类和对象(初始化列表)

embedded/2024/11/28 7:49:56/

目录

一、前言

二、正文

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?)

因此,从这里我们知道自定义类型成员变量必须调用该成员变量的默认构造函数进行初始化,那么你既没有默认构造函数又不想在初始化列表中进行初始化。你不报错,谁报错?

那么这个问题该怎么解决了呢?

  1. 如果该自定义类型成员变量在没有默认构造函数的前提下,必须在初始化列表中完成初始化。
  2. 如果该自定义类型成员变量,一开始就有默认构造函数,那么是否在初始化列表中完成初始化,完全看大家的心情。其实既然已经有默认构造函数了,是否在初始化列表中初始化已经无所谓了。

这个时候可能有小伙伴们要问了,如果该自定义类型成员既写了默认构造函数,又在初始化列表中初始化了,编译器会怎么做呢?

其实如果我们在初始化列表中已经对该自定义类型成员完成初始化了,那么默认构造函数的缺省值也就没用了,这就类似于我们有一个默认构造函数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.全篇总结

哈哈,懒画图了,直接把课件给你们扣下来了。

三、结语

今天的初始化列表就分享到这了 ,帅哥美女们我们下次再见~


http://www.ppmy.cn/embedded/141135.html

相关文章

Javaweb 前端 HTML css 案例 总结

顶部导航栏 弹性布局 搜索表单区域 表单标签 表单标签&#xff0c;表单项 复选&#xff0c;一次选多个 隐藏域&#xff0c;看不到&#xff0c;但会传参数 text输入框 radio单选 男女&#xff0c;是 前端页面上显示的值 搜索表单区域 button 按钮 表格数据展示区域 fo…

PHP 超级全局变量

超级全局变量是指在php任意脚本下都可以使用 PHP 超级全局变量列表: $GLOBALS&#xff1a;是PHP的一个超级全局变量组&#xff0c;在一个PHP脚本的全部作用域中都可以访问。 $_SERVER&#xff1a;$_SERVER 是一个PHP内置的超级全局变量,它是一个包含了诸如头信息(header)、路…

docker搭建socks5代理

准备工作 VPS安全组/策略放行相应端口如启用了防火墙&#xff0c;放行相应端口 实际操作 我们选用“历史悠久”的Dante socks5 代理服务器&#xff0c;轻量、稳定。Github也有对dante进行进一步精简的镜像&#xff0c;更为适宜。github项目地址如下&#xff1a; https://gi…

Educator头歌:离散数学 - 图论

第1关&#xff1a;图的概念 任务描述 本关任务&#xff1a;学习图的基本概念&#xff0c;完成相关练习。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;图的概念。 图的概念 1.一个图G是一个有序三元组G<V,R,ϕ>&#xff0c;其中V是非空顶点集合&am…

数据结构--图

图 图的基本概念图的存储结构邻接矩阵邻接表十字链表邻接多重表 图的遍历广度优先遍历深度优先遍历 图的应用最小生成树克鲁斯卡尔算法普里姆算法 最短路径迪杰斯特拉算法贝尔曼-福特算法弗洛伊德算法 拓扑排序关键路径 图的基本概念 图是由顶点集合及顶点间的关系组成的一种数…

ArkTS四种渲染控制能力

大家好&#xff0c;我是 V 哥。ArkTS 是 OpenHarmony 框架的一部分&#xff0c;提供了声明式 UI 渲染的能力。下面来对ArkTS中四种渲染控制能力&#xff1a; if/else、ForEach、LazyForEach 和 ContentSlot 详细介绍一下&#xff1a; 1. if/else 渲染控制 if/else 是 ArkTS 提…

第一章:Go 语言概述 2.安装和配置 Go 开发环境 --Go 语言轻松入门

第一章&#xff1a;Go 语言概述 2.安装和配置 Go 开发环境 --Go 语言轻松入门 安装和配置 Go 开发环境相对简单&#xff0c;以下是在不同操作系统上安装和配置 Go 的步骤&#xff1a; Windows 1. 下载 Go 安装包 访问 Go 下载。选择适用于 Windows 的安装包&#xff08;通常…

11.27字节番茄小说后端实习OC面经

本帖暂时只介绍部分面试题及解析 1.介绍一下bean流程 本面试题其实就是考察spring的基础&#xff0c;鉴于是字节&#xff0c;所以还是答得详细一点比较好 Bean的启动流程是Spring框架中的核心机制之一&#xff0c;它基于依赖注入&#xff08;Dependency Injection&#xff0…