c++类——友元

本文最后更新于:1 年前

(一)引言:

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

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

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

(二)概念:

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

友元的关键字为 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.普通函数做友元:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#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.类做友元:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#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.类的成员函数做友元:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#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;
}

c++类——友元
https://github.com/xiaohei07/xiaohei07.github.io/2023/03/17/c++类——友元/
作者
07xiaohei
发布于
2023年3月17日
许可协议