C++ 类和对象(友元、内部类、匿名对像)

目录

一、前言

二、正文

1.友元

1.1友元函数的使用

1.1.1外部友元函数可访问类的私有成员,友员函数仅仅是一种声明,他不是类的成员函数。

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

2.友元类的使用

2.1什么是友元类

2.2 友元类的关系是单向的,不具有交换性。

2.3 友元类关系不能传递。

3.内部类

3.1内部类的使用

3.1.1 由于内部类是一个独立的类,跟定义在全局相比,它只是受外部类类域限制和访问限定符限制,所以外部定义的对象不包含内部类。

3.1.2内部类默认是外部类的友元类

4.匿名对象

4.1匿名对象的应用场景


一、前言

昨天我们已经分享了类和对象中类型转换和static成员的知识,有兴趣的小伙伴可以看一下:

https://blog.csdn.net/yiqingaa/article/details/144066165

今天让我们在一起学习类和对象中的友元、内部类,和匿名对象吧。

二、正文

1.友元

  • 友元提供了一种突破类访问限定符封装的方式,友元分为:友元函数和友元类,在函数声明或者类声明的前面加friend,并且把友元声明放到一个类的里面。
  • 外部友元函数可访问类的私有和保护成员,友元函数仅仅是一种声明,他不是类的成员函数。
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
  • 一个函数可以是多个类的友元函数。
  • 友元类中的成员函数都可以是另一个类的友元函数,都可以访问另一个类中的私有和保护成员。
  • 友元类的关系是单向的,不具有交换性,比如A类是B类的友元,但是B类不是A类的友元。
  • 友元类关系不能传递,如果A是B的友元,B是C的友元,但是A不是C的友元。
  • 有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。 

1.1友元函数的使用

 从上图可以看出在我们的Print函数中系统爆出了警告,说A::_a1、A::_a2是不可访问的。那是因为_a1、_a2被限定符private给限制住了,在类外面无法访问类里面的私有成员(这里是_a1和_a2)那么我们如何在类外面定义的函数访问我们的私有成员呢?这里我们就可以借助我们的友元函数来解决这个问题:

#include<iostream>
using namespace std;
class A
{
public:friend void Print(const A& aa);//我们只需要把函数抄写一下放在类里面,然后前面加个friend就行。A(int a1 = 1, int a2 = 2){_a1 = a1;_a2 = a2;}
private:int _a1;int _a2;
};
void Print(const A& aa)
{cout << aa._a1 << " " << aa._a2 << endl;
}
int main()
{A a;Print(a);return 0;
}

友元函数的使用方法很简单,只需要把定义在类外面的函数抄写一份到类里面,然后在函数前面加个friend就行了,friend+函数->友元函数(前提是这个函数定义在类外面,不然这么做其实无实际意义,毕竟如果这个函数是定义在类里面的,直接就可以调用私有成员。没必要多此一举)

  • 友员函数可以这么理解:之前我们是陌生人,我去你家里玩,私闯民宅这是违法的。不过现在我们是朋友了(我是你的友元函数,如上图中Print是类A的友元函数,即Print是A的朋友)那么我去你家玩,就没有任何问题了。
  • 友元可以定义在类里面的任何地方,哪怕是定义在限制符private里面都没问题,不过个人建议放在类里面的第一行,易于让人知道:哦,你使用了友元函数(清晰明了)。

  • 1.1.1外部友元函数可访问类的私有成员,友员函数仅仅是一种声明,他不是类的成员函数。

如图所示,当我们想要通过  类名+::的方式去访问类中的公共成员的时候,只发现了类A的构造函数,而没有发现Print,这说明了友员函数仅仅只是一种声明,而非是这个类的成员函数。

  • 1.1.2一个函数可以是多个类的友元函数
#include<iostream>
using namespace std;
class B;
class A
{
public:friend void Print(const A& aa,const B& bb);//Print是A的盆友A(int a1 = 1, int a2 = 2){_a1 = a1;_a2 = a2;}
private:int _a1;int _a2;
};
class B
{
public:friend void Print(const A& aa, const B& bb);//Print是B的盆友B(int b1 = 3, int b2 = 4){_b1 = b1;_b2 = b2;}
private:int _b1;int _b2;
};
void Print(const A& aa,const B& bb)
{cout << aa._a1 << " " << aa._a2 << endl;cout << bb._b1 << " " << bb._b2 << endl;
}
int main()
{A a;B b;Print(a,b);return 0;
}

