继承与多态知识点

访问权限

  1. 子类无法访问父类的private成员
  2. 父类的proected成员可以被子类(子类内部)访问,不可以被外部访问
  3. private会被继承,但无法访问。(可以通过查看子类大小验证)
  4. 继承默认是private方式
  5. 父类中成员通过相应的继承方式继承给子类后,这些成员对于子类来说,访问权限以访问方式为最低级别(如果原成员访问级别比访问方式低,则原成员对子类来说访问权限不变)。例如,父类中变量A为private权限,无论通过什么方式继承,子类都不可以访问该变量

继承中的构造顺序

先构造父类,后构造子类

析构的顺序

先析构子类,后析构父类

成员方法之间的关系

1、重载:

  • 作用域相同
  • 函数名相同
  • 参数列表不同(子类不能重载父类的成员方法,因为作用域不同)

2、隐藏

  • 子类会隐藏父类中相同函数名的方法。(无关返回值和参数列表,只需函数名相同即可)

3、覆盖

  • 子类会覆盖与父类相同(同函数名、同参数列表、同返回值)的虚函数,覆盖只会发生在虚函数表中。

虚函数表:如果类中有虚函数(被virtual修饰的函数,或父类中存在继承而来的),则其实例的最开始有一个虚函数指针:vfptr。虚函数指针指向一个虚函数表。虚函数表中存放的是函数指针,即为实现该虚函数的地址。

例如有如下代码:

class A
{
public:
    virtual void Show()
    {
        cout << "this is A" << endl;
    }        
};

class B
{
    Show()
    {
        cout <<"this is B" << endl;
    }
}

     类A在编译的时候会产生一个进入Show函数的地址,记为地址SA,同样类B在编译的时候会产生一个进入Show函数的地址,记为SB。SA和SB存在.rodata段(存放一些不可以被修改的常量数据)中。

     如果A对象实例话一个对象a,此时a对象的前4的字节就是一个指针,该指针指向一个虚函数表,虚函数表中存有A类提供的Show函数的其实地址SA,此时a调用Show函数,因为Show函数是一个虚函数,编译器就会去虚函数表中找该函数的入口地址,即进入SA,因此调用的就是A类的Show方法了。

    同理,如果B对象实例话一个对象b,此时b对象的前4的字节也是一个指针,该指针指向一个虚函数表,虚函数表中存有B类提供的Show函数的其实地址SB,此时b调用Show函数,因为Show函数也是一个虚函数(父类中的虚函数继承到子类中也是一个虚函数),编译器就会去虚函数表中找该函数的入口地址,即进入SB,因此调用的就是B类的Show方法了。

多态的类型

1、静多态:编译时期的多态。有:重载、模板

2、动多态:运行时期的多态。即继承中的多态

动多态发生的条件:

  • 指针调用
  • 虚函数
  • 对象完整:即在调用构造函数之后到调用析构函数之前的阶段。

纯虚函数

不需要实现的虚函数。形如:virtual void Call() = 0;

拥有纯虚函数的类成为虚基类。虚基类不允许实例化对象。

相关注意事项:

1、什么时候析构函数必须写成虚函数?

存在基类的指针指向堆上的子类的对象。因为析构函数不写成虚函数有可能会发生内存泄漏。

2、构造函数能否写成虚函数?
不可以。因为虚函数表在对象中,调用虚函数必须存在对象。而构造函数就是产生对象的,因此自相矛盾

3、inline函数能否写成虚函数?

不可以。因为inline函数编译期在调用点展开,release版本没有地址。虚函数表中存储的是函数地址。

4、static函数能否写成虚函数?

不可以。因为没有this指针,无法找到对象的地址,就无法找到虚函数表。static函数可以不依赖对象调用。而虚函数表存在于对象中。

猜你喜欢

转载自blog.csdn.net/qq_41727218/article/details/86434851