友元函数的一些特点:
1.友元函数需要在类中任意位置进行声明,跟普通函数声明不同的是要加上friend 关键字,然后在类外进行实现,所以友元函数并不是类的成员函数。声明为友元函数之后,友元函数便可访问类中的私有成员。
2.友元函数没有隐藏的this指针,故参数列表中要多一个类的参数,相当于this指针,但是不一定为第一个参数,这使得友元函数的操作更加灵活,能够完成一些成员函数无法完成的操作,使用友元函数将会使类的操作更加灵活。
比如:
class Complex
{
public:
Complex(int real = 0, int imag = 0) :m_real(real), m_imag(imag)
{}
~Complex()
{}
Complex operator+(int a) {
return Complex(m_real + a, m_imag);
}
private:
int m_real;
int m_imag;
};
void main()
{
Complex c, c1(1, 3);
c = c1 + 10;
//c = 10 + c1;
}
这里我们通过运算符重载+号实现了Complex + int 即c = c1 + 10; ,编译器可以运行通过;
但是如果想实现int +Complex 即//c = 10 + c1; 的话,还是需要对+号进行运算符重载,重新解释+号的含义,但是由于类中的成员函数第一个参数默认为自身类型this,按照c = 10 + c1这样赋值运算的话,相当于调动10.operator(c1),10并不是一个对象,无法给Complex operator+(int a)函数中的this指针传参,无法编译通过。
那么如何解决上面的问题呢?这就需要用友元函数来实现了:
class Complex
{
friend Complex operator+(int val, const Complex& c); //将函数声明为友元函数,加friend关键字
friend Complex operator+(const Complex& c1, const Complex& c2);
public:
Complex(int real = 0, int imag = 0) :m_real(real), m_imag(imag)
{}
Complex& operator=(const Complex& c)
{
if (this != &c)
{
m_real = c.m_real;
m_imag = c.m_imag;
}
return *this;
}
~Complex()
{}
Complex operator+(int a) {
return Complex(m_real + a, m_imag);
}
private:
int m_real;
int m_imag;
};
Complex operator+(int val, const Complex& c)
{
return Complex(val + c.m_real, c.m_imag); //友元函数可以访问到类中的私有成员。
}
Complex operator+(const Complex& c1, const Complex& c2)
{
return Complex(c1.m_real + c2.m_real, c1.m_imag + c2.m_imag);
}
void main()
{
Complex c, c1(1, 3), c2(2, 5);
c = c1 + 10;
c = 10 + c1;
c = c1 + c2;
}
3.友元函数和static成员函数都没有this指针,static成员函数调动的优先级会比友元函数高。
4.友元函数不能用const修饰,友元函数的调用与普通函数的调用和原理相同。
5.一个函数可以是多个类的友元函数
class Stu; //前向声明
class Test;
class Test
{
friend void fun(const Test &t, const Stu &s);
public:
Test()
{
}
private:
int m_a = 0; //默认值
int m_b = 0;
};
class Stu
{
friend void fun(const Test &t, const Stu &s);
public:
int m_x = 0;
int m_y = 0;;
};
void fun(const Test &t, const Stu &s) //fun()既是class Test也是class Stu的友元函数
{}
void main()
{
Test t;
}
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
特点:
1.友元关系是单向的,不具有交换性。
比如下面的例子有Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
2.友元关系不能传递
如果B是A的友元,C是B的友元,则不能说明C时A的友元。、
class Date; // 前置声明
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成员变量
public:
Time(int hour, int minute, int second)
: _hour(hour)
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
,_t(9,30,45)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t._second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};
void main()
{
Time t1(11,2,3);
Date d1(2020,3,15);
Date d2 = d1;
d1.SetTimeOfDate(1, 10, 11);
}