【C++深度剖析教程6】C++之友元

这几天在复习数学考试,都没有学C++,今天抽空来学一点。

什么是友元?

  • 友元是C++中的一种关系
  • 友元发生在函数与类之间或者类与类之间
  • 友元关系是单向的,不能传递
    这里写图片描述

在具体讲解友元的性质之前,我们先来看看一个程序,这个程序是计算两点之间的距离:

#include <stdio.h>
#include <math.h>class Point
{
private:double x;double y;
public:Point(double x,double y){this->x = x; //this指针指向当前的对象this->y = y;}double getX(){return x;}double getY(){return y;}
};double func(Point& p1,Point& p2)
{double ret = 0;ret = (p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) +(p1.getY() - p2.getY()) * (p1.getY() - p2.getY());ret = sqrt(ret);return ret;
}int main()
{Point p1(1,2);Point p2(10,20);printf("p1(%f,%f)\n",p1.getX(),p1.getY());printf("p2(%f,%f)\n",p2.getX(),p2.getY());printf("|p1-p2| = %f\n",func(p1,p2));return 0;
}

上面代码运行结果为:

p1(1.000000,2.000000)
p2(10.000000,20.000000)
|p1-p2| = 20.124612

看着也没什么大问题。下面我们来简单分析一下这个程序的实用性:进行计算这个两点之间的距离时的代码为:

 ret = (p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) +(p1.getY() - p2.getY()) * (p1.getY() - p2.getY());

进行了8次函数的调用!!!这么简单的计算,就需要调用8次函数,这效率之慢在当初C++诞生的时候,是不被大多数人接受的,大多数人用C编程习惯了,都是可以直接这样调用的:

 ret = (p1.x - p2.x) * (p1.x - p2.x) +(p1.y - p2.y) * (p1.y - p2.y);

然而C++里面你就不能像上面那样直接调用类(C里面可以是结构体或者数组)的成员变量。因为它是private类型的变量。那么在C++诞生的那个年代,人们为了让C++语言完全兼容C语言,就在C++中加入了友元的存在。那么友元的定义以及性质是什么样的呢?

  • 在类中以friend关键字声明友元
  • 类的友元可以是其他类或者具体的函数
  • 友元不是类的一部分
  • 友元不受类中访问级别的限制
  • 友元可以直接访问具体类的所有成员

直接将上面的代码修改一下看看:

#include <stdio.h>
#include <math.h>class Point
{
private:double x;double y;
public:Point(double x,double y){this->x = x; //this指针指向当前的对象this->y = y;}double getX(){return x;}double getY(){return y;}friend double func(Point& p1,Point& p2)};double func(Point& p1,Point& p2)
{double ret = 0;ret = (p1.x - p2.x) * (p1.x - p2.x) +(p1.y - p2.y) * (p1.y - p2.y);ret = sqrt(ret);return ret;
}int main()
{Point p1(1,2);Point p2(10,20);printf("p1(%f,%f)\n",p1.getX(),p1.getY());printf("p2(%f,%f)\n",p2.getX(),p2.getY());printf("|p1-p2| = %f\n",func(p1,p2));return 0;
}

运行结果为:

p1(1.000000,2.000000)
p2(10.000000,20.000000)
|p1-p2| = 20.124612

这样看来,我们已经实现了直接访问类的私有成员变量。上面的代码,func函数是Point类的友元,可以直接访问Point的所有数据。但是这样做,又不好,为什么呢?
友元的尴尬:

  • 友元是为了兼顾C语言的高效性而诞生的
  • 友元直接破坏了面向对象的封装性
  • 友元在实际产品中的高效是得不偿失的
  • 友元在现代软件工程中已经被逐渐遗弃

虽然基于以上的尴尬让友元这个功能在现代软件工程中很少被使用,但是我们学习嘛,肯定是都要学的,哈哈~

友元需要注意的一些事项:

  • 友元关系不具备传递性
  • 类的友元可以是其他类的成员函数
  • 类的友元可以是某个完整的类(所有的成员函数都是友元)
    这里写图片描述
    下面我们再分析一个代码,来看看友元的真实面目:
#include <stdio.h>class ClassC
{const char* n;
public:ClassC(const char* n){this->n = n;}friend class ClassB;
};class ClassB
{const char* n;
public:ClassB(const char* n){this->n = n;}void getClassCName(ClassC& c){printf("c.n = %s\n", c.n);}friend class ClassA;
};class ClassA
{const char* n;
public:ClassA(const char* n){this->n = n;}void getClassBName(ClassB& b){printf("b.n = %s\n", b.n);}/*void getClassCName(ClassC& c){printf("c.n = %s\n", c.n);}*/
};int main()
{ClassA A("A");ClassB B("B");ClassC C("C");A.getClassBName(B);B.getClassCName(C);return 0;
}

运行结果为:

b.n = B
c.n = C

分析代码知:类B是C的友元,类A是B的友元,所以在B中可以直接访问C的私有成员变量,在A中可以直接访问B的成员变量。 那么在A中能否直接访问C呢?试验一下将上面的代码注释掉得部分恢复,编译运行,运行结果为:

test.cpp: In member function ‘void ClassA::getClassCName(ClassC&)’:
test.cpp:6: error: ‘const char* ClassC::n’ is private
test.cpp:44: error: within this context

很显然,A是不能访问C的私有成员的。

总结一下:

  • 友元是为了兼顾C语言的高效而诞生的
  • 友元直接破坏了面向对象的封装性
  • 友元关系不具备传递性
  • 类的友元可以是其他类的成员函数
  • 类的友元可以是某个完整的类
    这里写图片描述

想获得各种学习资源以及交流学习的加我(有我博客中写的代码的原稿):
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题。

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

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

相关文章

SpringAOP xml 方式和注解简单实现日志处理

1.首先是用注解方式捕捉Controller 层异常&#xff1a; 首先是引入aop 依赖的jar <!-- Spring AOP 日志管理需要导入的包 --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.13</…

call stack and stack buffer overflow

http://en.wikipedia.org/wiki/Call_stack http://en.wikipedia.org/wiki/Stack_buffer_overflow Stack_buffer_overflow里提到的frame pointer 的位置不一样&#xff0c;不同的系统实现应该是不一样的。 运行时的栈是从高地址向低地址分配的&#xff0c;堆是从低地址向高地址…

谈一下我对如何设计微服务接口的理解和思考

微服务是一个独立运行、自带数据存储管理&#xff0c;对外提供接口的自治系统。微服务设计很关键的一点是微服务接口的设计。不同微服务经常是分配给不同的团队开发的&#xff0c;接口是各团队编程的契约。 下面只讨论微服务间接口的设计&#xff0c;至于微服务内部子模块间接…

【C++深度剖析教程7】C++之类中的函数重载

函数重载的回顾&#xff08;接上一篇文章&#xff09;&#xff1a; 函数重载的本质为相互独立的不同的函数C中通过函数名和函数参数确定函数调用无法直接通过函数名得到重载函数的入口地址函数重载必然发生在同一个作用域中 类中的成员函数可以进行重载 构造函数的重载普通成…

Linux 服务器远程控制三剑客Telnet、SSH 和 VNC 之 VNC

使用VNC服务实现远程控制Telnet和SSH服务只能实现基于字符界面的远程控制&#xff0c;如果要基于图形界面进行远程控制&#xff0c;可以借助免费的VNC来完成。VNC是VirtualNetworkComput-ing英文的缩写&#xff0c;它是一款优秀远程控制软件&#xff0c;类似Windows的终端服务。…

嵌入式Linux操作系统移植IMX6开发板之实现USB 自动挂载

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 本篇文章讲述如何实现USB自动挂载&#xff0c;U盘即插即用&#xff0c;不用手动挂载的方法&#xff0c;以及给出U盘自动挂载的原理。 目前做的IMX6开发…

C# 繁体,简体 互转

usingMicrosoft.VisualBasic; publicstaticstringTraditional2Simplified(stringstr) { //繁体转简体 return(Microsoft.VisualBasic.Strings.StrConv(str, Microsoft.VisualBasic.VbStrConv.SimplifiedChinese, 0)); } publicstaticstringSimplified…

【C++深度剖析教程8】C++的操作符重载的概念

之前学习了类的函数重载的概念&#xff0c;今天学习操作符重载的概念。在这之前我们先看一个例子&#xff1a; 上面是一个复数的加法&#xff0c;a为复数的实部&#xff0c;b为复数的虚部&#xff0c;在main函数里我想实现复数c1与c2的加法。很显然&#xff0c;正常的号操作符…

大数据开发者应该知道的分布式系统 CAP 理论

无论你是一个系统架构师&#xff0c;还是一个普通开发&#xff0c;当你开发或者设计一个分布式系统的时候&#xff0c;CAP理论是无论如何也绕不过去的。本文就来介绍一下到底什么是CAP理论&#xff0c;如何证明CAP理论&#xff0c;以及CAP的权衡问题。 CAP理论概述 CAP理论&a…

【C++深度剖析教程11】C++学习之编写代码实现复数类

今天&#xff0c;我来学习将复数的加减乘除以及比较运算&#xff0c;编写一个复数类&#xff0c;方便计算复数之间的运算。具体用的方法就是之前写过的操作符重载的概念来实现&#xff08;操作符重载的概念学习&#xff09;。 那么为了显得清晰&#xff0c;今天写的程序运用模块…

IT餐馆—第二十五回 结对

周五开会时&#xff0c;有人提出在团队中采用结对开发的Agile实践。 当然团队里有人说&#xff0c;如果让新手与水平高的人结对&#xff0c;基本上就是知识的单向传递了&#xff0c;对于新手来说的确是个不错的学习机会&#xff0c;但对于水平高的开发者&#xff0c;就未必不乐…

Spring Cloud各组件总结归纳

前面介绍了很多Spring Cloud的组件&#xff0c;本篇按照自己的角度来做一次归纳。 Spring Cloud技术应用从场景上可以分为两大类&#xff1a;润物无声类和独挑大梁类。 润物无声&#xff0c;融合在每个微服务中、依赖其它组件并为其提供服务。 Ribbon&#xff0c;客户端负载均…

移植Linux系统到iMX6开发板之LVDS显示屏驱动程序的框架分析与移植

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天记录一下项目中的关于LVDS显示屏的驱动程序的分析与移植。因为驱动源码厂家已经提供好&#xff0c;我们需要做的就是读懂驱动程序的代码&#xff…

Java api 1.8 中文 帮助文档

java 1.6 帮助文档 中文 链接&#xff1a;http://download.csdn.net/detail/qw599186875/9608735 英文 Java1.8 帮助文档 英文 中文 – 谷歌版 在线版: https://blog.fondme.cn/apidoc/jdk-1.8-google/下载链接&#xff1a;http://download.csdn.net/detail/qw599186875/980219…

设计模式记--Observer Pattern观察者模式

观察者模式——定义了对象之间的一对多依赖&#xff0c;这样一来&#xff0c;当一个对像改变状态时&#xff0c;它的所有依赖者都会收到通知并自动更新. 从定义可以看出,OBSERVER(观察者)模式逻辑上需要两组对象来实现.首先它必需要有发布者(Publish),也可称为被观察的目标 (…

前端学习(64):css继承属性小结

今天来总结一点关于css中哪些属性可以被继承&#xff0c;哪些不可以被继承。不是很全&#xff0c;仅供大家参考&#xff0c;也方便于自己以后复习。 一、不能被继承的属性 1、display&#xff1a;规定元素应该生成的框的类型 2、文本属性&#xff1a; vertical-align、 text…

iMX6开发板移植Linux系统之LVDS显示屏驱动程序分析之LVDS参数的匹配过程分析

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 上一篇分析LVDS驱动程序移植过程的文章&#xff08;文章链接为&#xff1a;移植Linux系统到iMX6开发板之LVDS显示屏驱动程序的框架分析与移植&#xf…

日常spoken英语学习

今天遇到一个老外&#xff0c;说&#xff1a;can you speak engilsh dou you know coffee in here 我是想了半天&#xff0c;第一有点蒙&#xff0c;第二确实忘了&#xff0c;咖啡馆在哪了&#xff0c;回了一句&#xff1a;Iam think 感觉不知道如何组织语言了&#xff0c;口…

【C++深度剖析教程9】初探C++标准库

在这之前&#xff0c;我写的C程序不能叫做标准的C程序&#xff0c;因为里面写的大多数还带有C语言的影子。今天我们来学习C标准库。 首先看一下例子&#xff1a;操作符<<的原生意义是按位左移。那么我们重载这个操作符&#xff0c;将变量或者常量&#xff0c;左移到一个…

Quartus II常见问题集锦

1、 【问题】Pin Planner 的使用问题&#xff1a;在QuartusII 7.2 &#xff0c;时序仿真都通过&#xff0c;但是&#xff0c;一旦使用Pin Planner设定引脚后&#xff0c;时序仿真就发生变化&#xff0c;与功能仿真结果不一致&#xff0c;不是理想的结果。使用Pin Planner时要注…