C++ 类的访问权限

1. 可访问性问题

先明确一下基本出发点:一个类的访问者有四类:自己、友元、子类、普通用户。

“自己”和“友元”可以随意访问该类,不管它们。

只需要讨论子类普通用户

子类又分两种:儿子、孙子(孙子的儿子同理递推即可)。


我们先说能不能访问的问题,从简单的说起。

先说一级继承:

class Dad {
public:
	string pub_text = "pub_text";
protected:
	string prot_text = "pro_text";
private:
	string pirv_text = "priv_text";
};

上面的很简单,public能被儿子和普通用户,protected能让儿子访问,privtae只能Dad自己访问。

我们把这里的publicprotectedprivate称为基类访问说明符

成员类型(基类访问说明符) 可访问者 不可访问者
public 儿子 ,普通用户
protected 儿子 普通用户
private 儿子 ,普通用户

下来,有三个儿子继承了Dad

class Public_son: public Dad {

};

class Protected_son: protected Dad {

};

class Private_son : private Dad{

};

把这里冒号后面的publicprotectedprivate称为派生访问说明符

这里三种儿子,对Dad的访问没有任何差异,它们都能访问(基类部分)Dadpublic部分和protectde部分,不能访问Dadprivate部分。

所以容易明白,这里的派生访问说明符不是针对儿子的,而是针对其他人的。

下面我们来讨论这些“其他人”。


其他人分两种,第一种是儿子的儿子,也就是Dad的孙子;另一种是其他用户。

  1. public继承,则相当于Dad没有额外限制.

    • Dadpublic成员人人可访问,孙子还可以访问protected成员,private成员只有Dad自己可以访问。
  2. protected继承,则孙子可以访问爷爷的publicprotected成员,但是不能访问private;普通用户什么都不能访问——包括Dadpublic成员。

  3. private继承,爷爷,也就是前面的Dad类,对派生类、用户都不可见。

总结下来,其实就相当于叠加,取两权限的“最小范围”。在子类的两类用户看来就像下面一样:

  1. public继承
    • Dadpublic成员 = 儿子的public成员;
    • Dadprotected成员 = 儿子的protected成员;
    • Dadprivate 成员 = 儿子的private成员;
  2. protected继承
    • Dadpublic成员 = 儿子的protected成员;
    • Dadprotected成员 = 儿子的protected成员;
    • Dadprivate 成员 = 儿子的private成员;
  3. private继承
    • Dadpublic成员 = 儿子的private成员;
    • Dadprotected成员 = 儿子的private成员;
    • Dadprivate 成员 = 儿子的private成员;

看起来就像分类“合并”。

用表格描述,孙子和其他用户,对于基类部分的可访问性:

儿子对父亲的继承类型 可访问者 不可访问者
public 孙子,普通用户
protected 孙子 用户
private 用户,孙子

2. 改变可访问性

改变访问方式有两种,友元、using声明。

先说友元:

  1. 友元是不互通的,
  2. 友元不能继承,每个类负责控制自己的成员的访问权限。

比如,子类的友元不能随意访问基类部分。

下面的代码是错的:

class Dad {
public:
	string pub_text = "pub_text";
protected:
	string prot_text = "pro_text";
private:
	string pirv_text = "priv_text";
};

class Son :  Dad {
	friend void func (Son son);
};

void
func (Son son)
{
	cout << son.priv_text << endl;//不可以!
}

这很合理,毕竟连Son自己都不能随意访问Dad

struct默认访问权限是public,默认public继承;
class默认访问权限是private,默认private继承。


using声明可以将子类能够访问的成员的权限进行更改。

using声明位于public,则被声明的对其他人就变成public的;放在private就变成private的;protected同理。

看下面的例子,Son私有继承Dad,本来用户不能访问prot_text,能访问pub_text,现在将它们反了过来:

class Dad {
public:
	string pub_text = "pub_text";
protected:
	string prot_text = "pro_text";
private:
	string priv_text = "priv_text";
};

class Son :  private Dad {
public:
	using Dad::prot_text;
private:
	using Dad::pub_text;
	
};

int
main (void)
{
	Son son;
	cout << son.prot_text << endl; //可访问
	cout << son.pub_text << endl;//错误,不可访问
}

3. 访问方式问题

子类可以访问父类,但并不是说可以直接访问。

看下面的代码:

class Dad {
public:
	string pub_text = "pub_text";
protected:
	string prot_text = "pro_text";
private:
	string pirv_text = "priv_text";
};

class Son :  Dad {
public:
	void func (Dad dad) {
		cout << dad.prot_text << endl; //错误
	}
};

编译器会报错,说Dadpro_textprotected的,所以你不能访问。

初学者看到这里很容易发懵:protected不是能被子类访问吗?

你需要细细品味一下C++ Primer 5,p543的一句话:

此外,protected还有另外一条重要的性质。
派生类的成员或友元只能通过派生类对象来访问基类的受保护成员。派生类对于一个基类对象中的受保护成员没有任何访问特权。

如果你这样做,那是对的:

class Son :  Dad {
public:
	void
	func (Son &son) {
		cout << son.prot_text << endl;
	}
};

至于为什么要这样设计,我想大概是为了降低各类型之间的耦合度,保持相对独立吧。

你完全可以提供protectedgettersetter来达到目的。

4. 多态

派生类向基类的转换,或者说,把派生类绑定到基类的指针/引用,会受到派生说明符的影响。

(以下转换均视为引用/指针之间的转换)

假设有两个类,DadSon

  1. 只有Son公有继承Dad时,用户才能将Son转换为Dad,换句话说,将Son(的引用/指针)当作Dad(的引用/指针)使用。

  2. 不管哪种继承方式,Son的成员和友元 始终 可以使用SonDad的转换。

  3. 只有Sonprotected / public继承Dad时,Son的派生类(也就是Dad的孙子类)的成员、派生类的友元可以使用 SonDad的转换。

从类设计者意图来看,也很好理解,SonDad私有继承,就意味着Son不希望让用户使用它的基类部分(Dad)。

而将Son转为Dad,静态类型还是Son,用户就能偶藉由Dad来访问Son的基类部分,违背了类设计者的意图,不合理。

然而子类不能向父类转换,违背了里氏代换原则,大概不是一种很好的设计。

或许这是privateprotected继承比较少见的原因吧,平时使用的都是public继承。

private继承的意义仅仅是实现上的继承,不牵扯面向对象设计理念。
相关内容见《Effective C++第三版 》条款39:明智而审慎地使用private继承

参考书籍《C++ primer 5》p542 ~ p546

你可能感兴趣的:(笔记,c++,c++,继承,类,面向对象编程)