写在前面
类具有封装性,类中的私有数据只有通过该类的成员函数才可以访问。如果在程序中需要访问类的私有成员,就必须通过对象来调用类的成员函数,但是频繁调用成员函数将影响程序运行效率。为解决上述问题,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;
}
结果:
上例中,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;
}
结果:
友元函数重载“+”运算符的使用样例:
#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;
}
结果:
友元成员函数
友元函数不仅可以是普通函数(非成员函数),而且可以是一个类中的成员函数。假设一个类(如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;
}
结果:
在该例子中,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;
}
结果:
上例中声明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
请登录后发表评论
注册