一篇文章详细解释C++的友元(friend)

 更新时间:2022年03月08日 11:18:42   作者:代码乌龟  
这篇文章主要为大家详细介绍了C++的友元(friend),文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

一.友元函数

友元函数可以是普通函数或者类成员函数。

先看普通函数声明为友元函数:

如下所示:

#include <iostream>
#include <cmath>
using namespace std;
class Point
{
    //普通函数声明为类的友元函数
	friend double TwoPointsDistant(const Point& pnt1, const Point& pnt2);
public:
	Point(double x=0, double y=0)
		:_x(x), _y(y)
	{}
	double getPointXAxis() const { return this->_x; }
	double getPointYAxis() const { return this->_y; }
private:
	double _x;
	double _y;
};
//计算两点的距离
double TwoPointsDistant(const Point& pnt1, const Point& pnt2)
{
	return sqrt(  pow((pnt1._x - pnt2._x), 2) + pow((pnt1._y - pnt2._y), 2)   );
}
int main()
{
	Point point1(1.1,2.2);
	Point point2(3.3, 4.4);
	cout << TwoPointsDistant(point1, point2) << endl;
	system("pause");
	return 0;
}

这里说明一点:TwoPointsDistant()函数必须在Point类的定义下面,至于原因,很简单,你若放在Point上面,Point的数据成员_x和_y都没定义呢,你用个锤子。

再看类成员函数声明为友元函数:

还以上面的类为例,现在加一个_PointMove_类,它有一个成员函数_PointAxisAddOne,_作用是将点的坐标都加1。如下:

class PointMove
{
public:
	void PointAxisAddOne(Point& pnt);
};

这里就出现了一个问题:_Point_和_PointMove_哪个放在前面?先给出答案,应该把_PointMove_放在前面,并且是必须的,如下:

class Point;//前向声明Point
class PointMove
{
public:
	void PointAxisAddOne(Point& pnt);
};
class Point
{
    //普通函数声明为类的友元函数
	friend double TwoPointsDistant(const Point& pnt1, const Point& pnt2);
    //类成员函数声明为友元
	friend void PointMove::PointAxisAddOne(Point& pnt);
    /*这里同前*/
};
//类成员函数的定义
void PointMove::PointAxisAddOne(Point& pnt)
{
	pnt._x = pnt._x + 1;
	pnt._y = pnt._y + 1;
}

这里注意,对于类的成员函数,声明为其他类的友元函数时需要加上类的作用域,即指出该函数属于哪个类。如上面的_PointMove::_。​

同时,需要说明的是,PointAxisAddOne()函数的定义是必须放在Point类定义后面的,这和普通函数的道理是一样的。

最后说明

1.一个函数Func被声明为类A的友元函数,那么是不能直接使用this指针来访问类A的数据成员的(当然,若Func是类B的成员函数,它可以通过this访问类B的数据成员),这和成员函数不同。

2.一个函数Func为什么要声明为某个类A的友元,就是因为函数的参数类型为类A类型,我想访问这个类对象的数据成员,所以被声明为类A的友元函数的参数类型必定为类A,如friend Func(A& obj);

二.友元类

若是将一个类C都声明为另一个类A的友元类,则类C中的成员函数均可访问类A中的私有数据成员。如下:

class Point
{
    //友元类
    friend class PointInfo;
    ...
}
class PointInfo
{
public:
	//打印点所处象限
	void PrintQuadrant(const Point& pnt) const
	{
		if (pnt._x > 0 && pnt._y > 0)
			cout << "点"<<"(" << pnt._x << "," << pnt._y<<")" <<"处于第一象限" << endl;
	}
};

当然,你也可以把_PointInfo_写在_Point_前,只是函数_PrintQuadrant()_的定义就不能在类内实现了,只能在_Point_后实现,原因和前面一样,不再赘述。

三.完整示例:

