类和对象-4

文章目录

  • 前言
  • const成员函数
  • 取地址及const取地址操作符重载
  • 构造函数续
    • explicit
  • static成员
  • 友元
  • 内部类
  • 匿名对象

前言

在前面的文章中,我们了解到了类的四个默认成员函数:构造、析构、拷贝构造和赋值重载。接下来我们会继续学习剩下的两个默认成员函数以及类和对象的一些细节语法。
那么在了解剩下的两个成员函数前,我们先来学习一下const成员函数

const成员函数

首先有如下场景:

class A
{
public:
	void Print()
	{
		cout << "Success" << endl;
	}
private:
	
};
int main()
{
	const A a;
	a.Print();
	return 0;
}

在这里插入图片描述

如上我们只是想调用A的print函数,这个函数不会改变任何值,但实际上我们运行不了这个代码。
这又是我们的隐藏this指针的锅了,每个成员函数的参数里面都有一个隐藏的this指针参数,更关键的是这个this指针没有被const修饰哦!
这又是一个典型的权限放大了,我们的a是被const修饰的,但是this指针却没有被const修饰,因此解决方法就是让this指针也被const修饰,具体方法也很简单:“)”后加const即可。

class A
{
public:
	void Print()const
	{
		cout << "Success" << endl;
	}
private:
	
};
int main()
{
	const A a1;
	a1.Print();
	A a2;
	a2.Print();
	return 0;
}

在这里插入图片描述
由此可知,当成员函数被const修饰时,既可以传入const修饰的类也可以传入没有被const修饰的类。
因此,在成员函数不需要修改类的属性时,最好用const修饰成员函数。

取地址及const取地址操作符重载

那么了解了const成员函数后,我们就可以揭开最后两天默认成员函数的庐山真面目了。
那就是取地址操作符重载const取地址操作符重载
没错,由于const成员函数的存在,这个简单的操作符重载竟然分成了两份。其实仔细想想上一个默认成员函数是赋值重载,这个刚好要改变类的属性,因此不能被const修饰。不然六个默认成员函数可能就变成了七个默认成员函数了。
那么这两个默认成员函数存在的意义又是什么呢?
这当然是为了构成逻辑自洽了:由于我们对自定义类型用的操作符往往都是要重载的,而取地址这个操作符不可以说不重要,简直就是c++的灵魂所在,因此编译器会默认生成取地址的重载。因而,我们绝大多场景用编译器默认生成的取地址重载就已经足够了,除非,你想实现一些特殊的功能。比如悄悄传个空地址回去:

class A
{
public:
	A* operator&()
	{
		return (A*)nullptr;
	}
private:	
};

咳咳,除了这些特殊的场景外。我们是不需要手码这两个默认成员函数的。

构造函数续

在前面我们已经学到了构造函数的一些基础用法,但由于知识的局限性,构造函数的一些其他特点还没有详谈。仔细想想构造函数的定义是不是要我们初始化一个类来着,最终我们的类的属性也的确获得了一些初始值,但那真的是初始化么?
就像这样:

class B
{
public:
	B(int x)
	{
		_x = x;
		_x++;
		_x = 1;
	}
private:
	int _x;
};

这里我们对_x进行了多次赋值,最终我们的确得到了_x的值为1,但这完全谈不上是初始化吧。
如果这个案例还不够深刻请看下面的例子:

class B
{
public:
	B(int x,int& ry)
	{
		_x = x;
		_x++;
		_x = 1;
		_ry = ry;
	}
private:
	int _x;
	int& _ry;
};

如上,我们尝试对_ry这个成员变量进行初始化,但貌似这个写法就是前面提及的引用初始化的经典错误写法啊。
关于引用这种变量是要被初始化的,既然上述方式不可行,那真正可行的方式是怎样的呢?
不多卖关子了,如下所示:

class B
{
public:
	B(int x, int& ry)
		:_x(x)
		, _ry(ry)
	{
	}
private:
	int _x;
	int& _ry;
};