由上图可知,函数Print不仅是类A的盆友,同时也是类B的盆友。就像一个人可以是很多人的盆友一样,并不是独属于你的盆友。

2.友元类的使用

  • 2.1什么是友元类

定义:一个类可以将另一个类声明为它的友元类,被声明为友元的类将获得对原始类所有成员的访问权限,无论是私有、保护还是公有成员  。

#include<iostream>
using namespace std;
class B;
class A
{
public:friend class B;//这里我们直接让B整个类都是A的盆友,这样我们不仅在Fun1函数中能使用A中隐私变量,在Func2中也能使用A中隐私成员。A(int a1 = 1, int a2 = 2){_a1 = a1;_a2 = a2;}void Func1(){cout<< _a1 << " " << _a2 << endl;}
private:int _a1;int _a2;
};
class B
{
public:B(int b1 = 3, int b2 = 4){_b1 = b1;_b2 = b2;}void Func2(const A& aa){cout << aa._a1 << " " << aa._a2 << endl;}void Func3(const A& aa){cout << aa._a1 << " " << aa._a2 << endl;}
private:int _b1;int _b2;
};
int main()
{A a;B b;b.Func2(a);b.Func3(a);return 0;
}

就例如上图所示,如果我们想在B类中任意使用A中的私有成员(_a1和_a2)我们可以让B一整个类都成为类A的友元,这样我们不仅让类B中的Func1能访问到A中的私有成员(_a1和_a2),Func2中也能访问到A中的私有成员(_a1和_a2)。

  • 2.2 友元类的关系是单向的,不具有交换性。

比如A类是B类的友元,但是B类不是A类的友元,我们如果想要在A类中访问B中私有成员(_b1和_b2)就访问不到。

  • 2.3 友元类关系不能传递。

如果A是B的友元,B是C的友元,但是A不是C的友元。

可以理解为我是你的盆友,你是他的盆友,但这并不意味着,我和他就是盆友。

3.内部类

  • 如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
  • 内部类默认是外部类的友元类。
  • 内部类本质也是一种封装,当A类跟B类紧密关联,A类实现出来主要就是给B类使用,那么可以考虑把A类设计为B的内部类,如果放到private/protected位置,那么A类就是B类的专属内部类,其他地方都用不了。 
3.1内部类的使用

#include<iostream>
using namespace std;
class A
{
public:A(int a1 = 1, int a2 = 2){_a1 = a1;_a2 = a2;}void Func1()const{cout<< _a1 << " " << _a2 << endl;}class B{public:B(int b1 = 3, int b2 = 4){_b1 = b1;_b2 = b2;}void Func2(const A&aa){cout <<aa. _a1 << " " << aa._a2 << endl;}void Func3(const A& aa){cout << aa._a1 << " " << aa._a2 << endl;}private:int _b1;int _b2;};
private:int _a1;int _a2;
};
class B
{
public:B(int b1 = 3, int b2 = 4){_b1 = b1;_b2 = b2;}
private:int _b1;int _b2;
};
int main()
{A a;return 0;
}

上面就是我们简单的写了个内部类。类B被定义在类A的内部。

  • 3.1.1 由于内部类是一个独立的类,跟定义在全局相比,它只是受外部类类域限制和访问限定符限制,所以外部定义的对象不包含内部类。

看图可知,外部定义的对象a中没有类B,只有成员函数Fun1。

  • 3.1.2内部类默认是外部类的友元类

因为B被定义在A中,所以B是A的盆友,即在B中可以任意访问到A中的私有成员。

例如:B中的Func2和Func1都能访问到A的私有成员(_a1和_a2)。

4.匿名对象

  • 用类型(实参)定义出来的对象叫做匿名对象,相比之前我们定义的 类型 对象名(实参) 定义出来的叫有名对象。
  • 匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象。 

如上图所示,就是有名对象和匿名对象的区别。

值得注意的是:匿名对象的生命周期,只有该匿名对象创建的哪一行,一旦运行到下一行,该匿名对象就被销毁了。

4.1匿名对象的应用场景

匿名对象的使用主要是方便。例如:

