类和对象(下)--- 初始化列表、explicit、友元、static、匿名对象和内部类

        本篇将会对类和对象的主要知识收尾,先会对构造函数进行补充,分别补充了构造函数体赋值、初始化列表、explicit 关键字,然后介绍 static 成员知识以及友元、内部类还有匿名对象等知识点,目录如下:

目录

1. 构造函数补充

1.1 构造函数体赋值

1.2 初始化列表

1.3 explicit 关键字

2. static 成员

3. 友元

3.1 友元函数

3.2 友元类

4. 内部类

5. 匿名对象

1. 构造函数补充

        前面由于对于默认函数的知识还不够充分,所以就没有将构造函数的剩余部分加上,接下来将会把构造函数的所有知识都进行补充完毕。

1.1 构造函数体赋值

        在创建对象时,编译器通过调用构造函数,给对象中的各个成员变量一个合适的初始值。虽然通过调用构造函数对象就已经存在一个初始值,但是我们不能将其称为对对象中的成员变量进行初始化构造函数体中的语句只能将其称之为赋初值,而不能称为初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。

        如下:

class Date {
public:Date(int year, int month, int day) {_year = year;_month = month;_day = day;// 在调用一次构造函数体时确实将函数进行了多次赋值_year = 1;}
private:int _year;int _month;int _day;
};

1.2 初始化列表

        初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个“成员变量”后面跟一个放在括号中的初始值或表达式

        首先我们先需要探讨为什么需要初始化列表:

        如上,当我们在成员变量中定义了一个由 const 修饰的成员变量的时候,我们可以发现,不管是编译器默认生成的构造函数还是由自己定义的成员函数来说,都编译不成功。这是因为有些成员变量必须在定义的时候初始化,所以我们需要在某个地方将这些必须在定义的时候初始化的变量进行初始化,这就诞生出了初始化列表

        初始化列表的形式如下:

class A
{
public:// 定义以下的构造函数,使得不会存在默认的构造函数A(int a):_a(a){}
private:int _a;
};class Date {
public:Date(int year, int month, int day, int& x):_year(year),_month(month),_day(day),_p((int*)malloc(sizeof(int)))  // 同样在初始化列表中对指针进行分配空间,_num(1),_ref(x),_a(1){ //... 函数体中也可以在次进行赋值_year = 1;}
private:int _year = 1;  // _year的缺省值,其实是初始化列表的缺失值,//当初始化列表对_year进行处理int _month;		// 那么就不会走_year = 1int _day;int* _p;// 必须走初始化列表const int _num;int& _ref;A _a;  // 没有默认构造的类,有默认构造的类可以不初始化
};

        以上就是初始化列表的一般使用形式了,其中,仍然存在一些需要注意的点:

        1. 每个成员变量只能在初始化列表中出现一次,但是可以在函数体中可以出现多次

        2. 类中包含这些成员,必须放在初始化列表中:引用成员变量、const 成员变量、自定义类型成员(无默认构造函数)

        对于初始化列表的使用

        尽量选择在初始化列表出就将所有成员变量进行初始化,因为无论构造函数是否存在初始化列表,都是会进行初始化列表的操作,虽然成员变量未定义在初始化列表中,但还是会进行操作,不管给的是一些随机值。

        初始化列表进行初始化的顺序,在初始化列表的初始化中,并不是根据从上而下的初始化顺序,而是按照成员变量声明的顺序进行初始化的,所以建议在初始化列表中,按照成员变量声明的顺序写下来

1.3 explicit 关键字

        构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造参数,还具有类型转换的作用(C++11标准后对于多参数同时具有类型转换的作用)。对于这句话的解释如下:

        如上图,对于 a1 来说,我们将3传入进去进行拷贝构造,然后 a1 中的 _a 变量就被赋值为了3,但是当我们将 4 直接赋值给 a2,又是如何进行赋值的呢,因为2作为一个常量,并不属于 A 类型的对象,为什么可以调用类似拷贝构造一样的赋值呢?

        这是因为在对 a2 进行赋值的时候,先会对2进行类型转换,也就是将2构造成一个a的零时对象,然后将 a2 与该临时对象进行调用拷贝构造(也许在Debug的时候,发现并不会调用拷贝构造,那是因为编译器将其优化了,同一个表达式连续步骤的构造,很可能会合二为一),所以最后 a2 中的成员变量被拷贝成功。(但是最好将a2需要赋值的常量数字设置为与a2成员对象相同类型的变量,比如a2的成员变量为int型,那么最好传入一个int型的对象,最好不要传入其他类型的参数,有些编译器可能会报错)

        对于 a3 来说,当成员变量为什么常引用也可以赋值成功呢,这是因为对2进行类型转化的时候生成的是一个临时变量,临时变量具有常性

