本期我们讨论 C++ 的可见性。
可见性是一个属于面向对象编程的概念,它指的是类的某些成员或方法有多可见。
我说的可见性是指,谁能看见它们,谁能调用它们,谁能使用它们等这些内容。
可见性是对程序实际运行方式完全没有影响的东西,对程序性能或其他类似的东西也没有影响,它纯粹是语言层面存在的东西,目的是让你能够写出更好的代码或者帮助你组织代码。
C++ 中有三个基础的可见性修饰符,private、protected、public。
在其它语言,比如 Java 和 C# 中有其他的关键字。比如 Java,你可以不使用可见性修饰符,这就是所谓的 default 可见性修饰符;在 C# 中,有个可见性修饰符 internal。
让我们来看看 C++ 中的这三个可见性修饰符是如何工作的。
例子时间
在一个叫做 Entity 的类中,我把 X 和 Y 定义为两个变量,因为这是一个 class,所以默认的可见性实际上是私有的(private),也就是说,这段代码和前面写上 private 是完全一样的,但是如果用的是 struct,那么它将默认是公有的(public)。
在这里没有写可见性,并不意味着它没有可见性,它其实是有的。只是它隐式的给了一个 private 可见性。
那我们就从 private 开始吧。
什么是 private?
private
private 意味着只有(only*)这个 Entity 类可以访问这些变量,它可以读取和写入它们。
上面我的描述中的 only* 带 *,因为在 C++ 中有个叫做 friend 的东西,friend 是 C++ 的关键字,它可以让类或者函数称为类 Entity 的朋友(友元)。friend 的意思就是友元,它表示可以从类中访问私有成员。
继续。
我定义了一个构造函数,把 X 赋值为 0,然后在 main 函数里面实例化这个 Entity。
当然,在这个类的作用域外是不能操作 X=2 或者类似的操作的,因为它是私有的。
我们创建一个 Entity 的子类 Player。
Player 内部有一个构造函数,这里也是不能访问 X 的。
这个特性同样适用于函数。
如果我们创建一个函数 Print。同样的,我可以从 Entity 类中调用 Print 函数,这完全没有问题。如果我试着从 Player 中去调用它,却做不到,还是基于同样的原因。
接下来是 protected
protected
protected 比 private 更可见,比 public 更不可见。
protected 的意思是,类 Entity 和相关层次结构中的所有子类也可以访问这些符号。
你可以看到现在 Player 类中可以写 X=2 和调用 Print 函数了。
因为 Palyer 是 Entity 的子类,然而,我仍然不能在 main 里面这样做,因为它是完全不同的函数,且在类的外面。
最后是 public。
public
这当然意味着所有人都可以访问。
我们可以在 Entity 类中访问它,可以在 Player 中访问它,也可以在 main 函数中访问它。
好了,这就是关于可见性的简短的回答。
现在我们谈谈为什么你要用可见性以及说明情况下要用到可见性。
为什么要用可见性
首先,我认为 public 一切是一个糟糕的想法。
对于开发者和写代码而言,这是风格问题,这是一个如何写好代码的问题。
不管是阅读代码还是扩展代码,可见性是让代码更加容易维护,更加容易理解,这与性能无关,也不会产生完全不同的代码。
可见性不是 CPU 需要理解的东西,它只是人类发明的东西,为了帮助其他人和自己。
如果你把某件事标记为 private,这基本上就是告诉看到代码的每个人,当然包括你自己,——你不应该从其他类或者其他代码中访问此内容,你只能从类的内部访问。
这意味着,如果我从来没有使用过一个类,我想看它包含了什么,我只被允许接触 public 的东西,这就是我使用这个类应该的方式,这是这个类的正确用法,——调用公共函数。
如果我使用一个类,我面对它,看到了一个我想调用的私有函数。我就明白我不应该调用私有函数,这个类的作者可能提供了一些其他的方法来实现同样的事情。
代码是个错综复杂的东西,通过明确可见性,我们可以确保人们不会调用他们不应该调用的代码,并可能破坏某些东西。
一个很好的例子是 UI 界面。
如果我想移动按钮的位置,如果我只访问按钮的坐标 X 和 Y,然后改变变量,按钮实际上可能不会移动,因为 X 轴和 Y 轴的的位置改变方式可能不同,为了让按钮真正移动,我们可能需要刷新显示。
如果我只进行了 X=5 之类的操作,X 变量是改变了,但是显示器实际上并不知道,它不知道要从内存获取新的值,而是继续使用旧的值。
然而,如果我在类中创建一个方法叫做 SetPosition 或者 SetX 之类的,我可以做的不仅仅是给 X 赋值,我还可以调用另一个叫做 Refresh 之类的方法,它能做所有它需要做的事情。
我可以让 X 变量本身为私有,然后设置那个 SetPostion 或 SetX 函数为公共函数。
显然,想要使用这些代码的人,可以看到,——哦,原来我不应该直接给 X 赋值,我应该调用 SetX 或者 SetPosition 方法。
这个例子说明了,为什么你想要使用可见性。
通常写代码的时候,你会发现,你很快忘记你写了什么东西,而且不要认为,你不会团队合作,你不需要去处理可见性,然后把所有的东西搞成 public 的。不要这样做,在几个月、几周甚至几天后,再去看你代码,你可能就已经忘记它是如何工作的了。
但是通过使用可见性这样简单的东西,你可能会看到你想要的访问和利用类的方式。
最后的话
现在人们对于可见性的争论有很多,我建议你坚持自己的想法和习惯。
有些人总是将变量写成私有的,然后搞一个公共的 getter 或者 setter ,这也是我强烈反对的一种操作。
你可以把你的想法发在评论区,本期就到这里,下期再见。