c++类——友元

如有兴趣了解更多请关注我的个人博客https://07xiaohei.com/

(一)引言:

类具有封装和信息隐藏的特性,只有类的成员函数能够访问类的私有成员,程序中其他的函数在一般情况下是无法访问私有成员的。

然而,有些时候,类要求自己的成员可对部分外部函数/类可见,如果将其内部数据成员和成员函数均声明为公有的,会破坏类隐藏的特性。另外,对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等需要时间开销,定义过多的成员函数会影响程序的运行效率。

因此,为了解决上述问题,需要在类中声明友元——为了让非成员函数/类即普通函数/类能够访问类的私有成员。

(二)概念:

友元是一种定义在类外部的普通函数或类或类的成员函数,需要在类的体内进行声明。

友元的关键字为 friend,在声明时加在需要设为友元的类/函数头前。

友元本身不是成员函数,但可以访问类中的私有成员,与成员函数的特权相同。

友元能够提高程序的运行效率,但是它破坏了类的封装性和隐蔽性,需要谨慎使用。

(三)特性:

  1. 友元只能出现在类的定义中。
  2. 友元只能通过其所在类的对象访问它所在类的成员。
  3. 友元关系是单向的,不具有交换性,即A是B的友元,但B不一定是A的友元。
  4. 友元关系不具有传递性,即A是B的友元,B是C的友元,但是C不一定是A的友元。(以声明为准)
  5. 友元的声明位置在类中是任意的,不受类访问限定符限制。
  6. 友元函数不能用const修饰,也没有this指针。
  7. 一个函数/类可以是多个类的友元函数。
  8. 友元的成立是由这个类来决定的。

(四)分类:

1.普通函数做友元:

多用于运算符重载(详见c++类——运算符重载)。

形式为:类内 friend 返回类型 函数名(参数表) ;

​ 类外 返回类型 函数名(参数表) { 函数体}

参数表内一定有类的值传递/引用传递/地址传递(用于访问类的私有成员,要不然定义它为友元干嘛呢)。

2.类做友元:

友元类中所有的成员函数均为友元函数。

编译器对格式有严格要求:

  1. 首先对友元类进行前置声明(有些编译器可以省略,会自动补全)。
  2. 然后定义要声明友元类的一般类,此时在其中可以将友元类声明为友元。
  3. 最后定义友元类本身,因为前面对声明友元类的一般类进行了定义,此时可以在其中访问一般类的私有数据成员。

即按声明友元类——定义一般类——定义友元类的顺序进行,一般类要声明友元,友元类可以访问一般类的私有数据成员。

3.类的成员函数做友元:

声明时要在函数名前加上类名::(是成员函数所在类)。

编译器对其格式要求更加严格。

  1. 首先对一般类进行前置声明(在成员函数做友元时此句绝对不可以省略)。
  2. 然后对含友元函数的类进行定义。此时,要被用于作为友元函数的成员函数只进行声明,不进行定义。成员函数的参数表一定有一般类的值/引用/指针用于访问其私有成员。
  3. 定义一般类,声明友元函数(做友元函数的成员函数所在类类名一定要加上)。
  4. 定义做友元函数的成员函数(在其所在类的类外),因为前面的三步,此时才可以访问一般类的私有成员。

(五)代码实现:

1.普通函数做友元:
#include<iostream>
#include<cmath>
using namespace std;
class Point
{
	friend double Distance(Point& a, Point& b);//一般建议友元函数声明放在最前面
private:
	double x, y;
public:
	Point()
	{
		x = 0;
		y = 0;
	}
	Point(double x, double y)
	{
		this->x = x;
		this->y = y;
	}
	void move(double x, double y)
	{
		this->x = x;
		this->y = y;
	}
	double getX()
	{
		return x;
	}
	double getY()
	{
		return y;
	}
	~Point()
	{
	}
};
double Distance(Point& a, Point& b) 
{
	double temp1 = a.x - b.x;//因友元可以访问a和b对象的私有数据成员
	double temp2 = a.y - b.y;
	return sqrt(temp1 * temp1 + temp2 * temp2);
}
double Distance2(Point& a, Point& b) 
{
	double temp1 = a.getX() - b.getX();//不是友元函数,通过类公有的成员函数获得私有数据成员值
	double temp2 = a.getY() - b.getY();
	return sqrt(temp1 * temp1 + temp2 * temp2);
}
int main()
{
	Point* p1 = new Point(1.0, 2.0);
	Point* p2 = new Point(2.0, 3.0);
	Point* p3 = new Point(2.0, 3.0);
	double d1 = Distance(*p1, *p2);
	double d2 = Distance(*p1, *p3);
	cout << d1 << endl << d2 << endl;//均可成功
	delete p1;
	delete p2;
	delete p3;
	return 0;
}
2.类做友元:
#include<iostream>
using namespace std;
class Date; //前置声明,部分编译器可省略。
class Time
{
	friend class Date;//友元类声明
private:
	int hour;
	int minute;
	int second;
public:
	Time(int i1, int i2, int i3) :hour(i1), minute(i2), second(i3) {}
	Time() { hour = minute = second = 0; }
	void display() 
	{
		cout << hour << ":" << minute << ":" << second << endl;
		return;
	}
};
class Date//定义友元类
{
private:
	int year;
	int month;
	int day;
	Time t;
public:
	Date(){ year = month = day = 0; }
	Date(int i1, int i2, int i3) :year(i1), month(i2), day(i3){}
	void display()
	{
		cout << year << ":" << month << ":" << day << ":";
		cout << t.hour << ":" << t.minute << ":" << t.second << endl;//友元类使用Time类私有数据成员
		return;
	}
	void settime(int i1, int i2, int i3)
	{
		t.hour = i1, t.minute = i2, t.second = i3;//友元类修改Time类私有数据成员的值
		return;
	}
};
int main() 
{
	Date d;//可以调用Time类的构造函数
	d.display();//可以使用上面创建的Time对象的值
	d.settime(1, 2, 3); //可修改
	d.display();
	return 0;
}
3.类的成员函数做友元:
#include<iostream>
using namespace std;
class Time; //前置声明一般类
class Date  //定义Date类,此类含有友元函数
{
private:
	int year;
	int month;
	int day;
public:
	Date() { year = month = day = 0; }
	Date(int i1, int i2, int i3) :year(i1), month(i2), day(i3) {}
	void display(Time& t); //声明成员函数,此为Time类的友元函数
	void settime(int i1, int i2, int i3, Time& t); //同上
};
class Time
{
	friend void Date::settime(int i1, int i2, int i3, Time& t);//Date类的成员函数做友元函数的声明
	friend void Date::display(Time &t);//同上
private:
	int hour;
	int minute;
	int second;
public:
	Time(int i1, int i2, int i3) :hour(i1), minute(i2), second(i3) {}
	Time() { hour = minute = second = 0; }
	void display()
	{
		cout << hour << ":" << minute << ":" << second << endl;
		return;
	}
};
void Date::display(Time& t) //定义Date类要做友元函数的成员函数,此时可访问Time类私有成员 
{
	cout << year << ":" << month << ":" << day << ":";
	cout << t.hour << ":" << t.minute << ":" << t.second << endl; //可以访问Time类的私有成员
	return;
}
void Date::settime(int i1, int i2, int i3, Time& t) //同上
{
	t.hour = i1, t.minute = i2, t.second = i3;
	return;
}
int main() 
{
	Date d;
	Time t;
	d.display(t);
	d.settime(1, 2, 3,t);
	d.display(t);
	return 0;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xiaohei07

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值