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

news2024/11/24 6:27:53

本文主要介绍面向对象编程的友元的使用,以及友元的特性和分类,提供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.coloradmin.cn/o/1307749.html

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

相关文章

中伟视界:水位识别、智能化巡检、远程监控,提升智慧河道管理效率

随着现代科技的发展&#xff0c;人工智能&#xff08;AI&#xff09;算法在各行各业中的应用越来越广泛。特别是在水利行业中&#xff0c;智慧河道的运营管理已成为了一个重要的课题。本文将探讨如何利用AI算法优化智慧河道的运营效率&#xff0c;以及如何通过水位识别视频分析…

《Kafka权威指南》读书笔记

《Kafka权威指南》第一、三、四、六章&#xff0c;是重点。可以多看看。 一、 Kafka的组成 kafka是一个发布与订阅消息系统消息&#xff1a;kafka的数据单元称为"消息"。可以把消息看成是数据库中的一个"数据行"。 消息的key&#xff1a;为key生成一个一…

指定安装nginx版本链接

Index of /packages/centos/7/x86_64/RPMS/ (nginx.org) 找到想要下载的对应版本直接下载 rpm -ivh http://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.24.0-1.el7.ngx.x86_64.rpm 查看nginx信息 rpm -qa nginx rpm -qi nginx 命令rpm -ivh是Linux系统中的一种用于…

SELinux介绍

本章主要介绍在RHEL8中如何使用 SELinux。 了解什么是 SELinux了解 SELinux 的上下文配置端口上下文了解SELinux的布尔值了解SELinux的模式 在 Windows系统中安装了一些安全软件后&#xff0c;当执行某个命令时&#xff0c;如果安全软件认为这个命令对系统是一种危害&#…

IPO观察丨“氢风”徐来之际,国鸿氢能会是下一个宁德时代吗?

继亿华通之后&#xff0c;港股又迎来了一家氢能源企业。 近日&#xff0c;氢燃料电池电堆研发商国鸿氢能科技&#xff08;嘉兴&#xff09;股份有限公司&#xff08;以下简称“国鸿氢能”&#xff09;在香港交易所主板挂牌上市。 具体来看&#xff0c;国鸿氢能立足于一个前景…

企业首选的免费开源供应链管理协作系统功能应用介绍

本文节选自Odoo亚太金牌服务机构【开源智造】所编写的《Odoo最佳业务解决方案》如需获取完整的知识内容&#xff0c;请至开源智造官网免费获取。感谢网友一键三连&#xff1a;点赞、转发、收藏&#xff0c;您的支持是我们最大的前进动力&#xff01; 供应链协作 用Odoo供应链协…

紫光展锐CEO任奇伟博士:展锐5G芯筑基当下,迈向未来

12月5日&#xff0c;紫光集团执行副总裁、紫光展锐CEO任奇伟博士受邀出席2023世界5G大会5G产业强基发展论坛&#xff0c;发表了题为《展锐5G芯&#xff1a;筑基当下&#xff0c;迈向未来》的演讲。 ​ 世界5G大会由国务院批准&#xff0c;国家发展改革委、科技部、工信部与地方…

GetX使用笔记+心得(持续更新...)

本文旨在记录在学习和使用GetX过程中遇到的问题和心得体会&#xff0c;如果有表达不正确的地方&#xff0c;欢迎下方留言指正&#x1f604;&#x1f604; 简书地址 #使用&#xff1a; 首先需要引入包&#xff0c;不赘述了&#xff0c; 其次在入口main.dart里面把MaterialApp换…

mybatis动态SQL-choose-when-otherwise

1、建库建表 create database mybatis-example; use mybatis-example; create table emp (empNo varchar(40),empName varchar(100),sal int,deptno varchar(10) ); insert into emp values(e001,张三,8000,d001); insert into emp values(e002,李四,9000,d001); insert into…

线性代数基础【2】矩阵

