友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元函数
声明友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
格式如下:
class Myclass
{
public:
……
friend void add(int n );
……
}
友元函数的特点:
友元函数可以使一个普通的函数,也可以是其他类的函数,但它一定不是本类的成员函数。
一般成员函数只可以访问一个类的私有和保护的成员,但友元函数可以访问多个类的私有和保护函数。
友元函数可以绕过成员函数,直接访问类的私有和保护函数,这样就避免了调用成员函数相关的开销。
如果没有友元功能,一个函数要想访问某个类的私有和保护的成员时,只能是将这个成员设置为公共的,这样一来用户就可以访问该类中的所有成员,从而破坏了信息的隐蔽性。
由于友元函数不是本类的成员函数。其定义和调用方式与普通函数一样,在调用友元函数时不需要使用“ · ”运算符,在定义时不需要实用类前缀。
友元函数并不是类的成员函数,他不带有this指针。所以必须用对象名或对象的引用作为友元函数的形参,并在函数体内使用运算符“.”来访问对象的成员。
由于友元函数可使用类里面的所有成员,从而破坏数据的安全性,所以使用友元函数必须谨慎,不要通过友元函数对数据成员进行危险的操作。
例:
//编写一个函数,以友元函数的方式计算一个点到一条直线的距离
#include<iostream>
#include<math.h>
using namespace std;
class Point
{
public:
Point(int x1, int y1){ x = x1; y = y1; }
int x, y;
};
class Line//线类
{
int a, b, c;
public:
Line(int a1, int b1, int c1){ a = a1; b = b1; c = c1; }
friend double dist(Line l, Point p)
{
double d;
d = abs(l.a*p.x + l.b*p.y + l.c) / (sqrt(l.a*l.a + l.b*l.b));
return d;
}
};
void main()
{
Point p(10, 10);
Line l(2, 4, -3);
cout << "d=" << dist(l, p) << endl;
system("pause");
}
友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元关系是单向的,不具有交换性。比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
友元关系不能传递 如果B是A的友元,C是B的友元,则不能说明C时A的友元。
例:编写一个程序,实现栈的压入和弹出。其中有两个类:一个是结点类Node,它包含结点值data和指向下一个节点next,另一个类是栈Stack,它包含头指针top。
#include<iostream>
using namespace std;
class Stack;
class Node
{
int data;
Node *next;
public:
Node(int d)
{
data = d;
next = NULL;
}
friend class Stack;
};
class Stack
{
Node *top;
public:
Stack()
{
top = NULL;
}
void push(int d)
{
Node *p = new Node(d);
if (top != NULL)
p->next = top;
top = p;
}
int pop(int &c)
{
Node *p = top;
if (top != NULL)
{
c = p->data;
top = top->next;
delete p;
return 1;
}
else return 0;
}
};
void main()
{
Stack s;
int c;
s.push(1);
s.push(2);
s.push(3);
s.push(4);
cout << "出栈次序:";
while (s.pop(c))
cout << c << " ";
cout << endl;
system("pause");
}