/*从15.6 Access control and inheritance开始
本节着重讲使用 access specifer和 derivation access specifer来实现access control,本文中,有一个关于access control的重要设计概念:
Each Class Controls Access To Its Own Members. (每个类负责控制各自成员的访问权限。)
这句话包含有多层含义,在分析这句话之前,首先搞清楚类的用户的概念。
我们在目前的访问控制和继承的学习中,可以概括得认为只有三类用户:
1.类的实现者
2.普通用户
3.派生类
一般作为类的设计者,会留出Base类的接口,其中,public的接口对于普通用户和派生类都是可以访问的;private的接口对于两类用户都是不可访问的。protected的接口是介于public和private的Access specifer,该类接口对于普通用户是不可见的,而对于派生类的成员和友元是可访问的。
为了便于学习,我们定义基类Base,包含有一个public成员,一个protected成员,一个private成员。
class Base{
public:
void pubMem();
protected:
int proMem;
private:
int priMem;
};
然后通过代码了解对于三类用户,即派生类的实现者的访问控制,定义三个继承类,分别采用公有、保护、私有的方式继承。在这三个类的基础上,进一步增加一层公有继承,一共六个类,类的继承关系图如下:
struct DerivationPublic:public Base {
int f(){ return proMem; }; // 派生类用户可以访问基类的保护成员
//int g(){ return priMem }; 错误 派生类用户试图访问基类的私有成员
};
struct DerivationPrivate : private Base{
int f(){ return proMem; };
};
struct DerivationProtected : protected Base{
int f(){ return proMem; };
};
//可以看出派生访问控制符,并不影响第三类用户对基类的成员访问特性,即派生类用户对基类的成员访问性。
//Derivation access specifer影响的是,派生类中的基类成员在该派生类中的Access specifer。
//为了验证上面一句话,我们继续进行派生。
struct Derivation_from_public: public DerivationPublic
{
int f(){ return proMem; };
void g(){ pubMem(); };
};
struct Derivation_from_protected : public DerivationProtected
{
int f(){ return proMem; };
void g(){ pubMem(); };
};
struct Derivation_from_private : public DerivationPrivate
{
//int f(){ return proMem; }; 错误,因为在DerivationPrivate中,所有基类成员(class Base)
//void g(){ pubMem(); };错误,都作为私有成员被继承,而私有成员对于派生类用户是不可见的.
};
由此,我们可以看出对于第三类用户,即派生类用户而言,派生类的实现中,可以访问基类的公有成员和保护成员,其Derivation access specifer并不影响类中成员对于基类的访问权限。Derivation access specifer所造成的影响在于派生类中基类部分的成员access specifer,对此,在MSDN中的说明如下
也就是说,公有继承会继承基类成员原本的access specifer,(基类的公有成员仍作为派生类的公有成员,保护成员仍作为派生类中的保护成员)。私有继承会将继承的基类中的公有成员和保护成员变成派生类中的私有成员,保护继承会将基类中的公有成员及保护成员都作为派生类中的保护成员。
而基类的私有成员则统一被隐藏,不能通过一般的方法实现访问。
下面再贴一段代码,分别创建对于基类及其派生类的对象,通过对象对类内成员的访问来表现C++的Access control。
int main(){
Base b;
b.pubMem();
//b.proMem; 错误,不可访问
//b.priMem; 错误,不可访问
DerivationPublic bpu;//公有派生的普通用户
//bpu.f(); //其本身的Public成员。
bpu.pubMem(); //Class DerivationPublic 中 ,Base 部分的PubMem保持public访问控制。
DerivationProtected bpr;
//bpr.f();
//bpr.pubMem();//此时,DerivationProtected中的 PubMem被作为protected,不可被用户访问
DerivationPrivate bpi;
//bpi.pubMem(); 错误 不可访问
Derivation_from_public dfpub;
Derivation_from_private dfpri;
//dfpri.pubMem(); 错误 不可访问
Derivation_from_protected dfpro;
//dfpro.pubMem(); 错误 不可访问
//下面一部分是对派生类向基类的转换可访问性实验
Base *p;
p = &b;
p = &bpu; //公有继承,Base类作为公有成员被继承,所以可以使用向Base的转换。
//p = &bpr; 错误 非公有继承,不可使用向Base的转换
//p = &bpi; 错误 非公有继承,不可使用向Base的转换
p = &dfpub;
//p = &dfpri; 错误 第一次继承非公有继承,故不可使用向Base的转换
//p = &dfpro; 错误 第一次继承非公有继承,故不可使用向Base的转换
}
对于派生类向基类转换的可访问性,有转换的用户,Derivation access control 影响。
可以概括为以下三条:
1.第二类用户即普通用户,只能使用公有继承的派生类向基类的转换。
2.第三类用户即派生类的代码,如派生类的成员函数或是友元,可以使用任何继承方式派生的派生类向基类的转换。
3.对于第三类用户的第三类用户(即派生类的派生类中的成员函数或是友元),如果第一次派生的继承方式是公有继承或是保护继承,那么二次派生类内部代码可以实现一次派生类向基类的转换,如果一次派生为私有继承,则不可以。
谈到友元,我们知道友元关系是不具有传递性的,即当class B为class A的友元,而class C又为class B的友元时,无法推断出class C是class A的友元。(这告诉我们朋友的朋友不能拿来吹NB o(╯□╰)o)
在友元的非传递性中,我们就可以看出了上面提到的重要原则。
Each Class Controls Access To Its Own Members.
Each Class Controls Access To Its Own Members.
Each Class Controls Access To Its Own Members.
同样的,友元的关系不可被继承;但在继承的关系中,特别要注意一点,即被继承的基类成员的访问权限,仍旧由基类自己所控制,而不是被派生类所控制。代码如下:
class Base{
//other memebers
friend class FR;
int _base;
};
class Drva :public Base{
int _j;
};
class FR{
public:
int f(Base b){ return b._base; };
//int g(DRVA d){ return d._j; };错误,FR是基类Base的友元,无法访问其派生类中定义的成员 _j ;
int k(DRVA d){ return d._base; };//正确,因为_base成员不是在派生类中定义的,\
//而是作为基类成员被继承的。所以可以通过Derivation._base_member来访问。
};