c++ 派生类向基类转换的可访问性
首先明确几个问题:
1.成员函数和友元函数、友元类的权限是一样一样一样一样的,是最高的权限,private、protected、public成员都可以随意访问。
2.用户代码(类外调用的其他代码)权限是最低的,只有类的public成员才能访问(不考虑静态成员),protected、private都无法访问。
3.继承体系,如类B继承于类A,只要有private(不管是A的private还是B的继承方式)都会得到private;没有private的,只要有protrected都会得到protected;其他的都是public。这样实际的意义是:基类私有的变量,你不能通过继承,还反而变成公有了,那岂不是类型不安全了?随便继承下就可以改动基类的数据了!另外:初学者都喜欢背继承规则(我当时就是),不要死记那个继承方式表,没有用,要理解原因!
基于以上两个前提,有以下性质:
转载于:https://www.cnblogs.com/zmlctt/p/3728342.html,同时添加了自己的注解(加粗部分)
声明:加粗的解释是我个人的理解,不保证正确性!
1.如果派生类以public继承基类,则是is a关系,用派生类可以完成基类的所有功能,所以可以在任意地方将派生类自动转换成基类,注意,这里都是指指针或引用,而不是对象。 比如:
class A{};
class B:public A{}
B b;
void function(const A &);
这时就可以使用function(b),会默认把B类型转换成A类型。
解释:动态多态的内容
2.假定D继承B:
不论D以什么方式继承B,D的成员函数和友员函数都能使用派生类向基类的转换;派生类向其直接基类的类型转换对于派生类的成员函数和友员函数来说永远是可访问的。
例如:
class B{}
class D:private B //这里以private或者protected或者public 都可以
{
void f()
{
B * base=new D; //编译正确
}
}
解释:前提说过:成员函数和友元函数的权限是最高的,派生类-->基类,也并不会造成数据不安全,所以可以。
如果不是成员函数(即用户代码),例如:
class B{};
class D:private B //如果这里是public就可以,private或protected会错
{
/*void f()
{
B * base=new D;
}
*/
};
int main()
{
B * b=new D; //会出现编译错误,注意,这是用户代码
};
解释:如果B * b=new D;可以编译成功,是想干什么?显然是要多态,动态调用B类/D类的不同版本的函数。那么我们假设B类的一个public函数f,D类继承该函数f,但注意是private/protected,所以D类的f函数也对应的为:private或者protected。那么问题也就变成了另一个问题:用户代码可以调用一个类的private/protected成员函数吗?不可以阿!所以B * b=new D;这样写的目的(多态)是无法达到的,那么也就没有存在的意义,所以C++语法规定这种调用不能通过编译是合情合理的。
3.如果D继承B的方式是public或者protected,则D的派生类的成员或者友员可以使用D向B的类型转换;反之,如果D继承B的方式是private,则不能使用。
class B{};
class D:public B{};//public或protected都可以
class E:private D或者protected D或者public D
{
void f()
{
B *b=new D; //可以编译通过
D *d=new E; //可以编译通过,这就是2介绍的情况。
}
};
解释:
只说第一行:B* b=new D;为什么能编译通过。逆向思维,为什么要这样写,这样能通过编译是为了做什么?肯定是要动态调用B或D类的不同版本函数(多态),对吧?假设B的一个public成员函数f,D类(public继承B)里是可以继承该f函数的,所以E类(继承于D,什么方式无所谓)也继承了D类的f函数,也就是说B类的f函数经过继承体系,最终在E类里可见,可以通过B类对象来调用这个f函数。所以B* b=new D;能编译成功,必然有其意义在,有意义才有存在的理由,不然就没有存在的价值。(有点哲学意味,但我认为这样反推是有道理的。)
但是如果变成private继承:
class B{};
class D:private B{};
class E:private D或者protected D或者public D
{
void f()
{
B *b=new D; //错,不可以编译
D *d=new E; //可以编译,这就上2介绍的情况
}
};
解释:
只说第一行:B* b=new D;为什么无法通过编译。还是和上面一样的想法:B类有没有成员可以被E类调用/使用的?如果有,那么就应该可以编译成功,但是实际编译失败,那么我们来看看到底行不行:
假设B类的一个public成员函数f,在D类(private继承B)中,f函数可以继承于B类的,但属性变成了private。E类再继承D类,E类肯定无法继承D类的f函数了,对不对?(这时候就不要考虑B类了,这样想:D类是基类,有一个private成员函数f,E类继承于D类,E类显然继承不了这个f函数)也就是说,经过我们分析,B类的public成员都无法在E类中被调用/使用,更不用说B类的protected/private成员了。结论:由于B类的任何成员都无法被E类继承,那么E类中就不应该允许B *b=new D; 这样的调用存在,因为根本就没有任何成员/函数给它进行多态!
继承体系测试代码:
#include<iostream> using namespace std; class A{}; class B:public A{}; class C:protected A{}; class D:private A{}; class E:public B{}; class F:public C{}; class G:public D{}; int main(){ A *pb, *pc, *pd, *pe, *pf, *pg; pb = new B; // 正确 public派生,可以转换[*B ---> *A]. pc = new C; // 错误 protected派生,不可转换[*C -\-> *A]. pd = new D; // 错误 private派生,不可转换[*D -\-> *A]. pe = new E; // 正确 public派生的子类,可以转换[*E ---> *A]. pf = new F; // 错误 pg = new G; // 错误 private派生的子类,不可转换[*G -\-> *A]. return 0; }