        当一个类的成员变量有多个时,该如何进行构造呢?如下:

        当类中的成员变量有多个时,我们可以使用类似数组的形式将其赋值给对象(该形式是C++11标准之后才有的形式)。

        当我们不想使用常量直接赋值给一个对象的时候,我们可以使用 explicit 关键字修饰构造函数,这样就不会进行类型转换后调用拷贝构造。如下:

        所以,使用 explicit 修饰的构造函数,将会禁止构造函数的隐式转化

2. static 成员

static 成员的概念:

        声明为 static 的类成员称之为类的静态成员,用 static 修饰的成员变量,称之为静态成员变量;用 static 修饰的成员函数,称之为成员函数。静态成员变量一定要在类外进行初始化。

        静态成员变量所具有的特点之一就是为所有成员共享,不属于某个具体的对象,那我们如何计算一个类在程序中创建了多少个类对象呢。如下程序:

class A {
public:A(int x = 0, int z = 0):_a(x),_ca(z){_num++;}A(const A& a) {_a = a._a;_ca = a._ca;_num++;}static int GetNum() {return _num;}
private:int _a;int _ca;static int _num;
};int A::_num = 0;int main() {A a1(3,5);A a2 = {4, 6};const A& a3 = {5, 7};cout << a1.GetNum() << endl;return 0;
}

        如上程序,就可以计算出一个类构造了多少个对象。

        那么 static 成员的特点又有哪一些呢:

        1. 静态成员为所有类对象所共享,不属于某个具体的对象,存在在静态区;

        2. 静态成员变量必须在类外定义,定义时不添加 static 关键字,类中只是声明;

        3. 类静态成员既可用 类名: :静态成员 或 对象.静态成员来访问

        4. 静态成员函数没有隐藏的 this 指针,不能访问任何非静态成员

        5. 静态成员也是类的成员,受 public、protected、private 访问限定符的限制。

3. 友元

        友元提供了一种突破封装的方式,可以让我们在类外也使用被 private 修饰的变量,但是友元会增加耦合度,破坏封装,所以在很多时候并不建议使用友元。

        友元分为:友元函数友元类

3.1 友元函数

        当我们尝试使用 operator 操作符去构造 operator<< 重载成员函数。虽然确实可以做到,但是因为 cout 的输出流对象和隐含的 this 指针在抢占第一个参数的位置(若使用 operator<< ,那么就是 this 指针占据第一个参数),但实际使用的过程中,我们应该使用 cout 作为第一个参数,所以我们不得不将 operator 重载成全局函数。虽然将 operator 重载成全局函数,但会导致类外没有办法访问类中被 private 修饰的成员,这个时候就需要使用友元来解决问题。

        如下:

        当我们在类域中定义时,就只能将其写成这个样子,但是这并不是 cout 的使用风格,这个时候我们就需要将 operator<< 在类外定义,然后在类域中进行友元声明,如下:

        如上,当我们使用友元修饰一个函数之后,友元函数就可以直接访问类的私有成员了,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加 friend 关键字

        对于友元函数的说明:

        1. 友元函数可以访问类的私有保护成员,但不是类的成员函数

        2. 友元函数不可以使用 const 修饰

        3. 友元函数可以在类定义的任何地方声明不受类访问限定符限制

        4. 一个函数可以是多个类的友元函数

        5. 友元函数的调用和普通函数的调用相同

3.2 友元类

        友元类的所有成员函数都可以是另一个类的友元函数都可以访问另一个类中的所有非公有成员

        但是对于友元类来说,存在以下几点特性:

        1. 友元关系是单向的,不具有交换性

        2. 友元关系不具有传递性,如 a 是 b 的友元,b 是 c 的友元,a 不是 c 的友元

