C++学习笔记(18)——友元

在《设计类》一文中总结了友元函数的相关的知识。该篇笔记第四节总结到为了访问类的私有成员,可以将函数设置为类的友元函数。并同时提到C++中友元有三种,分别是友元函数,友元类,友元成员函数。本文将全面总结友元的相关知识。

一、复习友元函数

类的友元函数是将函数声明为类的友元,注意它并不是类的成员函数,不能用类的作用域解析符(::)去调用它。后面将会讲到的友元成员函数正是体现了这样的区别。

类的友元函数可以访问类的私有成员变量,准确的说它可以访问类的所有成员变量。通常用来重载乘法和打印运算符:

class A
{
	...
public:
	friend std::ostream & operator<<(std::ostream & os, const A & a);//友元函数的通常用法,重载打印输出运算符
private:
	int a;
	int b;
}
std::ostream & operator<<(std::ostream & os, const A & a)
{
    os << a.a << a.b;//用以访问A的私有变量
    return os; 
}
  • 二、友元类

有些情况下,A和B类并没有is-a关系和has-a关系,也即他们不存在继承和包含的关系,但是他们又确实存在某种关系。书上的例子是电视机和遥控器,电视机和遥控器并没有上述继承或包含关系,但是遥控器完成的大部分功能实际都是电视机自己要完成的功能。这种关系很像是某种友情,则可以用友元类来表达。

A类的友元类B类可以访问A类的所有成员,包括公有成员、私有成员和保护成员。注意只有A类即原始类可以指定自己的友元类,所以友元类的声明必须在原始类中用friend关键字修饰。友元类的声明可以放在原始类的public、private、protected下。

class A
{
	...
public:
	friend class B;//B为A的友元类
private:
	int a;
protected:
	int b;	
}

则在B中可以访问A中的任何成员:通常都是用A类的引用对象作为函数参数来调用A的成员。

class B
{
	...
public:
	void func(A &a){a.a = 1; a.b = 2};
}

三、友元成员函数

与友元函数不同,友元成员函数是类的成员函数。友元成员函数旨在指定类某个特定的成员函数为另一个类的友元函数。这也使得该函数能够访问另一个类的所有成员。

如上述友元类中的func函数,我们可以只指定该函数为A的友元成员函数,以使得func函数可以访问A的所有变量,但该函数本身是B的成员函数:

class A
{
	...
public:
	friend void B::func(A &a);
private:
	int a;
protected:
	int b;	
}

如此编译器在编译A类的声明时,要求必须首先知道B的定义(声明友元成员函数时调用了B类),所以可以在A类声明之前声明B类:

class B
{
	...
public:
	void func(A &a){a.a = 1; a.b = 2};
}
class A
{
	...
public:
	friend void B::func(A &a);
private:
	int a;
protected:
	int b;	
}

这里又有新的问题,即,首先编译B类声明时,发现其中的func函数涉及A类声明,这总矛盾的解决办法是将A类声明为前向声明。但是此时func函数的实现也不能在类的声明中内联实现了,因为前向声明只是告诉编译器A是一个类,其内部成员并没有告知编译器,也就不能出现a.a和a.b的调用。所以,需要将B的func实现放在后面实现:

class A;//前向声明
class B
{
	...
public:
	void func(A &a);//只定义原型
}
class A
{
	...
public:
	friend void B::func(A &a); //友元成员函数,得以在B中访问A的内部成员
private:
	int a;
protected:
	int b;	
}
void B::func(A &a)	//也可以用inline关键字定义为内联函数,放在h文件中
{
	a.a = 1; //可以访问A中的私有成员
	a.b = 2; //可以访问A中的保护成员
}

四、双向友元类

可以使得A类和B类互为友元类。可以的A中可以访问B的成员,反之,B中可以访问A的成员。但是函数的定义必须放到类声明之后。由于在funcA之前已经有friend class B;了,所以不需要前向声明。

class A
{
	...
public:
	friend class B;
	void funcA(B &b);
private:
	int a;
protected:
	int b;	
}
class B
{
	...
public:
	friend class A;
	void funcB(A &a);
private:
	int c;
	int d;
}
void A::funcA(B &b)
{
	b.c = 3;
	b.d = 4;
}
void B::funcB(A &a)
{
	a.a = 1;
	a.b = 2;
}

五、共同的友元

还有一种情况是函数需要同时访问两个类的私有成员。而这个函数不可能同时成为两个函数的成员函数。此时可以将该函数设计为两个类的友元函数(注意不是友元成员函数)。

class B;//在A中声明func时有B的类型,所以需要前向声明
class A
{
	...
public:
	friend void func(A &a, B &b); //func函数为A的友元函数
}
class B
{
	...
public:
	friend void func(A &a, B &b); //func函数为B的友元函数
}
inline void func(A &a, B &b)
{
	a.a = 1;//可以访问A的成员
	b.c = 2;//可以访问B的成员
}
发布了76 篇原创文章 · 获赞 63 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/bjtuwayne/article/details/86492350