当我们需要初始化时要写这样一个初始化列表,以“:”开始,以“,”分隔,成员变量(初始化值)。下面的函数体想写什么都可以。
这种略显丑陋的函数,就是我们构造函数真正的初始化方式啦!
它有以下特点:

  • 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
  • 类中包含以下成员,必须放在初始化列表位置进行初始化:
    引用成员变量
    const成员变量
    自定义类型成员(且该类没有默认构造函数时)
  • 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
    也就说:
class C
{
public:
	C(int x)
		:_x(x)
		,_y(_x)
	{
	}
	void Print()
	{
		cout << _x << ' ' << _y << endl;
	}
private:
	int _y;
	int _x;
};
int main()
{
	C c(10);
	c.Print();
	return 0;
}

在这里插入图片描述
正确的做法应该是将x赋给_y,_y赋给_x.

	C(int x)
		:_x(_y)
		,_y(x)
	{
	}

在这里插入图片描述

explicit

谈及构造函数就不得不提一个有意思的东西:隐式类型转换
对于构造函数为单参数的类支持隐式类型转换,

class D
{
public:
	D(int x)
	:_x(x)
	{
	}
private:
	int _x;
};
D d=1;

上述代码实则是将1转换成了D然后再拷贝构造给d,但有些编译器会对其优化,使得d能够直接以1为参数构造,因此这里就不多做验证了。
那么对于多参的构造函数能否也转换呢?
C++11以后就规定了多参构造函数也可以隐式类型转换了:

class E
{
public:
	E(int x,int y)
	:_x(x)
	,_y(y)
	{
	}
private:
	int _x;
	int _y;
};
E e={1,1};

当然如果你不想发生这种类型转换,认为代码是严谨的,就可以用explicit来修饰构造函数,禁止类型转换:

class E
{
public:
	explicit E(int x,int y)
	:_x(x)
	,_y(y)
	{
	}
private:
	int _x;
	int _y;
};

static成员

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

使用场景有实现一个类,计算程序中创建出了多少个类对象。

class A
{
 public:
	 A() { ++_scount; }
	 A(const A& t) { ++_scount; }
	 ~A() { --_scount; }
	 static int GetACount() { return _scount; }
 private:
	 static int _scount;
 };
 int A::_scount = 0;
 
 void TestA()
 {
	 cout << A::GetACount() << endl;//静态成员函数可以用作用域限定符调用
	 A a1, a2;
	 A a3(a1);
	 cout << A::GetACount() << endl;
 }

特点:

  1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

友元

在前面日期类的实现,我们已经浅谈过友元了。友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元函数:在类的任何一处用friend 函数声明,就可以使得这个函数在类外也可以调用类的保护、私有对象。

  • 友元函数可访问类的私有和保护成员,但不是类的成员函数
  • 友元函数不能用const修饰(因为const修饰的是this指针,而友元函数不是类的成员函数所以没有this指针)
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  • 一个函数可以是多个类的友元函数
  • 友元函数的调用与普通函数的调用原理相同

友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
跟友元函数定义相差无几,有以下特点:

  • 友元关系是单向的,不具有交换性。
  • 友元关系不能传递
  • 友元关系不能继承

啊! 这种关系让我想起了我的高等代数课,真是有种代数学的美啊。O.o
关于友元呢,大家对他的评价褒贬不一、两极分化。有人认为它破坏了类的封装是罪大恶极的,也有人觉得友元提供了一种不用写Get函数的便利。事实上任何事物都可以是一把双刃剑,就看你怎么使用罢了。就拿C++来说,有人拿他开发游戏、编写系统,也有人会用来开发非法软件、牟取暴利。所以,只要合适地使用友元,不刻意、恶意地破坏封装,我觉得问题是不大的。

内部类

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

内部类就是外部类的友元类,但是外部类不是内部类的友元。
老实说,这多少有点抽象了。是不是像极了含辛苦茹养育孩子长大的父母,父母的东西属于孩子,但是孩子的东习…
emmmm。不得不提的是内部类的使用场景非常罕见,所以没必要过多纠结。

匿名对象

这里来的我目前所掌握的类和对象的最后一个知识点了。
正如c语言可以申请匿名结构体一样,我们的类也可以申请匿名对象。要注意好区别哦,是结构体匿名,但这里不是类匿名而是对象匿名哦!

