C++中的友元类

早之前很少用友元类,直到最近跟某个博主写 Asio 网络库,跟着用到友元类,但是今天却遇到一个问题,通过查询得到解决,特记录于此。不过,在此之前,还是把友元类的基础知识做个记录。

基础知识

友元可以是一个函数,该函数被称为友元函数。

类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

1
2
3
4
5
6
7
8
class Box
{
double width;
public:
double length;
friend void printWidth( Box box ); // 友元函数
void setWidth( double wid );
};

友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。

下面声明 Session 类 是 MsgNode 类的友元类,意味着 Session 类可以访问 MsgNode 类 中所有的成员变量和成员函数(public、protected、private)

1
2
3
4
5
6
7
8
9
class MsgNode
{
friend class Session;
public:
MsgNode(short max_len);
~MsgNode();
protected:
char* data_;
};

注意点

友元并不属于这个类本身,无论是友元函数还是友元类。都不能使用类内的this指针,同时也不可以被继承,如同父亲的朋友不一定是儿子的朋友这个道理。

  • 友元关系不能被继承
  • 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明
  • 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

遇到的问题:不可访问友元类的继承的私有成员

我要先把类的继承关系罗列出来,才能讲这里的问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MsgNode
{
friend class LogicNode;
friend class LogicSystem;
friend class RecvNode;
public:
MsgNode(short max_len);
~MsgNode();
void clear(); // 清空数据,避免多次构造节点带来的开销
private:
char* data_;
};

class RecvNode : public MsgNode { // 接收节点
friend class LogicNode;
friend class LogicSystem;
public:
RecvNode(short max_len, short msgID);
private:
short msgID_;
};

RecvNode 类 基础自 MsgNode 类,即有部分成员变量来源于它。我在 LogicSystem 类中要去访问 RecvNode 类 中的成员函数,就在 RecvNode 类中把 LogicSystem 声明为 友元,成功访问到 msgID_ 成员函数,但是却不能访问 data_ 成员。

截止,我们提出这个问题:子类从父类继承的成员变量,是属于子类呢还是属于父类呢?

因此可以看出,父类中的所有变量都被子类给继承了下来,都属于子类的一部分。虽然父类中 private 访问权限的成员不能被子类访问,但是仍然属于子类的一部分。同理,在子类继承父类时,除了继承父类中所有的成员变量,也同时继承了除了父类构造函数外的所有成员函数,这样便可以有效节省代码量,提高代码复用效率。

哦,我们知道了!!!父类中 private 访问权限下的成员不能被子类访问,哪怕它属于子类的一部分。那么子类 RecvNode 都不能访问到父类 MsgNode,凭什么你 友元类 LogicSystem 就可以呢?显然 LogicSystem 类也不可以访问。

该怎么做 ?让子类 RecvNode 能访问到父类 MsgNode即可,所以修改父类 MsgNode 的 private 下的成员变量为 protected 即可。你当然也可以修改为 public,那我设置友元类干什么?