C++访问控制与继承

来源:互联网 发布:js面向对象有什么用 编辑:程序博客网 时间:2024/06/16 22:21

本文大部分内容为转载,部分为自己添加,转载地址:http://www.cnblogs.com/songlee/p/5738134.html

关键概念:类的设计与受保护的成员(参考《C++ Primer 中文版第五版》15.5节P544-545)

不考虑继承的话,我们通可以认为一个类有两种不同的用户:普通用户 和 类的实现者。其中,普通用户编写的代码使用类的对象,这部分代码只能访问类的公有(接口)成员;实现者则负责编写类的成员和友元的代码,成员和友元既能访问类的公有部分,也能访问类的私有(实现)部分。如果进一步考虑继承的话就会出现第三种用户,即派生类。派生类可以访问基类的公有(public)成员和受保护(protected)成员,但不能访问基类的私有(private)成员。

基类把它希望派生类能够使用的部分声明成受保护的,从而实现普通用户不能访问受保护的成员,而派生类及其友元(派生类的友元)仍不能访问私有成员。

和其他类一样,基类应该将其接口成员声明为共有;同时将其实现的部分分为两组:一组供派生类访问,应将其声明为受保护的,这样派生类就能在实现自己的功能时,使用基类的操作和数据;另一组只能由基类及其友元访问,应该声明为私有的。

继承相关点:

  • 大多数类都只继承自一个类,这种形式的继承叫做“单继承”。本文主要讲的是单继承。
  • 一个派生类的对象中,包含继承自基类的部分和派生类自定义的部分。正因为派生类含有基类部分,所以可以进行派生类到基类的类型转换(仅限指针或引用),这种转换是隐式的。
  • 不存在从基类向派生类的隐式类型转换。
  • 派生类向基类的自动类型转换只对指针或引用有效,对象之间不存在类型转换。
  • 如果基类定义了静态成员,则不论派生出多少个派生类,每个静态成员都只存在唯一实例。
  • 防止一个类被继承可以使用关键字final,这时C++11新标准中提供的。
  • 继承中的虚函数与纯虚函数(见文章)。


一、公有、私有和受保护成员

1 . 访问说明符

在C++中通过使用访问说明符public、protected、private来对类的成员进行访问控制,控制成员对于普通用户或派生类来说是否可访问:

  • public:定义为public的成员对普通用户、类的实现者、派生类都是可访问的。public通常用于定义类的外部接口。

  • protected:定义protected成员的目的是让派生类可以访问而禁止其他用户访问。所以类的实现者和派生类可以访问,而普通用户不能访问。

  • private:定义为private的成员只能被类的实现者(成员和友元)访问。private部分通常用于封装(即隐藏)类的实现细节。

  1. class People{  
  2. protected:  
  3.     string name;  
  4. };  
  5.   
  6. class Student : public People{  
  7. public:  
  8.     friend void Print(Student &s);  
  9.     friend void Print(People &p);  
  10. };  
  11.   
  12. // 正确,可以通过派生类对象访问基类的protected成员  
  13. void Print(Student &s){ s.name="Songlee"; cout<< s.name << endl; }  
  14. // 错误,不能通过基类对象访问基类的protected成员  
  15. void Print(People &p){ p.name="Songlee"; cout<< p.name << endl; }  

需要注意的是,派生类的成员或友元只能通过派生类对象来访问基类的受保护成员。派生类对于一个基类对象中的受保护成员没有任何访问特权。

2 . 改变成员的可访问性

有时我们需要改变派生类继承的某个名字的访问级别,通过使用using声明:

  1. class People{  
  2. protected:  
  3.     string name;  
  4. };  
  5.   
  6. class Student : public People{  
  7. public:  
  8.     using People::name;  // 将继承来的name成员的访问权限改为public  
  9. };  
  10.   
  11. int main()  
  12. {  
  13.     Student me;  
  14.     me.name = "SongLee";     // 可以访问name了  
  15.     cout << me.name << endl;    
  16.     return 0;  
  17. }  
通过在类的内部使用using声明语句,我们可以将该类的直接或间接基类中的任何可访问成员(非私有成员)标记出来,改变其访问权限。

二、公有、私有和受保护继承