class F
{
public:
	F(int x=0)
	:_x(x)
	{
	}
	void Print()
	{
		cout<<Hehe<<endl;
	}
private:
	int _x;
};
int main()
{
	F();
	F(1);
	F().Print;
	return 0;
}

如上我们演示了匿名类的构造方式以及使用场景。

  • 注意到,匿名对象可以F()形式构造,但有名对象不可以F f(),因为编译器会识别成函数声明
  • 匿名对象的声明周期只有对应的那一行哦!

那么类和对象的内容就到此结束了,希望对读者有所帮助呢。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java疫苗接种管理系统

本系统lw为2024-3-21本人原创&#xff0c;查chong13% 1.3W字&#xff0c;可以直接上交&#xff0c;这并不是乱七八糟的技术文档和项目文档。 4.2 功能结构设计 可视化的疫苗接种管理系统功能结构设计主要包括以下几个模块&#xff1a; 登录注册模块&#xff1a;这个模块负责…

【SpringSecurity】十六、OAuth2.0授权服务器、资源服务器的配置(理论部分)

文章目录 0、OAuth2服务端结构1、授权服务配置2、授权服务器 ⇒ 配置客户端详情3、授权服务器 ⇒ 管理令牌配置4、授权服务器&#xff1a;配置端点访问的安全约束5、资源服务器配置 相关&#x1f4d5;&#xff1a; 【OAuth2授权服务器配置完整Demo】 0、OAuth2服务端结构 OAu…

Postgresql数据库入门简介

Postgresql入门 1.Postgresql数据库简介 PostgresQL是一个功能强大的开源数据库系统。经过长达15年以上的积极开发和不断改进&#xff0c;PostgreSQL已在可靠性、稳定性、数据一致性等获得了业内极高的声誉。目前PostgreSql可以运行在所有主流操作系统上&#xff0c;包括Linux…

Word 操作

个性化设置 打开Microsoft Office后&#xff0c;可以在账户中设置Office的主题和背景。 如图所示&#xff0c;即可完成。 导航 如图所示&#xff0c;在Microsoft Office打开的docx文档中&#xff0c;鼠标点击左下角红框所示的地方&#xff0c;就会出现导航栏目。 护眼颜色 …

用DevOpsGPT 5分钟开发一个网页小游戏

前言&#xff1a; 今天教大家如何制作一个简易的网页小游戏&#xff0c;步骤很简单&#xff0c;我们只需要用到一个智能开发软件&#xff0c;即可自动帮助我们完成开发。话不多说&#xff0c;接下来&#xff0c;我们直接上教程&#xff01; ​ 官网&#xff1a;KUAFUAI - AI 驱…

UE5.3 StateTree使用实践

近期浏览UE的CitySample&#xff08;黑客帝国Demo&#xff09;&#xff0c;发现有不少逻辑用到了StateTree学习一下&#xff0c;StateTree是多层状态机实现&#xff0c;以组件的形式直接挂载在蓝图中运行。 与平时常见的一些FSM库不同&#xff0c;StateTree并不会返回给外界当…

鸿蒙一次开发,多端部署(七)响应式布局

自适应布局可以保证窗口尺寸在一定范围内变化时&#xff0c;页面的显示是正常的。但是将窗口尺寸变化较大时&#xff08;如窗口宽度从400vp变化为1000vp&#xff09;&#xff0c;仅仅依靠自适应布局可能出现图片异常放大或页面内容稀疏、留白过多等问题&#xff0c;此时就需要借…

Blender小:图片转mesh,并展UV

其实就这么一个插件&#xff1a;Image2Mesh https://github.com/kedepot/i2m-std blender加载完图片之后点这个就行 到了这一步还不行如果直接放到unity中使用的话&#xff0c;添加材质和贴图之后每一个小块都是独立的区域来展示贴图&#xff0c;所以需要展UV

UE5 C++增强输入

一.创建charactor&#xff0c;并且包含增强输入相关的头文件 1.项目名.build.cs。添加模块“EnhancedInput”&#xff0c;方便找到头文件和映射的一些文件。 PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine&q…

kafka学习笔记01(小滴课堂)

