OOP中的-控制修饰符

面向对象编程(Object-Oriented Programming)中,最初开始接触到的就是访问控制修饰符。访问控制修饰是几乎所有的OOP语言都会涉及到的,下面就整理三个我熟悉的语言。

C/C++

更详细的内容可以参考《C++ primer》-类 这一章。

概述

有三种修饰符,分别:privatepublicprotected。其中最后一个protected是在继承出现以后才有效的。对于无论是成员变量还是成员函数,这些修饰符都具有相同的作用,即访问控制。其设计的主要作用是为了防止封装的类内成员被类的使用者无意更改或误操作,同时,对访问权限的限制可以实现代码间的松耦合。在类内算法做调整的过程中保持使用者可调用的代码接口,只更改私有函数等。
在详述各个控制符之前要讲一下structclass的区别。二者都可以用来定义类,唯一的不同就是默认的访问权限不同。struct的第一个修饰符之前定义的成员默认是public的,但是class是默认private的。

private 与 public

这两个要一起来说,因为二相相生。private就是将控制权限制在类内,只能是类内的函数访问。public完全没有限制。这两个是最容易理解的,也没有什么好说的。

protected

比上面两个略复杂一点点。在没有继承的情况下,protectedprivate是一样的,所以protect之所以与private不同在于派生类的访问。其修饰的成员可以被派生类直接访问,但是不被派生类对象访问。这个有些博客写的不太严谨,protected无论什么时候都不能与public一样,也就是类的使用者不能直接访问。
换句话说,派生类可以将其修饰的成员当成自己的亲成员来访问,派生类内部可以用对象实例来访问,但是类外不行。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

class Base{
protected:
int m;
...
}

class A : public Base{
public:

// Note-1
void Add(A & tmp){ tmp.m += 1; }
...
}

int main(){
A tmp;
// Note-2
int x = tmp.m;
return 0;
}

/*
* 其中,Note-1的写法没有问题,但是Note-2的写法就是非法的。
*/

friend

有的时候我们的类成员不能被使用者直接访问,但是我需要让其他的一些非类内成员函数来访问,如重载运算符的时候需要被模板函数访问,所以又有了一个新的控制访问修饰符,这个修饰符只用来修饰一些函数或类的声明,也就是说可以让一些类或函数成为该类的友元,使得其可以访问类内的成员变量和成员函数,不受其他修饰符的限制。

  1. 只能修饰非成员函数的声明,或其他类。
  2. 只能在类内修饰。
  3. 其声明不受其他几个修饰符影响。
  4. 友元关系不能传递, 也不可继承。
  5. 友元声明,只是声明了访问权限,并没有实际声明,如要使用该函数,仍然需要显示用普通声明再声明一次。

用作继承权限修饰符

除了用于成员权限声明,此三个修饰符海可以用来修饰类的继承方式,不同的继承方式使得基类成员在派生类中的权限不同。

基本用法如下:

1
2
3
4
5
class CLASSNAME : [修饰符] BASECLASS {...}

class A : public Base{...}

修饰符默认为private

private

  • 基类中的所有protectedpublic 成员在该派生类中是private成员。
  • 基类中的所有private成员在派生类中不可用。

protected

  • 基类中的所有protectedpublic 成员在该派生类中是protected成员。
  • 基类中的所有private成员在派生类中不可用。

public

  • 基类中的所有public 成员在该派生类中是public成员。
  • 基类中的所有protected 成员在该派生类中是protected成员。
  • 基类中的所有private成员在派生类中不可用。

using 改变权限

如果在子类中使用using关键字则可以更改继承过来的变量的权限。例如:

1
2
3
4
5
6
7
8
9
10
class Base{
private:
int m;
}

class A : public Base{
public:
using Base::m;
// 则此类中m为public变量,同样可以更改为其他类型。
}

Java

在Java中有四种访问修饰符,分别是:privateprotectedpublic默认修饰符。其中只能由public或默认修饰符修饰(这里指访问权限的修饰,下面有final修饰符的用法)。且程序中有多个类的时候,程序文件名要命名为public修饰的那个类的名称。Java解释器运行的时候运行main函数所在的类。表现在命令行即:

1
2
3
4
5
6
7
8
9
10
11
public class A {
...
}

class B {
public static void main(Stringp[] args){
...
}
}

// 文件名为 A.java

编译:

1
javac A.java

运行:

1
java B

public

可以被任意访问。

private

其修饰的成员只能由类内函数访问、修改。与C++无较大区别。

protected

其修饰的成员可由一下几个访问:

  1. 类内其他成员。
  2. 同一个包中的其他类
  3. 该类的子类。

默认修饰符

一般只能被同一个程序、同一个包中的其他类访问。

继承中的区别

Java的继承没有修饰符,派生类可以继承基类的所有非私有成员(这里的继承是指可以操作,私有成员依然存在只是不能操作,不能访问,在C++中也是一样的),并且,继承具有传递性。

例如B继承了A, C继承了B,则C可以直接访问A的非私有成员。

基类成员隐藏

派生类中如果有同名变量(类型可不同),则实现了对积累成员的隐藏。在函数中,基类定义的函数处理的是积累的变量,子类实现的函数处理的是子类的变量。

可以使用super来调用被隐藏的变量。super是该类的直接父亲。

方法的覆盖

同名&同类型&同参数列表,的方法会对基类方法进行覆盖(override)。这样会完全清除基类的对应方法,所以是override.

其他控制符

下面这两个控制符事实上不是本文章的重点,但是顺带提一下就过了。下面与上面的东西不是一回事,所修饰的东西也不是一个类型,所以不要搞混了。

static

  1. 静态的,可修饰变量、方法、符合语句。是属于类的,而不属于对象。
  2. 静态函数只能处理静态变量,不能处理类内的非静态变量。
  3. 静态变量初始化是由static修饰的符合语句来完成。可以直接声明并赋值也可以用下面的方式。
    1
    2
    3
    4
    5
    static int a,b;
    static{
    a = 1;
    b = 2;
    }

final

常量修饰符,可修饰变量、方法、类。
final修饰的方法不能被重写覆盖。final修饰的类不能派生子类。

PHP

有了上面的内容就可以类推到这个上面也是一样的,只是会更简单一些。

  1. 类和方法默认public
  2. 特殊的就是,同一个类的不同实例之间可以无限制直接互相访问。
Talk is not cheap.