我们注意到,在类的派生列表中用到了访问说明符public、protected和private,它们分别表示不同的继承方式:

  1. class A : public B { /*  */ };     // 公有继承  
  2. class A : private B { /*  */ };    // 私有继承  
  3. class A : protected B { /*  */ };  // 受保护继承  

派生类的派生列表中的访问说明符对于派生类的成员(及友元)能否访问其直接基类的成员没什么影响。派生类的成员(及友元)对基类成员的访问权限只与基类中的访问说明符有关。

那么派生列表中的访问说明符有什么作用呢?
派生列表中访问说明符的作用控制派生类用户对于基类成员的访问权限,注意是派生类的用户。下面给出不同的继承方式导致的访问权限的变化:

  • public继承:如果继承是公有的,则成员将遵循其原有的访问说明符。父类中的public、protected和private属性在子类中不发生改变。

  • protected继承:比protected级别高的访问权限会变成protected。即父类中的public属性在子类中变为protected,父类中的protected和private属性在子类中不变。

  • private继承:比private级别高的访问权限会变成private。即父类中的三种访问属性在子类中都会变成private。

  1. class A {   // 基类  
  2. public:  
  3.     string A_public;      // 公有成员  
  4. protected:  
  5.     string A_protected;   // 受保护成员  
  6. };  
  7.   
  8. class B : private A {    // 私有继承  
  9. public:  
  10.     B(){ A_public="public"; A_protected="protected"; };  
  11. };  
  12.   
  13. int main()  
  14. {  
  15.     B b;   // 通过B的对象访问  
  16.     cout << b.A_public <<" "<< b.A_protected << endl;   // 错误,因为是私有继承  
  17.     return 0;  
  18. }  

如果我们在派生列表中不使用访问说明符,则struct关键字默认的是公有继承,class关键字默认的是私有继承。事实上,struct和class的唯一差别就是默认成员访问说明符及默认派生访问说明符,除此之外,再无其他不同。不过建议在继承时最好显式地将访问说明符写出来。

三、友元与继承

像友元关系不能传递一样(参考《C++ Primer 中文版第五版》7.3.4节P250),友元关系也不能继承。基类的友元在访问派生类成员是不具有特殊性,相应地,派生类的友元也不能随意访问基类成员。

  1. class A {   // 基类  
  2. public:  
  3.     string A_public;      // 公有成员
  4.     friend class C;      // C在访问A的派生类时布局特殊性
  5. protected:
  6.     string A_protected;   // 受保护成员
  7. private:
  8.     string A_private;   // 私有成员
  1. };  

  2. class B : public A{
  3. private:
  4.     string B_private;   // 私有成员
  5. };

  6. class C{
  7. public:
  8.    string f(A a) {return a.A_protected}//正确,C是A的友元
  9.     string f2(B b) {return b.B_private}//错误,C不是B的友元
  10.    //对基类的访问权限由基类本身控制,即使对于派生类的基类部分也是如此
  11.     string f3(B b) {return b.A_protected}//正确,C是A的友元
  12. };
每个类控制自己的成员的访问权限,当一个类(A)将另一个类(C)声明为友元时,这种友元关系只对做出声明的类(A)有效,对原来的那个类(A)来说,其友元的基类(C的基类,如果存在的话)或者派生类不具有特殊的访问能力。

  1. class D : public C{
  2. public:
  3.     string f(A a) {return a.A_protected} //错误,友元关系不能继承
  4. };
牢记:不能继承友元关系,每个类负责控制各自成员的访问权限。


派生类向基类转换的可访问性(参考《C++ Primer 中文版第五版》15.5节P544

另外,不同的继承方式也会影响派生类向基类的转换,假定Derive继承自Base:

  • 只有当Derive公有地继承自Base时,用户代码才能使用派生类向基类的转换;如果Derive继承Base的方式是受保护的或者私有的,则用户代码不能使用该转换。

  • 不论Derive以什么方式继承Base,Derive的成员函数和友元都能使用派生类向基类的转换;派生类向其直接基类的类型转换对于派生类的成员和友元来说永远是可访问的。

  • 如果Derive继承Base的方式是公有的或者受保护的,则Derive的派生类的成员和友元可以使用Derive向Base的类型转换;反之,如果Derive继承Base的方式是私有的,则不能使用。

1 0
原创粉丝点击