介绍分布式流处理平台kafka快速认知 介绍分布式流处理平台kafka核心概念解释 急速部署-Kafka相关环境准备和安装 Linux环境下Zookeeper和Kafka安装启动 解压两个软件的压缩包&#xff1a; tar -zxvf 启动zk: 去log目录进行查看&#xff1a; 查看一下2181端口是否被占用: 安装…

XML Data – Semi-Structured Data XML 数据 - 半结构化数据

Outline • Structured, Semistructured, and Unstructured Data • XML Hierarchical (Tree) Data Model • Extracting XML Documents from Relational Databases • XML Documents, DTD, and XML Schema • XML Languages 结构化、半结构化和非结构化数据 - XML 层次&#x…

linux之shell脚本基础

1.构建基础脚本 1.1 创建shell脚本 1.1.1 第一行需要指定使用的shell # 用作注释行.shell并不会处理脚本中的注释行,但是第一行的注释,会告诉shell使用哪个shell来运行脚本. #!/bin/bash 1.1.2 让shell找到你的脚本 直接运行脚本会提示-bash: a.sh: command not found.因…

Docker部署Alist全平台网盘神器结合内网穿透实现无公网IP访问云盘资源

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-oZuxWTWUiXLx3aQO {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

拌合楼管理系统(九) 地磅称重作弊的方式防范的方法

前言&#xff1a;不谈技术谈谈管理 大宗物资的行业离不开地磅&#xff0c;称重过磅作为运输货物的核心一环&#xff0c;往往很多出现货物损失的情况&#xff0c;都出现在称重作弊&#xff0c;一次作弊就可以导致企业直接的经济损失&#xff0c;让我想起一首古诗硕鼠硕鼠&#x…

LeetCode 21 / 100

目录 矩阵矩阵置零螺旋矩阵旋转图像搜索二维矩阵 II LeetCode 73. 矩阵置零 LeetCode 54. 螺旋矩阵 LeetCode 48. 旋转图像 LeetCode 240. 搜索二维矩阵 II 矩阵 矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为…

K8s-网络原理-中篇

引言 本文是《深入剖析 K8s》的学习笔记&#xff0c;相关图片和案例可从https://github.com/WeiXiao-Hyy/k8s_example中获取&#xff0c;欢迎 ⭐️! 上篇主要介绍了 Flannel 插件为例&#xff0c;讲解了 K8s 里容器网络和 CNI 插件的主要工作原理。还有一种“纯三层”的网络方…

【HM】STM32F407 HAL库 定时器

基本概念 兆赫兹 1MHZ&#xff08;兆赫兹&#xff09;是频率的单位&#xff0c;表示每秒周期性震动1,000,000次。 预分频器 不分频 2分频&#xff0c;两个脉冲输出一次 三分频 自动重装载寄存器 当计时器里的计数器自动重装载寄存器值&#xff0c;计数器清零 定时器分类 …

【ESP32 IDF】pwm脉宽调制技术

文章目录 前言一、PWM脉宽调制技术介绍二、pwm的使用2.1 LEDC定时器结构体结构体介绍配置定时器 2.2 配置LEDC通道结构体介绍初始化pwm 2.3 设置占空比设置占空比更新占空比 三、示例代码总结 前言 PWM&#xff08;Pulse Width Modulation&#xff0c;脉宽调制&#xff09;是一…

基于yolov5的单目测距实现与总结+相机模型+标定

写这篇文章的目的是为了总结我之前看的标定&#xff0c;相机模型以及单目测距的内容&#xff0c;如果有错误&#xff0c;还请不吝赐教。 参考链接&#xff1a; 相机模型、相机标定及基于yolov5的单目测距实现 深度学习目标检测目标追踪单目测距 单目测距代码部署&#xff08;目…

Linux的基本使用

1.Linux的背景 1.1什么Linux Linux是⼀个操作系统.和Windows是"并列"的关系. 1.2Linux系统的优势 1. 开源(意味着免费,便宜) 2. 稳定(Linux可以运⾏很多年,都不会发⽣重⼤问题) 3. 安全(Linux只有管理员或者特定⽤⼾才能访问Linux内核) 4. ⾃由(不会被强加商业产品和…