        3. 友元关系不能继承。

如下:

class Time
{friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类public:Time(int hour = 0, int minute = 0, int second = 0): _hour(hour), _minute(minute), _second(second){}
private:int _hour;int _minute;int _second;
};class Date
{
public:Date(int year = 1900, int month = 1, int day = 1): _year(year), _month(month), _day(day){}void SetTimeOfDate(int hour, int minute, int second){// 直接访问时间类私有的成员变量,想要访问,需要在 Date 类中存在 Time 类的对象_t._hour = hour;_t._minute = minute;_t._second = second;}
private:int _year;int _month;int _day;Time _t;
};

        对于如上的函数而言,当我们想要在 Date 类中定义 Time 类的友元时,那么 Time 中也可以访问 Date 类中的成员对象了,但是我们需要注意一个先后的问题,假若我们想要在 Time 中定义与 Date 对象有关的函数时,只能在类中进行声明,而不能进行定义,因为在使用函数时,会在函数以上找有关 Date 对象的定义,而不会向下寻找,所以这时候会报错,解决办法就是将声明与定义分离。

4. 内部类

        内部类:若一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。

如下就是一个内部类:

class A {
public:A(int y =0, int yy = 0):_a(y),_aa(yy){}// 内部类class B {public:void Print(){ // ...}B(int x = 0):_b(x){}private:int _b;};private:int _a;int _aa;
};int main() {A::B b(1);A a(1,2);b.Print();return 0;
}

        如上所示,A 中 B 就是一个内部类,将 B 定义在 A 类中,若想要定义一个 B 类型的对象,我们就需要先通过类 A 加上作用域限定符然后才能访问到类 B。但是若我们想要计算一下 A 类占据的内存大小呢?如下:

        如上图,我们计算出来的结果显示为 8,仅仅只是 A 的两个成员变量的大小。这是因为在 A 中即使定义了一个类,计算大小时,也并不会计算这个类的大小,因为 B 只是在 A 中的一个声明,当 B 被编译器编译后,其实什么也不会产生。

        内部类的特性:

        1. 内部类天生就是外部类的友元类,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的成员。

        2. 内部类可以定义在外部类的 public、private、protected

        3. 内部类可以直接访问外部类中的 static 成员,不需要外部类的对象。

        4. sizeof(外部类) = 外部类,和内部类没有任何关系。

5. 匿名对象

        在Cpp存在的匿名对象就是只定义一次,并且匿名对象的生命周期也是只在定义的这一行中,走过这一行之后声明周期也就结束了。如下:

        当我们 Debug 该程序的时候,发现走过匿名对象之后,该对象就被销毁了。匿名对象的定义方式如上两种,类(),或类(成员变量的参数)。

        匿名函数的作用为:当我们只想使用类中的某个成员函数时,我们就可以直接创建一个匿名对象,然后通过该匿名对象调用该函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/788069.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VUE3——生命周期

Vue3.0中可以继续使用Vue2.x中的生命周期钩子&#xff0c;但有有两个被更名&#xff1a; beforeDestroy改名为 beforeUnmountdestroyed改名为 unmounted Vue3.0也提供了 Composition API 形式的生命周期钩子&#xff0c;与Vue2.x中钩子对应关系如下&#xff1a; beforeCreate&g…

【ROS 笔记5】话题发布与订阅————高级(一)

1. 前言 在使用ros pulisher时, 我们在建立话题 pub = rospy.Publisher(chatter, String, queue_size=10) 我们的目的时将我们的message (string)通过话题发布出去,如:pub.publish(hello_str)。 如果是为了处理单个话题的问题, 我们只用一个pub.publish()去发布就好。 …

matlab学习(一)(3.26-4.1)

一.信息隐藏的概念 信息隐藏是利用一些技术&#xff0c;使得要隐藏的信息是用眼睛看不出变化的。它将秘密信息隐藏在可公开的媒体信息中&#xff0c;使人们凭直观的视觉和听觉难以察觉基存在。 二.空域信息隐藏技术 空域信息隐藏是将信息直接嵌入到图像的像素值中的技术。主要…

洛谷 P2658 汽车拉力比赛

思路&#xff1a;二分BFS 题目的大意就是找出一个难度系数&#xff0c;让到达每一个路标之间的相邻格子的高度之差为难度系数。 所以&#xff0c;想要找到这个难度系数&#xff0c;我们需要不断地枚举数据范围内的数据&#xff0c;然后一个一个试试&#xff0c;全部BFS遍历一…

Mysql 和 Redis 数据怎么保持一致性,怎么实现

MySQL 和 Redis 是两种不同类型的数据库&#xff0c;MySQL 是关系型数据库&#xff0c;而 Redis 是键值存储的非关系型数据库。它们在数据一致性的保证上需要采取不同的策略&#xff0c;尤其是在分布式环境中。以下是一些保持 MySQL 和 Redis 数据一致性的常见策略&#xff1a;…

ISELED-演示项目代码

目录 一、main函数二、点灯函数一、main函数 int main(void) {/* Write your local variable definition here */iseledInitType.crcEnable = 1;iseledInitType.firstLedAdr = 1;iseledInitType.tempCmpEnable = 0;iseledInitType.voltSwing = 0;/*** End of Processor Expert…

简单了解裸眼3D呈现技术

裸眼3D呈现是一种不需要佩戴任何特殊设备&#xff08;如3D眼镜或头盔&#xff09;即可观看到3D效果的技术。这种技术近年来得到了快速发展&#xff0c;为观众带来了更加沉浸式的视觉体验。 实现裸眼3D呈现的关键步骤包括&#xff1a; 创建立体图像源&#xff1a;首先需要有一…

HWOD:记录正负数

一、知识点 1、scanf()的返回值 scanf()返回值类型为int&#xff0c;返回转换成功的个数 有代码int temp; scanf("%d",&temp); 在屏幕输入一个数字&#xff0c;比如5&#xff0c;回车&#xff0c;scanf()返回1 在屏幕输入一个字符或字符串&#xff0c;比…

STM32 M3内核寄存器概念

内容主要来自<<M3内核权威指南>> 汇编程序中的最低有效位&#xff08;Least Significant Bit&#xff09;。LSB是二进制数中最右边的位&#xff0c;它代表了数值中的最小单位。在汇编程序中&#xff0c;LSB通常用于表示数据的最小精度或者作为标志位。 ---------…

人工智能|深度学习——基于Xception算法模型实现一个图像分类识别系统

一、Xception简介 在计算机视觉领域&#xff0c;图像识别是一个非常重要的任务&#xff0c;其应用涵盖了人脸识别、物体检测、场景理解等众多领域。随着深度学习技术的发展&#xff0c;深度卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;简称CNN&#xff…

linux下tar命令的压缩和解压详细使用方法

在Linux系统中&#xff0c;tar命令用于创建、查看、提取和解压 tar 存档文件。以下是 tar 命令的一些常见用法&#xff1a; 压缩文件或目录&#xff1a; tar -czvf archive.tar.gz /path/to/directory # 压缩目录为 .tar.gz 格式的文件tar -czvf archive.tar.gz file1 file2 …

测试人员前期参与设计方案时需要注意什么?

服务的健壮性跟系统设计有很大关系&#xff0c;前期设计时考虑多一些处理逻辑&#xff0c;可以避免后期出现问题带来的损失以及修复问题的成本。 在前期讨论设计方案时测试同学也需要参与&#xff0c;而不只是埋头设计用例和测试&#xff0c;开发同学可能因为思维局限或者思考…

ssm018简易版营业厅宽带系统+jsp

营业厅宽带系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本营业厅宽带系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间…

LeetCode 977 有序数组的平方(双指针法)

给你一个按 非递减顺序 排序的整数数组 nums&#xff0c;返回 每个数字的平方 组成的新数组&#xff0c;要求也按 非递减顺序 排序。 示例 1&#xff1a; 输入&#xff1a;nums [-4,-1,0,3,10] 输出&#xff1a;[0,1,9,16,100] 解释&#xff1a;平方后&#xff0c;数组变为 […

QCustomPlot一、QCustomPlot基础及画图显示

1、QCustomPlot下载 QCustomPlot源码demo 根据需要选择需要的文件&#xff1a; 完整版。QCustomPlot.tar.gz 源代码例子帮助文档&#xff1b; 共享库。QCustomPlot-sharedlib.tar.gz 库编译和使用&#xff1b; 源代码。QCustomPlot-source.tar.gz 源代码 里面包含了很多QCusto…

夜晚兼职好选择:六大副业助你增收

晚上兼职&#xff0c;无疑是许多寻求额外收入人群的理想选择。以下为您精心推荐的六个副业&#xff0c;既适合晚间操作&#xff0c;又能让您在轻松愉悦中赚取额外收益。 网络调查与市场研究&#xff1a;利用晚上的闲暇时光&#xff0c;参与网络调查与市场研究&#xff0c;为企业…

TO-277肖特基二极管 散热效果好 型号齐全

市场对于肖特基二极管的需求非常旺盛&#xff0c;近日常有客户前来东沃电子咨询TO-277B 封装系列肖特基二极管选型、价格、交期、现货等方面的问题。东沃电子推出的TO-277B 封装系列肖特基产品&#xff0c;外形扁平&#xff0c;散热片外露设计&#xff0c;能够有效改善散热能力…

JAVA 100道题(25)

25.实现一个死锁示例&#xff0c;并解释如何避免死锁。 在Java中&#xff0c;死锁通常发生在两个或更多的线程无限期地等待一个资源&#xff0c;而该资源又被另一个线程持有&#xff0c;后者也在等待另一个线程释放资源。这形成了一个循环等待条件&#xff0c;导致所有涉及的线…

配置plsql链接Oracle数据库(新手)

配置plsql链接Oracle数据库 安装Oracle客户端 、安装plsql客户端并激活 配置tnsnames.ora文件&#xff08;路径D:\app\peter\Oracle\InstantClient\network\admin根据你的实际路径设置&#xff09; 配置文件如下 # tnsnames.ora Network Configuration File: D:\app\peter\O…

练习 16 Web [极客大挑战 2019]LoveSQL

extractvalue(1,concat(‘~’, (‘your sql’) ) )报错注入&#xff0c;注意爆破字段的时候表名有可能是table_name不是table_schema 有登录输入框 常规尝试一下 常规的万能密码&#xff0c;返回了一个“admin的密码”&#xff1a; Hello admin&#xff01; Your password is…