第二章 矩阵 第一节 矩阵的基本概念与特殊矩阵 一、基本概念 ①矩阵 像如下图示的为矩阵,记为A(aij)m*n ②同型矩阵及矩阵相等 若A、B为如下两个矩阵 如果A和B的行数和列数相等,那么A和B为同型矩阵,且A和B的元素相等(即:aijbij),则称A和B相等 ③伴随矩阵 设A为mn矩阵(如上…

论文润色突显研究亮点 papergpt

大家好&#xff0c;今天来聊聊论文润色突显研究亮点&#xff0c;希望能给大家提供一点参考。 以下是针对论文重复率高的情况&#xff0c;提供一些修改建议和技巧&#xff1a; 标题&#xff1a;论文润色突显研究亮点――提升论文吸引力的关键步骤 一、引言 在学术研究中&#x…

常用的建表但范式、反规范化

规范化&#xff1a; 规范化是用于数据库设计的一系列原理和技术&#xff0c;它可以减少表中数据的冗余&#xff0c;增加数据完整性和一致性。通常有很多范式。 第一范式&#xff08;1NF&#xff09;&#xff1a; 常用的三种范式&#xff1a; 表中的字段都是不可再分割的原子属…

math.gamma()

伽马函数 将阶乘一般化&#xff0c;即将阶乘推广到实数域。 gamma(x) integral(0 to inf) of t^(x-1) * exp(-t) dt 不推了&#xff0c;总之就是表示阶乘

计网Lesson10 - 网络层之IP协议分析

文章目录 网络层协议IPv4 数据报格式IPv4 数据报首部格式版本&#xff08;Version&#xff09;首部长度&#xff08;Header Length&#xff09;区分服务&#xff08;Differentiated Services Field&#xff09;可选字段填充总长度&#xff08;Total Length&#xff09;标识、标…

JavaEE进阶学习: SpringBoot 日志文件

1.日志有什么用 日志的主要作用是记录系统的运行状态、事件和错误信息等。具体来说&#xff0c;日志可以用于以下几个方面&#xff1a; 故障排除&#xff1a;当系统出现故障或错误时&#xff0c;日志可以帮助开发人员定位问题的具体原因和位置&#xff0c;从而更快地修复系统。…

计算机网络简答题

面向连接和非连接的服务特点 面向连接的服务&#xff1a;通信双方在进行通信之前&#xff0c;要事先建立一个完整的可以彼此沟通的通道&#xff0c;在通信过程中整个连接的情况可以被实时的监控和管理 面向非链接的服务&#xff1a;不需要预先建立一个联络两个通信节点的连接&a…

【obs】官方最强插件obs-websocket入门

▒ 目录 ▒ &#x1f6eb; 导读需求开发环境 1️⃣ obs-websocket简介OBS版本说明obs-websocket版本说明安装&#xff08;27.x版本OBS&#xff09;配置插件 2️⃣ OBS-web介绍特征使用方法-5.xhttp vs https 3️⃣ obs-websocket-js开发tester.html 4️⃣ 其它开源项目obs-stud…

做题笔记:SQL Sever 方式做牛客SQL的题目--查询每天刷题通过数最多的前二名用户

----查询每天刷题通过数最多的前二名用户id和刷题数 现有牛客刷题表questions_pass_record&#xff0c;请查询每天刷题通过数最多的前二名用户id和刷题数&#xff0c;输出按照日期升序排序&#xff0c;查询返回结果名称和顺序为&#xff1a; date|user_id|pass_count 表单创建…

双指针的运用——双数之和II和三数之和

两数之和 https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/ 我们考虑这个排序过的数组&#xff0c;首先一个指针在最左&#xff0c;一个在最右。如果这两个数字比目标数字来的要小&#xff0c;那么如果我们左边指针移动了&#xff0c;移动后一定变…

高通平台开发系列讲解(USB篇)linux下如何让U盘可以识别问题

文章目录 一、简述二、修改方法三、宏介绍沉淀、分享、成长,让自己和他人都能有所收获!😄 一、简述 对于一些U盘不能自动被Linux内核识别的情况,可能需要进行一些调整和修改内核驱动的设置。 二、修改方法 在kernel中开启以下的宏开关 CONFIG_USB_STORAGE=y CONFIG_SCSI=…