C++面向对象(OOP)编程-友元(友元函数和友元类)

本文主要介绍面向对象编程的友元的使用,以及友元的特性和分类,提供C++代码。

1 为什么引进友元

        面向对象编程(OOP)的三大特性中的封装,是通过类实现对数据的隐藏和封装。一般定义类的成员变量为私有成员,成员函数为公有函数,通过公有函数作为类的接口实现与外部交互。一些情况下,类外的某些函数需要频繁访问类的成员变量,因此引入了友元的概念,将类外的函数定义为该类的友元函数,从而实现对该类私有成员的访问。由此,还引入友元类,就是一个类是另一个类的友元类,友元类可以访问另一个类的所有成员。友元函数和友元类称为友元。

        友元的作用是提高了程序的运行效率(减少了类型检查和安全检查的耗时),但是友元破坏了类的封装和隐藏性,让非类成员函数可以类的私有成员。 

        友元是C++语言中的一种关系,友元关系发生在函数与类之间或者类与类之间。友元关系是单向的,不能传递。

        与类有友元关系的函数称为友元函数,与类有友元关系的类称为友元类。

2 友元的性质

        (1)在被访问类中以friend关键字修饰友元类或者友元函数

        (2)友元不属于该类,且不受该类的访问限制,可以直接访问具体类的所有成员

        (3)友元关系不能被继承

        (4)友元关系是单向的,不具有交换性,只能是一个函数访问一个类的所有成员,或者一个类允许访问另一个类的所有成员,反之不行

        (5)友元关系不具有传递性

3 友元的本质

        友元的本质是提供不属于该类成员,包括全局函数、其他类的成员函数、其他类,访问本类所有成员和成员属性的属性。

4 友元分类

4.1 全局函数为友元函数

        全局函数拥有访问一个类所有成员的能力,需要在被访问类中用关键字friend声明f被访问类的友元函数。一个类可以拥有多个友元函数。一个函数也可以是多个类的友元函数。

代码如下:

#include <iostream>
#include <string>
using namespace std;class A
{private:string name_;int age_;public:A(const string name,const int age) : name_(name), age_(age) {cout << "A构造函数 初始化参数" << endl;};virtual ~A(){cout << "A析构函数 " << endl;}void func(){std::cout << "A's func()" << std::endl;}friend void get_members_global(A & a);  // 友元全局函数,可以访问A的私有成员变量和所有的公有成员};// 全局函数作为友元函数
void get_members_global(A & a)
{cout << a.name_ << " is " << a.age_ << " years old " << endl;a.func();
}int main()
{A a("Hubery",45);cout << "***************************全局函数作为友元函数***************************" << endl;get_members_global(a);return 0;
}

运行结果:

4.2 类的成员函数为友元函数

        类成员函数作为类的友元声明时只需在友元的名称前加上关键字friend,其格式如下:

friend 类型 类名::函数名(形式参数);

一个函数可以是多个类的友元函数,只需要在各个类中分别声明。

代码如下:

#include <iostream>
#include <string>
using namespace std;class A;class C
{public://类的成员函数作为友元函数void get_members_member(A &a);C(){cout << "C构造函数" << endl;}~C(){cout << "C析构函数" << endl;}
};class A
{private:string name_;int age_;public:A(const string name,const int age) : name_(name), age_(age) {cout << "A构造函数 初始化参数" << endl;};virtual ~A(){cout << "A析构函数 " << endl;}void func(){std::cout << "A's func()" << std::endl;}friend void C::get_members_member(A &a); // C的成员函数做友元函数,可以访问所有成员};// 类的成员函数作为友元函数
void C::get_members_member(A &a)
{cout << a.name_ << " is " << a.age_ << " years old " << endl;a.func();
}int main()
{A a("Hubery",45);C c;cout << "***************************类的成员函数作为友元函数***************************" << endl;c.get_members_member(a);return 0;
}

运行结果:

这里用到类的前向声明。前向声明,是一种不完全型(forward declaration)声明,即只需提供类名(无需提供类实现)即可。前向声明功能有限:
        (1)不能定义类的对象。
      (2)可以用于定义指向这个类型的指针或引用。
      (3)用于声明(不是定义)使用该类型作为形参或者返回类型的函数。

4.3 类作为友元

        友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。当希望一个类可以访问另一个类的私有成员、保护成员时,可以将该类声明为另一类的友元类。

定义友元类的语句格式如下:

friend class 类名;
friend和class是关键字,类名必须是程序中的一个已定义的类。

代码如下:

#include <iostream>
#include <string>
using namespace std;class B; // 前向声明class A
{private:string name_;int age_;public:A(const string name,const int age) : name_(name), age_(age) {cout << "A构造函数 初始化参数" << endl;};virtual ~A(){cout << "A析构函数 " << endl;}void func(){std::cout << "A's func()" << std::endl;}friend class B; // 声明B为A的友元类,B中成员函数可以访问A中所有的成员};class B
{private:string name_;int age_;public:B(const string name,const int age) : name_(name), age_(age) {cout << "B构造函数 初始化参数" << endl;};virtual ~B(){cout << "B析构函数 " << endl;}void func(){std::cout << "B's func()" << std::endl;};// 友元类void get_members_class(A & a){cout << a.name_ << " is " << a.age_ << " years old " << endl;a.func();};};int main()
{A a("Hubery",45);B b("Tom",24);cout << "***************************友元类***************************" << endl;b.get_members_class(a);return 0;
}

运行结果:

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

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

相关文章

模拟目录管理 - 华为OD统一考试(C卷)

OD统一考试(C卷) 分值: 200分 题解: Java / Python / C++ 题目描述 实现一个模拟目录管理功能的软件,输入一个命令序列,输出最后一条命令运行结果。 支持命令: 1)创建目录命令: mkdir 目录名称,如mkdir abc为在当前目录创建abc目录,如果已存在同名目录则不执行任何操作…

0x12 队列

0x12 队列 队列是一种“先进先出”的线性数据结构。一般来说&#xff0c;元素从右端进入队列&#xff0c;从左端离开队列。于是我们称队列的左端为队头&#xff0c;右端为队尾。 队列还有许多变体。例如两端都能插入或者取出元素的双端队列&#xff08;C S T L STL STL d e…

CentOS7安装 Docker Compose

docker系列 CentOS7安装 Docker Compose docker系列前言1、下载 Docker Compose2、 授权执行权限3、添加软链接4、验证安装 前言 下面的操作是在centos7中完成的。这里安装的是2.23.3版本的docker-compose。 1、下载 Docker Compose 确保你具有 curl 工具&#xff0c;然后使用…

python基于http的网络通信和网站端口暴露;Python网络编程之HTTP协议的python应用

一、HTTP协议概述 HTTP&#xff08;Hypertext Transfer Protocol&#xff09;即超文本传输协议&#xff0c;是Web应用程序使用的协议&#xff0c;在Web浏览器和Web服务器之间传递HTML页面和数据。HTTP是基于TCP/IP协议来传输数据的&#xff0c;是一种无状态的协议。 关键特点…

每个开发人员都想使用的编程语言

在任何时候&#xff0c;一些编程语言都会把大量的开发人员变成热情的布道者&#xff0c;试图说服世界其他地方的人相信它的伟大。 当热起来的时候&#xff0c;这种语言可能会成为行业标准&#xff0c;但其他时候&#xff0c;这种受欢迎程度就会消失。 在这个故事中&#xff0…

模拟I2C通信

test.c #include "iic.h"extern void printf(const char *fmt, ...); /** 函数名 &#xff1a; delay_us* 函数功能&#xff1a;延时函数* 函数参数&#xff1a;无* 函数返回值&#xff1a;无* */ void delay_us(void) {unsigned int i 2000;while (i--); } void d…

【JVM从入门到实战】(五)类加载器

一、什么是类加载器 类加载器&#xff08;ClassLoader&#xff09;是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。 类加载器只参与加载过程中的字节码获取并加载到内存这一部分。 二、jdk8及之前的版本 类加载器分为三类&#xff1a; 启动类加载器-加载Ja…

golang游戏服务器 - tgf系列课程07

数据管理 使用数据管理工具,对玩家数据进行自动化的缓存管理.需求描述 用户登录成功之后,我们需要根据用户的账号,创建用户的数据,并且将数据存放到mysql和redis中.之后我们通过接口,修改玩家的昵称,然后重新登录.观察数据是否准确准备工作 在开始服务器的代码编程之前,我们…

Linux cp命令教程:如何复制文件和目录(附案例详解和注意事项)

Linux cp命令介绍 cp命令在Linux中用于复制文件或目录。它的全称是copy&#xff0c;意为复制。使用cp命令&#xff0c;你可以将文件或目录从一个位置复制到另一个位置。 Linux cp命令适用的Linux版本 cp命令在所有主流的Linux发行版中都是可用的&#xff0c;包括但不限于Ubu…

绩效面谈为什么失败?

绩效面谈是整个绩效管理过程中的核心环节&#xff0c;是绩效反馈环节的重要手段之一。绩效面谈工作的成功与否直接关系到绩效管理体系能否良性运行&#xff0c;绩效改善的目标能否真正实现。对于许多已经导入绩效管理理念&#xff0c;并初步建立起绩效管理体系的企业来说&#…

pip install默认安装路径

pip install默认安装路径 当使用pip工具安装Python包时&#xff0c;默认情况下&#xff0c;包会被安装到Python的site-packages目录中。这个目录的路径取决于你的操作系统和Python的安装方式。 在Windows操作系统上&#xff0c;pip默认安装路径通常是C:\PythonXX\Lib\site-…

express 下搞一个 websocket 长连接

安装模块 npm i express npm i express-ws 新建文件app.js 先安排源码 监听端口 7777 var express require(express) var app express() require(express-ws)(app)var port 7777 var clientObject {} app.ws(/, (client, req) > {// 连接var key req.socket.re…

预测性维护对制造企业设备管理的作用

制造企业设备管理和维护对于生产效率和成本控制至关重要。然而&#xff0c;传统的维护方法往往无法准确预测设备故障&#xff0c;导致生产中断和高额维修费用。为了应对这一挑战&#xff0c;越来越多的制造企业开始采用预测性维护技术。 预测性维护是通过传感器数据、机器学习和…

计算机网络——习题——书上原题

目录 第一章 1. 填空题 第二章 1. 填空题 第三章 1. 填空题 2.选择题 第四章 1. 填空题 第五章 第六章 1. 填空题 第一章 1. 填空题 &#xff08;1&#xff09;计算机网络的主要功能包括 资源共享、数据通信、分布式处理、提高计算机的可靠性和集中管理 。 &…

上海亚商投顾:沪指再度失守3000点 北向资金净卖出近百亿

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日集体调整&#xff0c;尾盘均跌超1%&#xff0c;北证50则逆势拉升涨超3%。医药股逆势走强&#xf…

Node.js模块化的基本概念和分类及使用方法

1.模块概念 模块&#xff1a;指解决一个复杂问题的时候&#xff0c;自顶向下逐层把系统划分成若干模块的过程。对于整个系统来讲&#xff0c;模块是可以组合、分解和更换的单元。 在编辑领域中的模块&#xff0c;就是遵守固定的规则&#xff0c;把一个大文件拆成独立并且相互…

devops相关面试题

1、发布10 NPM包,熟悉NRM、NVM。 1、谈谈你参与发布的NPM包,它们解决了什么问题,有什么特点? NPM: 是 Node.js 默认的包管理工具 NRM&#xff1a;是 Node.js 源管理工具 NVM&#xff1a;Node.js 版本管理工具 我参与发布的NPM包为mk-form,它通过配置生成和验证复杂表单,简化…

网络编程及相关概念

网络 概念&#xff1a;两台或多台设备通过一定物理设备连接起来构成了网络 根据网络的覆盖范围不同&#xff0c;对网络进行分类&#xff1a; 局域网&#xff1a;覆盖范围最小&#xff0c;仅仅覆盖一片小区域。 城域网&#xff1a;覆盖范围较大&#xff0c;可以覆盖一个城市。…

打印机怎么扫描文件到电脑?6个步骤!轻松完成!

“在工作时我经常需要用到打印机&#xff0c;有时候需要将部分文件扫描到电脑。但是我不是很清楚应该如何操作&#xff0c;有什么方法可以让打印机快速传输文件到电脑的方法吗&#xff1f;” 在人们的工作和学习中&#xff0c;打印机成了很多用户的必备工具。人们可以用它来打印…

C++中的接口有什么用

2023年12月13日&#xff0c;周三上午 今天上午在适配器模式&#xff0c;我发现如果想真正理解适配器模式&#xff0c;就必须学会使用C中的接口&#xff0c;就必须明白为什么要在C中使用接口&#xff0c;所以重新学习了一下C中的接口 目录 C中的接口有什么用用代码说明“实现多…