C++——友元 – 作者:Johnson666

写在前面

类具有封装性,类中的私有数据只有通过该类的成员函数才可以访问。如果在程序中需要访问类的私有成员,就必须通过对象来调用类的成员函数,但是频繁调用成员函数将影响程序运行效率。为解决上述问题,C++提供一种友元机制,友元可以不通过调用成员函数就可以直接访问类的私有数据,以提高程序运行效率。
友元机制在数据封装这堵不透明的墙上开了一个小孔,方便了类外对象进行数据的访问,但是它破坏了封装性,因此友元的使用也要慎重。友元可以是一个普通函数,可以是一个类的成员函数,也可以是一个类。

友元函数

普通函数声明为友元函数,需要注意以下几点:
(1)声明友元函数要在类体里边说明,在函数的类型符前加关键字friend。
(2)在类体外定义,定义格式与普通函数相同。
(3)友元函数是非成员函数,在调用上与普通函数相同。
(4)友元函数可以直接访问该类中的私有成员。

例子:
通过友元函数实现两点间距离的计算:

#include<cmath>
#include<iostream>

using namespace std;

class Point
{
public:
	Point(double i, double j)
	{
		x = i;
		y = j;
	}
	void getxy()
	{
		cout << "("<<x<<","<<y<<")" << endl;
	}
	friend double Distance(Point a, Point b);

private:
	double x, y;
};

double Distance(Point a, Point b)
{
	double dx = a.x - b.x;
	double dy = a.y - b.y;
	return sqrt(dx * dx + dy * dy);
}

int main()
{
	double d11 = 3.0, d12 = 4.0;
	double d21 = 6.0, d22 = 8.0;
	Point P1(d11, d12), P2(d21, d22);
	P1.getxy();
	P2.getxy();
	double d = Distance(P1, P2);
	cout << "Distance is " << d << endl;
	return 0;
}

结果:
image.png
上例中,Distance是一个普通函数,函数的参数为Point对象a和b,若要访问两个对象的私有成员变量x和y,只能通过调用成员函数才能实现。将Distance声明为友元函数则可以打破这一规则,通过它可以利用普通函数方便地访问类的私有成员变量。

友元函数的使用样例:

#include<iostream>
using namespace std;
class Car;
class Boat
{
public:
	class Boat(double x) :weight(x) { };
	friend double getTotalWeight(Boat &boat, Car &car);
private:
	double weight;
};
class Car
{
public:
	class Car(double x) :weight(x) { };
	friend double getTotalWeight(Boat &boat, Car &car);
private:
	double weight;
};
double getTotalWeight(Boat &boat, Car &car)
{
	return (boat.weight + car.weight);
}
int main()
{
	Boat b1(10);
	Car c1(20);
	cout << getTotalWeight(b1, c1) << endl;
	return 0;
}

结果:
image.png

友元函数重载“+”运算符的使用样例:

#include <iostream.h>
class Box
{
public:
	 Box(){}
     Box(int l,int b,int h)
	 {
         length=l;
         breadth=b;
         height=h;
	 }
     friend Box operator+(Box& b1,Box& b2);
	 void print()
	 {
               cout<<"length:"<<length<<endl;
		 cout<<"breadth:"<<breadth<<endl;
		 cout<<"height:"<<height<<endl;
	 }
private:
      double length;
      double breadth;
      double height; 
};
Box operator+(Box& b1,Box& b2)
{
    Box box;
    box.length = b1.length + b2.length;
    box.breadth = b1.breadth + b2.breadth;
    box.height = b1.height + b2.height;
    return box;
}
int main( )
{
   Box Box1(1,2,3);
   Box Box2(4,5,6);
   Box Box3;
   Box3 = Box1+Box2;
   Box3.print();
   return 0;
}

结果:
image.png

友元成员函数

友元函数不仅可以是普通函数(非成员函数),而且可以是一个类中的成员函数。假设一个类(如B)的成员函数为另一个类(如A)的友元函数,这样类B的成员函数就可以访问类A的所有成员变量。当用到友元成员函数时,需注意友元声明和友元定义之间的相互依赖。

用友元成员函数计算某点到原点的距离:

#include<cmath>
#include<iostream>

using namespace std;

class Point;

class Line
{
public:
	double Distance(Point p);
};

class Point
{
public:
	friend double Line::Distance(Point p);  //声明Distance函数为友元函数
	Point(double i, double j)
	{
		x = i;
		y = j;
	}
	void getxy()
	{
		cout << "("<<x<<","<<y<<")";
	}

private:
	double x, y;
};

double Line::Distance(Point p)
{
	double dx = p.x - 0;
	double dy = p.y - 0;
	return sqrt(dx * dx + dy * dy);
}

int main()
{
	double d11 = 3.0, d12 = 4.0;
	Point P(d11, d12);
	Line L;
	double d = L.Distance(P);
	cout << "The distance from ";
	P.getxy();
	cout << " to (0,0) is " << d << endl;
	return 0;
}

结果:
image.png
在该例子中,Line类必须先定义,否则Point类就不能将一个Line类的成员函数指定为友元函数。然而,只有在定义了Point类之后,才能定义Line类的该成员函数(因为函数的参数是Point类的对象)。也就是说,必须先定义包含成员函数的类,才能将成员函数设为友元。因此如果没有第4行的class Point编译将会出错。

友元类

当类A为类B的友元时,类A即为类B的友元类,类A中的所有成员函数都是类B的友元函数。声明方法与前面声明友元函数类似,只要在类的声明语句前加friend即可。同样,修改上例可得友元类的应用如下。

用友元类计算某点到原点的距离:

#include<cmath>
#include<iostream>

using namespace std;

class Point
{
public:
	friend class Line;
	SetXY(double i, double j)
	{
		x = i;
		y = j;
	}
	double Distance();

private:
	double x, y;
};

class Line
{
public:
	Line(double i, double j)
	{
		p.SetXY(i, j);
	}
	void getxy()
	{
		cout << "("<<p.x<<","<<p.y<<")";
	}
	double display()
	{
		return p.Distance();
	}

private:
	Point p;
};

double Point::Distance()
{
	double dx = x - 0;
	double dy = y - 0;
	return sqrt(dx * dx + dy * dy);
}

int main()
{
	double d11 = 3.0, d12 = 4.0;
	Line L(d11, d12);
	double d = L.display();
	cout << "The distance from ";
	L.getxy();
	cout << "to (0,0) is " << d << endl;
	return 0;
}

结果:
image.png
上例中声明Line为Point的友元类,Line中所有的成员函数都是Point的友元函数,因此可以访问Point类中成员变量和成员函数。

关于友元类的使用需要注意以下几点:
(1)友元关系是单向的,不具有交换性。若类B是类A的友元,类A是否是类B的友元要看在类中是否有相应的声明。
(2)友元关系不具有传递性。若类B是类A的友元,类C是类B的友元,类C是否是类A的友元同样要看类中是否有相应的声明。
(3)友元关系不能被继承。若类B是类A的友元,类B的派生类不会自动成为类A的友元。

来源:freebuf.com 2021-06-19 17:28:10 by: Johnson666

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论