众所周知,在C++面向对象编程(OOP)中,存在一个名为友元的机制。其本质上是赋予了某个对象(类/函数)一个特权用来访问无法访问的数据。

为什么需要友元机制?友元机制是否有悖于OOP的思想呢?本文将记录我的思考。

什么是OOP?

要回答上述问题,首先要解决的一个问题是:什么是OOP?

编程范式

不妨先下面看看维基百科上对编程范式的介绍:

编程范型编程范式程式设计法(英語:Programming paradigm),是指软件工程中的一类典型的编程风格。常见的编程范型有:函数式编程指令式编程过程式编程面向对象编程等等。

尤如同写作中常常提到的表达方式,一篇文章中可以议论,可以抒情,也可以说明。一段程序中可以面向过程,可以面向对象,也可以面向函数。其本质上是程序员对程序的一种表达方式。

OOP的思想

OOP的全称是Object-oriented Programming,如同其名,它的指导思想为万物既对象。在面向对象程序设计(OOD)中,将现实世界的所有事物抽象为对象,对象具有方法数据。例如,对于一只小🐶,我们可以定义指挥坐下站起为方法,定义它的重量毛色等属性作为数据。

通过这样的过程,我们将复杂的现实世界抽象成了一个又一个对象。

面向对象编程理应满足三大特性,分别是:

  • 封装性
  • 继承性
  • 多态性

而友元主要涉及的就是第一个特性,即隐藏对象的属性和实现细节,仅对外提供公共访问方式。

为什么需要友元机制?

一个例子

电视机与遥控器就是一个非常经典的例子。

众所周知,遥控器和电视机本身都可以调整音量与频道等属性,但是二者本身并不互相归属,也就不存在派生关系。实际情况是:电视机 has a 遥控器

class TV {
private:
  int channel;
  int volume;
};

class Remote {
private:
  void change_channel();
  //Unable to access member channel in TV!
}

那么问题来了,遥控器需要遥控电视机,如何设计程序实现呢?

解决的方案

增加公有接口

class TV {
private:
  int channel;
  int volume;
public:
  void change_channel(int);
  void change_volume(int);
};

这样可以在不暴露私有成员的基础上实现对对象的操作。

但是一个遥控器也许可以遥控多个设备,这样的设计就要求所有的设备(如机顶盒等)实现相应的公有接口,一定程度上造成了冗余。

暴露私有成员

class TV {
public:
  int channel;
  int volume;
};

直接将私有成员暴露出来,简单,但破坏了数据的隐蔽性。

使用友元类

class TV {
private:
  int channel;
  int volume;
public:
  friend class Remote;
};

class Remote {
private:
  void change_channel();
  //Able to access member channel in TV.
}

这样,赋予遥控器类访问电视机私有成员的权限,恰到好处地解决了这一问题。

友元机制是否有悖于OOP的思想?

确实,友元机制破坏了OOP的数据隐藏原则,但是如果说其有悖于OOP的思想,又显得太过于片面。OOP作为一种现代程序设计方法,鼓励代码重用也是其思想之一,若是一味恪守封装性,又何尝不是一种固步自封呢?再言之,对于C++贫瘠的类访问控制而言,友元又何不是一种对于整体封装性的支撑呢?友元存在的目的不是为了破坏封装性,相反,倒不如将其视为一种对接口的拓展。采用这种方法可以降低数据封装对程序整体的冗余。

现实世界是复杂的,程序的抽象终究不能将其完美表达。


Reference

  • C++语言程序设计(第四版)郑莉
  • C++程序设计(第三版)谭浩强
  • C++ Primer Plus(6th edition) Stephen Prata

读的书少,如有疏漏请指正。

Last modification:March 1, 2022
博客维护不易,如果你觉得我的文章有用,请随意赞赏