#include <iostream>
#include <cmath>
using namespace std;
class Point;
class PointMove
{
public:
	void PointAxisAddOne(Point& pnt);
};
class PointInfo
{
public:
	//打印点所处象限
	void PrintQuadrant(const Point& pnt) const;
};
class Point
{
	friend class PointInfo;
	friend double TwoPointsDistant(const Point& pnt1, const Point& pnt2);
	friend void PointMove::PointAxisAddOne(Point& pnt);
public:
	Point(double x=0, double y=0)
		:_x(x), _y(y)
	{}
	double getPointXAxis() const { return this->_x; }
	double getPointYAxis() const { return this->_y; }
	void PrintAxis(const Point& pnt) const
	{
	}
private:
	double _x;
	double _y;
};
//打印点所处象限
void PointInfo::PrintQuadrant(const Point& pnt) const
{
	if (pnt._x > 0 && pnt._y > 0)
	cout << "点"<<"(" << pnt._x << "," << pnt._y<<")" <<"处于第一象限" << endl;
}
void PointMove::PointAxisAddOne(Point& pnt)
{
	pnt._x = pnt._x + 1;
	pnt._y = pnt._y + 1;
}
double TwoPointsDistant(const Point& pnt1, const Point& pnt2)
{
	return sqrt(  pow((pnt1._x - pnt2._x), 2) + pow((pnt1._y - pnt2._y), 2)   );
}
int main()
{
	Point point1(1.1,2.2);
	Point point2(3.3, 4.4);
	cout << TwoPointsDistant(point1, point2) << endl;
	PointInfo pf;
	pf.PrintQuadrant(point1);
	system("pause");
	return 0;
}

VS2015打印结果:

打印结果

四.同一个类(class)的类对象(object)互为友元

还以上面给出的例子为基础,现在在_Point_类加一个成员函数func(const Point& pnt),它返回点的x轴和y轴的和。如下所示。

class Point
{
    /*这里同上*/
	double func(const Point& pnt)
	{
		return pnt._x + pnt._y;
	}
private:
	double _x;
	double _y;
};

现在我生成两个对象,并作如下操作:

	Point point1(1.1,2.2);
	Point point2(3.3, 4.4);
	cout << point1.func(point2) << endl;

最后的结果是打印出7.7。看到这里不知道你有没有疑问:为什么可以通过point1直接访问point2的私有数据成员,而没有将func()声明为友元函数?侯捷老师是这么解释的:相同class的各个objects之间互为友元。

所以对于一个类A,若有一个成员函数Fun(A& arg),可以通过arg直接访问A的私有数据成员。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!

相关文章

  • C语言基础知识分享续篇

    C语言基础知识分享续篇

    这篇文章主要介绍了C语言基础知识分享续篇的相关资料,需要的朋友可以参考下
    2023-01-01
  • C语言设计图书登记系统与停车场管理系统的实例分享

    C语言设计图书登记系统与停车场管理系统的实例分享

    这篇文章主要介绍了C语言设计图书登记系统与停车场管理系统的实例分享,重在以最简单的一些需求来展示管理系统的设计思路,需要的朋友可以参考下
    2016-06-06
  • C语言数组a和&a的区别讲解

    C语言数组a和&a的区别讲解

    今天小编就为大家分享一篇关于C语言数组a和&a的区别讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-02-02
  • C 语言基础之C语言的常见关键字

    C 语言基础之C语言的常见关键字

    C语言中有一些预先定义的字符串,他们本身被赋予了自身的功能。并且我们在定义变量的时候,不能去抢他们的名字来用。他们就是今天的主角:关键字,下面文章将给大家做详细介绍
    2021-09-09
  • C语言线程池的常见实现方式详解

    C语言线程池的常见实现方式详解

    本文介绍了如何使用 C 语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧
    2025-01-01
  • 简单谈谈关于C++中大随机数的问题

    简单谈谈关于C++中大随机数的问题

    这篇文章主要介绍了关于C++中大随机数的问题,文中给出了详细的示例代码,相信对大家的学习或者工作具有一定的参考借鉴价值,有需要的朋友可以一起来学习学习。
    2017-01-01
  • C语言数据结构进阶之栈和队列的实现

    C语言数据结构进阶之栈和队列的实现

    栈和队列,严格意义上来说,也属于线性表,因为它们也都用于存储逻辑关系为 "一对一" 的数据,但由于它们比较特殊,因此将其单独作为一章,做重点讲解
    2021-11-11
  • C++ qsort函数排序与冒泡模拟实现流程详解

    C++ qsort函数排序与冒泡模拟实现流程详解

    qsort是一个库函数,基于快速排序算法实现的一个排序的函数,下面这篇文章主要给大家介绍了关于C语言qsort()函数使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-10-10
  • C++对Json数据的友好处理实现过程

    C++对Json数据的友好处理实现过程

    在Ajax的应用中,前台基本上会用到JSON作为数据交换格式,所以下面这篇文章主要给大家介绍了关于C++对Json数据的友好处理,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-02-02
  • C++ 实现LRU 与 LFU 的缓存算法

    C++ 实现LRU 与 LFU 的缓存算法

    设计和实现一个LRU 缓存机制。其支持获取数据 get 和 写入数据 put,设计并实现最少访问频率(LFU)缓存的数据结构。LFU的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间进行排序。其支持get 和 put,具体了解请看下文
    2021-09-09

最新评论