如有兴趣了解更多请关注我的个人博客https://07xiaohei.com/
(一)引言:
类具有封装和信息隐藏的特性,只有类的成员函数能够访问类的私有成员,程序中其他的函数在一般情况下是无法访问私有成员的。
然而,有些时候,类要求自己的成员可对部分外部函数/类可见,如果将其内部数据成员和成员函数均声明为公有的,会破坏类隐藏的特性。另外,对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等需要时间开销,定义过多的成员函数会影响程序的运行效率。
因此,为了解决上述问题,需要在类中声明友元——为了让非成员函数/类即普通函数/类能够访问类的私有成员。
(二)概念:
友元是一种定义在类外部的普通函数或类或类的成员函数,需要在类的体内进行声明。
友元的关键字为 friend,在声明时加在需要设为友元的类/函数头前。
友元本身不是成员函数,但可以访问类中的私有成员,与成员函数的特权相同。
友元能够提高程序的运行效率,但是它破坏了类的封装性和隐蔽性,需要谨慎使用。
(三)特性:
- 友元只能出现在类的定义中。
- 友元只能通过其所在类的对象访问它所在类的成员。
- 友元关系是单向的,不具有交换性,即A是B的友元,但B不一定是A的友元。
- 友元关系不具有传递性,即A是B的友元,B是C的友元,但是C不一定是A的友元。(以声明为准)
- 友元的声明位置在类中是任意的,不受类访问限定符限制。
- 友元函数不能用const修饰,也没有this指针。
- 一个函数/类可以是多个类的友元函数。
- 友元的成立是由这个类来决定的。
(四)分类:
1.普通函数做友元:
多用于运算符重载(详见c++类——运算符重载)。
形式为:类内 friend 返回类型 函数名(参数表) ;
类外 返回类型 函数名(参数表) { 函数体}
参数表内一定有类的值传递/引用传递/地址传递(用于访问类的私有成员,要不然定义它为友元干嘛呢)。
2.类做友元:
友元类中所有的成员函数均为友元函数。
编译器对格式有严格要求:
- 首先对友元类进行前置声明(有些编译器可以省略,会自动补全)。
- 然后定义要声明友元类的一般类,此时在其中可以将友元类声明为友元。
- 最后定义友元类本身,因为前面对声明友元类的一般类进行了定义,此时可以在其中访问一般类的私有数据成员。
即按声明友元类——定义一般类——定义友元类的顺序进行,一般类要声明友元,友元类可以访问一般类的私有数据成员。
3.类的成员函数做友元:
声明时要在函数名前加上类名::(是成员函数所在类)。
编译器对其格式要求更加严格。
- 首先对一般类进行前置声明(在成员函数做友元时此句绝对不可以省略)。
- 然后对含友元函数的类进行定义。此时,要被用于作为友元函数的成员函数只进行声明,不进行定义。成员函数的参数表一定有一般类的值/引用/指针用于访问其私有成员。
- 定义一般类,声明友元函数(做友元函数的成员函数所在类类名一定要加上)。
- 定义做友元函数的成员函数(在其所在类的类外),因为前面的三步,此时才可以访问一般类的私有成员。
(五)代码实现:
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;
}