访问控制与继承
2024年1月5日 2024年1月6日
继承引入了protected关键字:
- 类的用户无法访问
- 派生类的成员和友元可以通过派生类对象来访问基类的受保护成员; 对基类对象的成员不具有特殊的访问权限
- 友元和成员函数可以访问
派生类向基类转换的可访问性
- 派生类公有的继承基类, 用户代码可以使用派生类向基类的转换
- 派生类向其直接基类的类型转换对于派生类的成员函数和友元永远都是可访问的
- 如果直接派生类私有继承基类, 间接派生类无法使用直接派生类到基类的转换
1#include <iostream> 2 3using namespace std; 4 5class FooBase 6{ 7public: 8 FooBase(int aa) : a(aa) {} 9 FooBase() : FooBase(0) {} 10 11 void printFooBase() { cout << a; } 12 13private: 14 int a; 15}; 16 17class Foo : protected FooBase 18{ 19 friend void testFunction(Foo &f); 20 21public: 22 void printFoo() { printFooBase(); } 23}; 24 25void testFunction(Foo &f) 26{ 27 FooBase &fb = f; // 正确: testFunction为Foo的友元, 可以使用派生类到基类的转换 28 fb.printFooBase(); 29 cout << endl; 30} 31 32int main() 33{ 34 Foo f; 35 FooBase &fb = f; // 错误: Foo派生为protected 36 37 testFunction(f); 38 39 return 0; 40}
类的设计
不考虑继承时
类的用户有两种:
- 程序员, 使用类的公有接口和友元
- 类的设计者, 实现成员函数和友元, 既可以访问类的公有部分, 也能使用类的私有(实现)部分
加入继承
第三种用户: 派生类
基类仍提供接口供类的用户使用 public
, 但把实现分为两部分:
- 供派生类访问
protected
派生类在实现自己的功能时可以使用基类的这些操作和数据 - 供基类自己及其友元访问
private
友元关系既不能传递, 也不能继承
- 基类友元可以通过派生类对象访问基类部分
- 派生类友元只能通过派生类对象访问基类部分
1class Base 2{ 3 friend class Pal; // Pal可以访问Base对象, 以及派生类对象中的Base子对象 4protected: 5 int prot_mem; 6}; 7 8class Sneaky : public Base 9{ 10 int j; 11}; 12 13class Pal 14{ 15public: 16 int f(Base b) { return b.prot_mem; } // 正确: Pal是Base的友元 17 int f2(Sneaky s) { return s.j; } // 错误: j并不属于Base子对象 18 int f3(Sneaky s) { return s.prot_mem; } // 正确: Sneaky派生自Base, prot_mem属于Base子对象 19}; 20 21class D2 : public Pal 22{ 23public: 24 int mem(Base b) { return b.prot_mem; } // 错误: 友元关系不能继承 25};
每个类负责控制各自成员的访问权限
改变个别成员的可访问性
通过 using
声明
将直接基类或间接基类中的可访问成员标记为新的访问权限
派生类只能为那些它可以访问的名字提供using声明
1class Base 2{ 3public: 4 size_t size() const { return n; } 5protected: 6 size_t n; 7}; 8 9class Derived : private Base 10{ 11public: 12 using Base::size; // Base的成员到了Derived变为private, 重新设置为public 13protected: 14 using Base::n; // Base的成员到了Derived变为private, 重新设置为protected 15};