继承和多态的“真相”

1. 这篇随笔解决的所有疑惑和灵感都是来自于知乎上这位前辈的一段话:知乎

2. 关于继承的问题,其实关键点就是子类的对象有没有继承父类的对象的私有变量,关于这一点,答案毫无疑问,是包含的,而且子类对象包含父类所有成员

是父类指针指向子类对象的基础,是多态的前提。但是仅仅能够通过父类提供的接口(public或protected成员函数)来访问到,之所以这样设计,也是考虑到

父类的private变量只能通过父类提供的接口访问,不能有其他想法。其实关于这一点,可能主要涉及到语义的问题,stackoverflow上有一个答案说的比较好:

3. 关于成员函数的继承,首先可以确认的是,肯定不能继承的函数有:构造函数、析构函数、(复合)赋值运算符函数。

4. 其次,成员函数的继承与成员变量的继承很不一样,这里只需要记得一句话,成员变量是和对象绑定的,成员函数是和类绑定的。

5. 成员变量是和对象相关的,这句话其实不太对,因为static成员变量是和类相关的,该类的所有对象共享一份static变量的存储,所以以前想的Java接口的多继承可能也采用

    了C++的虚继承机制是扯淡。

    成员函数是和类相关的,原因主要是因为每个成员函数的第一个参数都是隐式的classname* this指针,而且,有一点是很明白的,函数只有一份存储,所以关于多继承

    的时候采取虚继承机制来避免重复拷贝成员变量,但是对于成员函数则全无此必要。

6. 关于成员函数中调用成员函数的情况,其实一直困惑了自己蛮久。

    其实是很简单的问题,假设父类的一个public函数调用了自己的一个private函数,然后子类通过调用这个public函数来成功调用了父类的这个private函数。

    原因是这样的,在这个public函数内部,是通过this指针来调用这个private函数的,所以通过子类对象或指针来调用public函数后,函数的隐式this指针参数

    指向子类对象,然后,在函数内部,通过父类的对象指针this来调用父类的private函数,这是天经地义的事情,因为函数是和类相关的,通过父类指针当然

    可以调用父类函数,而且,在C++中,private函数可以变虚,所以如果子类中重写了这个private函数的话,还会产生多态。(Java中private方法不能重写)。

    所以函数里面调用函数和子类"继承”父类的私有函数完全没有一点关系,子类永远不能"继承”父类的私有函数(即通过子类对象或指针访问)。

7. Java中不能“继承”的函数就意味着不能重写,包括私有方法、包方法(不同包的继承)、以及接口中的静态方法(静态方法本来就不能重写)。

    C++则全然没有这样的规定,可以让私有函数变虚,可见还是C++比较灵活一点。

8. 关于C++不能实现所谓的“跨类重载”,前面的随笔已经有所提到。其实注意到Java中重写有这样一条规定,就是重写的函数不能低于父类的访问权限,

    而C++中却并无此规定,所以有时候会有一些比较“诡异”的情况出现,所以还是遵循Java中的“best practice”为好。

9. 关于多态,C++的实现就是虚函数表和虚基类指针,其实也是比较简单的原理,只是各大编译器实现的细节肯定不太一样。

     可以参考:陈皓:C++虚函数表解析从头到尾漫谈虚函数C++虚基类的实现机制

10. 关于C++中的派生类向基类转换的可访问性,参考下图,以及C++ primer P544页。

11. 怎么说呢,其实自己这次纠结了这么久,主要考虑的感觉是深度探索C++对象模型的事,而且,你也知道,那本书是学习C++的高级读物。

      所以你现在来浪费大量编码时间来自己苦苦思索这些问题,而且还因为该死的完美主义始终不能说服自己,其实是得不偿失的,这些东西,

      通过实践是可以获得的,实践出真知,该踩的坑还是要踩的。

      最后还是希望自己可以回到初衷:深入探索C++,理解原理与触摸底层,而Java或者C#,就只需要专注于代码逻辑,更快更好地写出代码就行了。

原文地址:https://www.cnblogs.com/niuxichuan/p/6107779.html