C++学习笔记-友元函数的定义与使用
一、引言
在C++中,友元函数(Friend Function)是一个独特而强大的特性,它打破了类的封装性,允许一个或多个非成员函数访问类的私有(private)和保护(protected)成员。尽管这种特性在某些情况下可能引发争议,因为它违反了面向对象编程中的封装原则,但在需要高效访问类内部状态或实现某些特定设计时,友元函数却又非常适用。
1.1 封装与友元函数的关系
在面向对象编程(OOP)中,封装是一种将数据(属性)与操作这些数据的函数(方法)绑定在一起的方法。封装的主要目的是隐藏类的内部实现细节,只对外提供有限的接口供外部使用。然而,在某些情况下,我们可能希望允许某些特定的非成员函数访问类的私有成员。这时,友元函数就显得尤为重要了。
1.2 友元函数的概念
友元函数不是类的成员函数,但它可以访问类的所有私有(private)和保护(protected)成员。要使一个函数成为类的友元,需要在类定义中声明该函数为友元。友元函数可以是普通函数,也可以是其他类的成员函数。
二、友元函数的定义与使用
2.1 定义友元函数
在类定义中,使用friend关键字可以声明一个或多个友元函数。友元函数的声明可以放在类的任何位置(但通常放在类定义的开始部分),但它们的定义(实现)必须在类定义之外进行。
示例:
#include <iostream> class MyClass {
private: int value; public: MyClass(int val) : value(val) {} // 声明友元函数 friend void printValue(const MyClass& obj);
}; // 定义友元函数
void printValue(const MyClass& obj) { std::cout << "Value: " << obj.value << std::endl;
} int main() { MyClass myObj(10); printValue(myObj); // 输出: Value: 10 return 0;
}
在上面的示例中,printValue函数被声明为MyClass的友元函数,因此它可以访问MyClass对象的私有成员value。
2.2 友元函数的特性
非成员函数:友元函数不是类的成员函数,因此它不会隐式地接收类的this指针。
访问权限:友元函数可以访问类的所有成员,包括私有和保护成员。
作用域:友元函数的作用域不是类的作用域,而是定义它的作用域。但是,它可以通过类的对象来访问类的成员。
数量不限:一个类可以有任意数量的友元函数。
不继承:友元关系不是继承的。如果类B是类A的友元,那么类B的子类不会自动成为类A的友元。
2.3 友元成员函数
除了普通函数外,类的成员函数也可以被声明为其他类的友元函数。这允许一个类的成员函数访问另一个类的私有成员。
示例:
#include <iostream> class MyClassB; // 前向声明 class MyClassA {
private: int value; public: MyClassA(int val) : value(val) {} // 声明MyClassB的成员函数为友元 friend void MyClassB::printValue(const MyClassA& obj);
}; class MyClassB {
public: void printValue(const MyClassA& obj) { std::cout << "Value from MyClassA: " << obj.value << std::endl; }
}; int main() { MyClassA myObjA(20); MyClassB myObjB; myObjB.printValue(myObjA); // 输出: Value from MyClassA: 20 return 0;
}
注意,在声明友元成员函数时,需要先对目标类进行前向声明,因为编译器在解析MyClassA的类定义时需要知道MyClassB是一个类名。
三、友元函数的使用场景
3.1. 重载操作符
友元函数最常见的用途之一是用于重载操作符,特别是当操作符需要访问类的私有或保护成员时。例如,在自定义的复数类(Complex)中,你可能想要重载<<操作符以便能够直接将复数对象输出到流中。由于输出操作需要访问复数对象的实部和虚部(这些通常是私有成员),因此将<<操作符定义为复数类的友元函数是一个很好的选择。
3.2. 辅助函数
有时,我们可能需要一些辅助函数来帮助实现类的功能,但这些函数并不适合作为类的成员函数。例如,如果有一个表示二维图形的类(如圆形或矩形),我们可能需要一个函数来计算两个图形是否相交。这样的函数可能需要访问图形的私有成员(如中心坐标和半径或边界),但将这些功能作为类的成员函数可能并不合适,因为它们并不是图形对象本身的操作。这时,可以将这些辅助函数声明为类的友元函数。
3.3. 模板类中的友元
在模板类中,友元函数的使用变得更加复杂但也更加有用。模板类的友元可以是另一个模板类或非模板类,甚至可以是模板函数。这允许模板类与非模板代码或不同类型的模板代码之间进行更紧密的交互。例如,你可能有一个模板容器类,并希望提供一个模板化的友元函数来执行某种类型的算法,这个算法需要访问容器的内部数据结构。
3.4. 访问控制绕过
虽然这通常不是推荐的做法,但在某些特殊情况下,友元函数可以用来绕过类的访问控制,以提供对私有成员的访问。这可以在需要优化性能(如避免通过公共接口访问时产生的额外开销)或实现特定设计模式(如访问者模式)时非常有用。然而,过度使用友元可能会破坏封装性,使代码更难理解和维护。
3.5. 跨类访问
友元函数还可以用于实现跨类访问,即一个类的友元函数可以访问另一个类的私有成员。这在设计需要紧密协作的类时特别有用,例如,在某些设计模式(如代理模式或装饰器模式)中,一个类的行为可能依赖于另一个类的内部状态。
相关文章:
C++学习笔记-友元函数的定义与使用
一、引言 在C中,友元函数(Friend Function)是一个独特而强大的特性,它打破了类的封装性,允许一个或多个非成员函数访问类的私有(private)和保护(protected)成员。尽管这…...
熵、交叉熵、KL散度
这里写目录标题 熵KL散度引入交叉熵。交叉熵的二分类公式: 再次理解SoftMax函数结束 熵 熵,是一个物理上的概念,表示一个系统的不确定性程度,或者表示一个系统的混乱程序。 下边是信息熵的演示: 信息熵的公式如下&…...
THS配置keepalive(yjm)
启动完THS管理控制台和THS后,登录控制台,进入实例管理》节点管理,可以分别使用界面配置和编辑配置设置长连接。 1、界面配置 点击界面配置》集群设置,启用长连接,设置长连接数、最大请求数和超时时间。 2、编辑配置 …...
新加坡裸机云多IP服务器特性
新加坡裸机云多IP服务器是一种高性能、稳定性强,且具备多IP地址特性的服务器。它主要适用于需要高度计算性能、网络连接稳定和高安全性的业务场景,如跨境外贸等。下面将详细探讨该类型服务器的特性,rak部落为您整理发布新加坡裸机云多IP服务器…...
深入理解ADB:Android调试桥详解与使用指南
🍎个人博客:个人主页 🏆个人专栏:Android ⛳️ 功不唐捐,玉汝于成 目录 前言 正文 1. 什么是ADB? ADB的基本原理: 2. ADB的安装与配置 安装ADB工具集: 配置ADB环境变量&am…...
PACS-医学影像信息管理系统,全影像科室PACS源码,内置包括MPR、CMPR、VR等三维处理功能
PACS系统可以覆盖医院现有放射、CT、MR、核医学、超声、内镜、病理、心电等绝大部分DICOM和非DICOM检查设备,支持从科室级、全院机、集团医院级乃至到区域PACS的平滑扩展,能够与医院HIS、集成平台的有效集成和融合,帮助医院实现了全院医学影像…...
无人机搭载无人机反制设备可行性分析
一、引言 随着无人机技术的飞速发展,无人机在各个领域的应用越来越广泛。然而,无人机的不当使用也可能带来安全隐患和隐私问题。因此,无人机反制设备应运而生,用于对非法或危险无人机进行干扰和控制。本文将对无人机搭载无人机反…...
MATLAB绘制方波、锯齿波、三角波、正弦波和余弦波、
一、引言 MATLAB是一种具有很强的数值计算和数据可视化软件,提供了许多内置函数来简化数学运算和图形的快速生成。在MATLAB中,你可以使用多种方法来快速绘制正弦波、方波和三角波。以下是一些基本的示例,展示了如何使用MATLAB的命令来实现正弦…...
【通信协议-RTCM】MSM语句(2) - RINEXMSM7语句总结(重要!自动化开发计算卫星状态常用)
注释: 在工作中主要负责的是RTCM-MSM7语句相关开发工作,所以主要介绍的就是MSM7语句相关内容 1. 相位校准参考信号 2. MSM1、MSM2、MSM3、MSM4、MSM5、MSM6和MSM7的消息头内容 DATA FIELDDF NUMBERDATA TYPENO. OF BITSNOTES Message Number - 消息编…...
ios CCUIFont.m
// // CCUIFont.h // CCFC // //#import <Foundation/Foundation.h>// 创建字体对象 #define CREATE_FONT(fontSize) [UIFont systemFontOfSize:(fontSize)]interface UIFont(cc) (void)logAllFonts;end // // CCUIFont.m // CCFC // //#import "CCUIFont.h&…...
调度子系统在特定时间执行
时序逻辑调度器设计模式允许您安排Simulink子系统在指定时间执行。以下模型说明了这种设计模式。 时序逻辑调度器图表包含以下逻辑: 时序逻辑调度器的关键行为 时序逻辑调度器图表包含两个状态,它们以不同的速率调度函数调用子系统A1、A2和A3的执行&…...
【QAC】Dashboard服务端如何配置
【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 解决Dashboard服务端如何配置的问题。 2、 问题场景 客户想使用Dashboard,Dashboard服务端如何配置。 3、软硬件环境 1、软件版本:HelixQAC23.04 2、机器环境:Windows 64bit 3…...
深入理解Linux网络(四):TCP接收阻塞
TCP socket 接收函数 recv 发出 recvfrom 系统调用。 进⼊系统调⽤后,⽤户进程就进⼊到了内核态,通过执⾏⼀系列的内核协议层函数,然后到 socket 对象的接收队列中查看是否有数据,没有的话就把⾃⼰添加到 socket 对应的等待队列⾥…...
【iOS】内存五大分区
目录 堆(Heap)是什么五大分区栈区堆区全局/静态区常量区(即.rodata)代码区(.text) 函数栈堆和栈的区别和联系图解 OC语言是C语言的超集,所以先了解C语言的内存模型的内存管理会有很大帮助。C语言…...
Jupyter Notebook: 是一个强大的交互式计算
文章目录 引言Jupyter Notebook的原理基础使用安装与启动单元格(Cell)操作快捷键 高级使用魔术命令Markdown支持可视化版本控制 优缺点优点缺点 官网链接结论 引言 Jupyter Notebook是一个强大的交互式计算环境,特别适用于数据科学、机器学习…...
【C#学习笔记】变量、变量类型
在C#中,变量是存储数据的容器,每个变量都有其特定的数据类型,这决定了变量可以存储的数据类型和大小。以下是关于C#中变量的由浅入深的详细解释,并附带代码示例和解释: 基础概念 定义: 变量是存储数据的容…...
题解:T480718 eating
eating 题目背景 从前有个荣光的王国,小 A 是里面的国王,今天他要赐予他的子民以仓廪。 题目描述 在一条街上有 n n n 个饭店。小 A 站在这条街的最左端。 第 i i i 个饭店离这条街最左端的距离是 a i a_i ai,它所售卖的菜品的美味…...
MATLAB中matfile用法
目录 语法 说明 示例 创建 MAT 文件对象 启用对 MAT 文件的写访问权限 加载整个变量 将整个变量保存至现有 MAT 文件 加载和保存部分变量 确定变量大小 参数说明 局限性 提示 matfile的功能是访问和更改 MAT 文件中的变量,而不必将文件加载到内存中。 …...
Spring之Spring Bean的生命周期
Spring Bean的生命周期 通过BeanDefinition获取bean的定义信息调用构造函数实例化beanBean的依赖注入处理Aware接口(BeanNameAware、BeanFactoryAware、ApplicationContextAware)Bean的后置处理器BeanPostProcessor-前置初始化方法(Initiali…...
OSINT 开源情报中的地理定位方法
了解 OSINT 中的地理定位技术、如何获取地理位置数据以及如何将地理定位用于各种调查场景。 OSINT 中的地理定位基础知识 OSINT 代表开源情报,指的是从免费公共来源合法收集的有关个人或组织的信息。这包括在互联网上以及书籍、公共图书馆报告、报纸文章、新闻稿、…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
