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

news2024/11/27 12:28:55

        本篇将会对类和对象的主要知识收尾,先会对构造函数进行补充,分别补充了构造函数体赋值、初始化列表、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 = 1
	int _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.coloradmin.cn/o/1560836.html

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

相关文章

逐浪100人丨对话魔珐科技CEO柴金祥:AI时代,虚拟人的边界在哪?

Photo by 《机械姬》剧照 ©自象限原创 访谈丨程心 生成式AI的爆发&#xff0c;带领全民到达AIGC的新时代。 在这个新时代&#xff0c;虚拟人作为一种新兴的内容载体&#xff0c;不仅在娱乐、教育、零售等领域中扮演着越来越重要的角色&#xff0c;也成为了品牌与消费者…

第十三届蓝桥杯国赛真题 Java C 组【原卷】

文章目录 发现宝藏试题 A: 斐波那契与 7试题 B: 小蓝做实验试题 C: 取模试题 D: 内存空间试题 E \mathrm{E} E : 斐波那契数组试题 F: 最大公约数试题 G: 交通信号试题 I: 打折试题 J: 宝石收集 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#x…

关于Ansible的模块②

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 接《关于Ansible的模块 ①-CSDN博客》&#xff0c;继续学习和梳理Ansible的常用文件类模块 1. copy模块 从当前机器上复制文件到…

PCB表面缺陷检测项目 | 轻量化PCB表面缺陷检测算法实现

项目应用场景 面向 PCB 表面缺陷检测场景&#xff0c;包括漏孔 missing hole、咬伤 mouse bite、开路 open circuit、短路 short、支线 spur、杂铜 spurious copper 六种缺陷类型。 项目效果&#xff1a; 项目细节 > 具体参见项目 README.md (1) 下载模型 (2) 安装依赖&…

Master公式(计算递归复杂度)

Master公式 在计算涉及递归的算法的时候&#xff0c;计算复杂度就会变得有些麻烦。Master公式就是用来进行剖析递归行为和递归行为时间复杂度的估算的 Master公式&#xff1a;T(N) a*T(N/b) O(N^d) 公式解释&#xff1a;n表示问题的规模&#xff0c;a表示递归的次数也就是生…

使用 Yoda 和 ClickHouse 进行实时欺诈检测

背景 Instacart 是北美领先的在线杂货公司,拥有数百万活跃的客户和购物者。在其平台上打击欺诈和滥用行为不仅对于维护一个值得信赖和安全的环境至关重要,也对保持Instacart的财务健康至关重要。在这篇文章中,将介绍了一个欺诈平台——Yoda,解释了为什么我们选择ClickHous…

每日一题(相交链表 )

欢迎大家来我们主页进行指导 LaNzikinh-CSDN博客 160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节…

服务器主机推荐

服务器推荐&#xff1a;雨云 - 新一代云服务提供商 雨云的服务器享受免费使用CDN服务&#xff0c;当前CDN调整中&#xff1b; 使用CDN时域名无需备案&#xff1b; 注册后绑定微信可以获得一张首月5折优惠券&#xff1b; 雨云支持1元任意配置1天试用&#xff0c;试用服务器一…

2024年美团笔试题(1)

一.题目描述 小美拿到了一个排列&#xff0c;其中初始所有元素都是红色&#xff0c;但有些元素被染成了白色。 小美每次操作可以选择交换任意两个红色元素的位置。她希望操作尽可能少的次数使得数组变成非降序&#xff0c;你能帮帮她吗? 排列是指:一个长度为n的数组&#…

将在使用的git分支的历史其中的一个版本切换成新的git分支

要将正在使用的 Git 分支的历史中的一个版本切换成新的 Git 分支&#xff0c;可以按照以下步骤进行操作&#xff1a; 两种方式 1.&#xff08;命令行&#xff09;可以使用 git log 命令查看提交历史并找到对应的提交哈希值。 2.&#xff08;图形化&#xff09; 2.1通过idea…

Netty教程之NIO基础

NIO 介绍 NIO 全称java non-blocking IO&#xff08;非阻塞 I/O&#xff09;&#xff0c;后续提供了一系列改进的输入/输出的新特性&#xff0c;被统称为 NIO(即 New IO)&#xff0c;是同步非阻塞的。 阻塞和非阻塞是进程在访问数据的时候&#xff0c;数据是否准备就绪的一种…

Php_Code_challenge12

题目&#xff1a; 答案&#xff1a; 解析&#xff1a; 字符串拼接。

城市内涝模拟:慧天【HTWATER】软件,完全兼容SWMM模型格式,可以在本平台模型与SWMM模型之间实现转换

在城市排水防涝规划过程中&#xff0c;水文水动力耦合模型已经成为一种不可或缺的分析工具。在模型建立、城市内涝风险评估、排水系统性能诊断以及海绵城市规划等方面&#xff0c;内涝耦合模型提供了相应的模拟及分析工具&#xff1a; 一、丰富的数据处理功能&#xff0c;兼容…

算法刷题笔记(3.25-3.29)

算法刷题笔记 3.25-3.29 1. 相同的树2. 二叉树的最近公共祖先3. 二叉搜索树中第K小的元素通过双端队列duque 中序遍历 4. 二叉树的锯齿形层序遍历new LinkedList<Integer>(levelList)双端队列复制 数组需要左右顺序&#xff0c;考虑双端队列 5. 岛屿数量6. 字典序排数&am…

计算机视觉的应用25-关于Deeplab系列语义分割模型的应用场景,以及空洞卷积的介绍

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下计算机视觉的应用25-关于Deeplab系列语义分割模型的应用场景&#xff0c;以及空洞卷积的介绍。Deeplab是Google研发的一系列深度学习模型&#xff0c;主要用于图像语义分割任务&#xff0c;其在众多应用场景中展现出…

基于8086密码锁可修改仿真

**单片机设计介绍&#xff0c;基于8086密码锁可修改仿真 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于8086的密码锁可修改仿真设计是一个结合了微处理器控制、密码管理和仿真技术的综合性项目。通过此设计&#xff0c;用户可以设定和…

nginx界面管理工具之nginxWebUI 搭建与使用

nginx界面管理工具之nginxWebUI 搭建与使用 一、nginxWebUI 1.nginx网页配置工具 官网地址: http://www.nginxwebui.cn 源码地址&#xff1a;https://git.chihiro.org.cn/chihiro/nginxWebUI 2.功能说明 本项目可以使用WebUI配置nginx的各项功能, 包括http协议转发, tcp协议…

非关系型数据库--------------Redis配置与优化

一、关系数据库与非关系型数据库 1.1关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上一般面向于记录。SQL语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语言&#xff0c;用…

正弦实时数据库(SinRTDB)的使用(10)-数据文件的无损压缩

前文已经将正弦实时数据库的使用进行了介绍&#xff0c;需要了解的可以先看下面的博客&#xff1a; 正弦实时数据库(SinRTDB)的安装 正弦实时数据库(SinRTDB)的使用(1)-使用数据发生器写入数据 正弦实时数据库(SinRTDB)的使用(2)-接入OPC DA的数据 正弦实时数据库(SinRTDB)…

Python 用pygame简简单单实现一个打砖块

# -*- coding: utf-8 -*- # # # Copyright (C) 2024 , Inc. All Rights Reserved # # # Time : 2024/3/30 14:34 # Author : 赫凯 # Email : hekaiiii163.com # File : ballgame.py # Software: PyCharm import math import randomimport pygame import sys#…