我们只是想调用一下成员函数Func1:

  1. 创造有名对象a,再通过实例化对象a访问Func1,需要两行代码。如上图标号1。
  2. 直接创造匿名对象,直接访问Func1,只需要一行代码。如上图标号。

现在知道匿名对象的作用了吗,其实就是为了图方便,能写一行代码完成的事情,为什么要写两行呢,对吧。

三结言

本次博客的分享就到这呢,同学们咱们下次再见,拜拜喽。

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

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

相关文章

Docker Network

1.简介 容器之间的通讯时通过网桥通讯的&#xff0c;跨主机通讯可以使用flannel进行通讯 那么为什么主机可以访问到虚拟机内部呢&#xff1f;因为VMware虚拟出一个虚拟的网卡&#xff0c;而这个虚拟网卡和主机在同一个局域网下 NAT是映射一个虚拟网卡&#xff0c;进行路由通信…

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解

【Linux系统】—— 冯诺依曼体系结构与操作系统初理解 1 冯诺依曼体系结构1.1 基本概念理解1.2 CPU只和内存打交道1.3 为什么冯诺依曼是这种结构1.4 理解数据流动 2 操作系统2.1 什么是操作系统2.2 设计OS的目的2.3 操作系统小知识点2.4 如何理解"管理"2.5 系统调用和…

【行业解决方案篇十八】【DeepSeek航空航天:故障诊断专家系统 】

引言:为什么说这是“航天故障终结者”? 2025年春节刚过,航天宏图突然官宣"DeepSeek已在天权智能体上线",这个搭载在卫星和空间站上的神秘系统,号称能提前48小时预判99.97%的航天器故障。这不禁让人想起年初NASA禁用DeepSeek引发的轩然大波,更让人好奇:这套系…

计算机网络真题练习(高软29)

系列文章目录 计算机网络阶段练习 文章目录 系列文章目录前言一、真题练习总结 前言 计算机网络的阶段练习题&#xff0c;带解析答案。 一、真题练习 总结 就是高软笔记&#xff0c;大佬请略过&#xff01;

从猜想终结到算法革新,弹性哈希开启数据存储新篇章

目录 哈希表的前世今生基本原理从传统到现代:哈希表的演变历程安德鲁 克拉皮文及其团队的创作历程弹性哈希详解基本原理优点技术细节漏斗哈希解析基本原理优点技术细节新算法的实际应用案例电子商务推荐系统金融交易监控系统社交媒体内容过滤物联网设备管理结论与展望哈希表的…

DeepSeek各模型现有版本对比分析

文章目录 一、基础模型系列&#xff1a;V1 到 V3 的演进二、专用模型系列&#xff1a;推理与多模态三、版本选型与商业化趋势 DeepSeek作为最近特别火爆的模型&#xff0c;本文将对DeepSeek现有的主要版本进行对比分析,涵盖参数规模、训练数据、功能改进、应用场景和性能表现等…

RabbitMQ学习—day6—springboot整合

目录 1. springboot配置 2. 开始写RabbitMq代码 3. 队列优化 4. 插件实现延迟队列 5. 总结 前一小节我们介绍了死信队列&#xff0c;刚刚又介绍了 TTL&#xff0c;至此利用 RabbitMQ 实现延时队列的两大要素已经集齐&#xff0c;接下来只需要将它们进行融合&#xff0c;再加…

Automa 浏览器自动化编排 实现自动化浏览器操作

在日常的浏览器使用过程中&#xff0c;我们常常会遇到一些重复繁琐的任务&#xff0c;比如反复填写网页表单、从网页抓取数据、定时截图等&#xff0c;这些工作不仅耗费时间和精力&#xff0c;还容易出错。今天要给大家介绍的Automa&#xff0c;就是一款专门用来解决这类问题的…

【多模态处理篇五】【DeepSeek文档解析:PDF/Word智能处理引擎】

你知道吗?全球每天产生的PDF文档超过10亿份,但90%的上班族还在用复制粘贴的笨办法处理文档!DeepSeek文档解析引擎就像给你的电脑装上了"文档翻译官",能把PDF/Word里的文字、表格、公式甚至排版样式都变成AI能理解的"语言"。举个真实场景:法务小姐姐用…

【C语言】结构体内存对齐问题

