本文主要阐述关于C++继承中基类与派生类之间的访问关系
继承方式与访问方式
- 继承定义格式:
派生类可以继承定义在基类的成员,但是派生类的成员函数不一定有权访问从基类继承来的成员
访问限定符的作用:控制派生类从基类继承而来的成员是否对派生类用户可见。
protected受保护的成员
一个类使用protected关键字的作用是希望与派生类分享但是不希望其它的公共成员访问它,可以看作是public与private取中的产物。
- 与私有成员类似,protected成员对于类的用户是无法访问的(类外)
- 与公共成员类似,protected成员对于派生类的成员和友元是可以访问的(类内)
- 派生类的成员或友元只能通过派生类对象来访问基类的受保护成员,派生类对于一个基类对象中的受保护成员没有任何访问权限(大家可能感觉在这有点矛盾,且看下面分析)
对于派生类中的成员:
class person {
person() {
a = 10;
}
protected:
int a;
};
class student : public person {
person obj;
void foo() {
a = 11;
obj.a = 12;
}
};
派生类中的成员函数想要“通过对象“来访问这个对象中的protected成员,那么这个对象必须要是派生类实例化出来的,不能是基类实例化出来的。因为基类实例化出来的对象,这个对象属于“类外”。
如果是基类实例化出来的对象,那么派生类中的成员函数也只能访问这个基类对象的public成员,不能直接访问protected成员,没有特权。
对于派生类的友元:
class Person
{
protected:
string _name;//受保护成员
};
class Student : public Person
{
friend void fun1(Student&);//可以访问_name
friend void fun2(Person&); //不可以访问_name
protected:
int _stunum;
};
void fun1(Student& a1)
{
a1._name;
}
void fun2(Person& a2)
{
a2._name;
}
派生类的成员和友元只能访问派生类对象中基类部分的受保护成员,对于普通的基类对象中的成员(类外)不具有特殊的访问权限。
共有、私有、受保护继承
某个类对其继承来的成员的访问权限受以下两个因素影响:
- 基类中该成员的访问限定符
- 派生类的派生列表中的访问限定符
1. 派生类可以访问基类的公有成员和受保护成员
我们现在只需要了解派生类的作用域嵌套在基类的作用域之内,因此,对于一个派生类成员来说,使用派生类成员与使用基类成员的方式没有什么区别。
遵循基类的接口:
每个类负责定义各自的接口,要想与类的交互必须使用该类的接口,即使这个对象是派生类的基类部分。
派生类对象不能直接初始化基类的成员,虽然在语法上我们可以在构造函数体内给他的公有或保护的基类成员赋值,但是我们最好不这样做。派生类应该遵循基类的接口,通过调用基类的构造函数来初始化其基类部分。
继承关系分析:
class Person
{
public:
void print()
{
cout << _age << endl;
}
protected:
string _name;
private:
int _age;
};
class Student : public Person
{
public:
int fun()
{
//public继承能访问protected成员
cout << _name << endl;
//不能访问private成员
cout << _age << endl;
}
};
class Teacher : private Person
{
public:
int fun()
{
//private继承能访问protected成员
cout << _name << endl;
//不能访问private成员
cout << _age << endl;
}
};
2.派生访问说明符的目的是控制派生类用户(包括派生类的派生类在内)对于基类成员的访问权限
派生类的派生类:
class child : public Teacher
{
public:
int fun()
{
//child继承于Teacher,Teacher是private继承于Person,
//对于child来说Person的成员都是不可访问的
cout << _name << endl;
cout << _age << endl;
}
};
派生类的实例化对象(用户):
Student a1; //Student是public继承
Teacher a2; //Teacher是private继承
a1.print(); //对于其实例对象 public继承能访问基类的public成员
a2.print(); //private继承 实例出的对象不能访问基类任何成员
3. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它
4.基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的
5.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式
6.在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强
派生类向基类转换的可访问性
派生类向基类的转换是否可访问由使用该转换的代码决定,同时派生类的派生访问说明符也会有影响。假设D继承自B
- 只有当D公有(public)继承自B时,用户代码才能使用派生类向基类的转换,如果D继承自B的方式是受保护的或私有的,则用户不能使用该转换。
- 不论D以什么方式继承B,D的成员函数和友元都能使用派生类向基类的转换。派生类向其直接基类的类型转换对于派生类的成员和友元来说永远可访问。
- 如果D继承B的方式是公有或受保护的,则D的派生类成员和友元可以使用D向B的类型转换;反之,如果D继承B的方式是私有,则不能使用。
对于代码中给定的节点来说,如果基类的公有成员是可访问的,则派生类向基类的类型转换也是可访问的,反之则不行。
类的设计与受保护成员:
我们认为类有三种用户:普通用户、类的实现者、派生类
- 普通用户编写的代码使用类的对象,这部分代码只能访问类的公有(接口)成员
- 类的实现者负责编写类的成员和友元的代码,成员与友元既能访问公有也能访问私有
- 基类把希望派生类能够使用的部分声明为protected保护,普通用户不能使用受保护的成员,派生类及其友元依旧不能访问private私有成员
- 基类应该将其接口声明为公有,将属于自己实现的部分分为两类:一类可供派生类访问,另一组只能由基类或基类的友元访问。对于前者声明为protected保护,这样派生类就能使用基类的操作和数据,对于后者声明为private私有
如何改变个别成员的可访问性(using声明)
有时候我们需要改变派生类继承的某个名字的访问级别,可以通过使用using声明来达到这一目的
class Person
{
public:
string _name; // 姓名
string _sex; // 性别
int _age; // 年龄
};
class Teacher : private Person
{
public:
using Person::_name;
protected:
using Person::_age;
private:
using Person::_sex;
int _No; // 学号
};
因为Teacher使用了private私有继承,所以继承来的成员是Teacher的私有成员,但我们使用using声明语句改变了这些成员的可访问性,Teacher的用户将可以使用_name成员,Teacher的派生类也能使用_name与_age
通过在类的内部使用using声明语句,我们可以将该类的直接或间接基类的任何可访问成员标记出来。using声明语句中名字的访问权限由该using声明语句之前的访问说明符决定,比如using Person::_sex; 我们声明在private下,所以它是private私有成员
派生类只能为那些在当前类中可以访问的名字提供using声明
如有错误或者不清楚的地方欢迎私信或者评论指出🚀🚀