1.结构体内存对齐 我们已经基本掌握了结构体的使用了。那我们现在必须得知道结构体在内存中是如何存储的&#xff1f;内存是如何分配的&#xff1f;所以我们得知道如何计算结构体的大小&#xff1f;这就引出了我们今天所要探讨的内容&#xff1a;结构体内存对齐。 1.1 对齐规…

【多模态处理篇三】【DeepSeek语音合成:TTS音色克隆技术揭秘】

最近帮某明星工作室做AI语音助手时遇到魔幻需求——要求用5秒的咳嗽声克隆出完整音色!传统TTS系统直接翻车,生成的语音像得了重感冒的电音怪物。直到祭出DeepSeek的TTS音色克隆黑科技,才让AI语音从"机器朗读"进化到"声临其境"。今天我们就来扒开这个声音…

IDEA使用Maven方式构建SpringBoot项目

1、环境准备 确保你已经安装了以下工具&#xff1a; Java JDK&#xff08;推荐 JDK 8 或更高版本&#xff09; IntelliJ IDEA&#xff08;推荐使用最新版本&#xff09; 2、创建 Spring Boot 项目 &#xff08;1&#xff09; 打开 IntelliJ IDEA。 &#xff08;2&#xff09…

【Redis原理】底层数据结构 五种数据类型

文章目录 动态字符串SDS(simple dynamic string )SDS结构定义SDS动态扩容 IntSetIntSet 结构定义IntSet的升级 DictDict结构定义Dict的扩容Dict的收缩Dict 的rehash ZipListZipListEntryencoding 编码字符串整数 ZipList的连锁更新问题 QuickListQuickList源码 SkipListRedisOb…

Git Repo下如何制作一个patch文件

Git Repo下如何制作一个patch文件 1. 源由2. 步骤2.1 本地代码差异2.2 添加修改代码2.3 添加未跟踪代码2.4 确认打包文件2.5 输出打包文件2.6 自查打包文件2.7 恢复工作环境 3. 总结 1. 源由 patch分享&#xff0c;更好的差异化比较&#xff0c;减少时间浪费。同时&#xff0c…

跟着李沐老师学习深度学习(十四)

注意力机制&#xff08;Attention&#xff09; 引入 心理学角度 动物需要在复杂环境下有效关注值得注意的点心理学框架&#xff1a;人类根据随意线索和不随意线索选择注意力 注意力机制 之前所涉及到的卷积、全连接、池化层都只考虑不随意线索而注意力机制则显示的考虑随意…

STM32的“Unique device ID“能否修改?

STM32F1系列的"Unique device ID"寄存器的地址为0x1FFFF7E8。 这个寄存器是只读的。 "Unique device ID"寄存器位于“System memory”中。“System memory”地址范围为“0x1FFF F000- 0x1FFF F7FF”。 所有STM32 MCU上都存在系统引导加载程序。顾名思义&a…

模型思维 - 领域模型的应用与解析

文章目录 引言模型的核心作用与价值四大模型类型UML建模工具UML类图的核心价值类关系深度剖析企业级建模实践 领域模型&#xff08;推荐&#xff09; vs 数据模型&#xff08;不推荐&#xff09;区别联系错把领域模型当数据模型错误方案 vs 正确方案对比正确方案的实现1. 数据库…

基于GWO灰狼优化的WSN网络最优节点部署算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 无线传感器网络&#xff08;Wireless Sensor Network, WSN&#xff09;由大量分布式传感器节点组成&#xff0c;用于监测物理或环境状况。节点部署是 WSN 的关键问…

产品概念的提出

产品概念的提出 一个产品或者一个产品概念idea是怎么想到的呢&#xff1f;很多情况下它其实来自生活中的一些不爽、不满意、想吐槽&#xff0c;凡是用户抱怨的事情就是用户的强烈刚需需求是我们要去做的事情。当有了一个想法时需要弄清楚一下几个问题&#xff1a; 核心用户事…

3.Docker常用命令

1.Docker启动类命令 1.启动Docker systemctl start docker 2.停止Docker systemctl stop docker 3.重启Docker systemctl restart docker 4.查看Docker状态 systemctl status docker 5.设置开机自启(执行此命令后每次Linux重启后将自启动Docker) systemctl enable do…