virtual

简单说明:

纯虚函数就像是java 中的接口函数,不能直接实例化,必须被派生类继承,而后对基类中的虚函数进行实现。 虚函数的使用就是为了方便多态的使用,经常须要在基类中定义虚函数,而后对基类进行继承,再对基类中的虚函数进行重载。java

virtual在函数中的使用限制

  • 普通函数不能是虚函数,也就是说虚函数的定义必须在某个类中。虚函数不能够是一个全局函数,不能够单独在类外定义,不然会致使编译错误。
  • 静态成员函数不能是虚函数,即static成员函数是和类同生共处的,它不属于任何一个对象,使用virtual也将致使错误。
  • 构造函数不能是虚函数,不然会形成编译错误
  • 内联函数不能是虚函数,若是内联函数被virtual修饰,计算机会忽略inline使之变成纯虚函数。

函数重载和虚函数的不一样

  • 重载的几个函数必须在同一个类中,覆盖的函数必须在有继承关系的不一样类中
  • 重载的函数必须函数名相同,参数列表不一样。覆盖的几个函数必须函数名、参数、返回值都相同。
    函数重载中,参数列表不一样的目的就是为了,在函数调用时编译器可以经过参数来判断程序是在调用哪一个函数。 这也很天然的解释了为何函数不能经过返回值不一样来重载,由于程序在调用函数时颇有可能不关心返回值,编译器就没法从代码中看书程序在调用的是哪一个函数。
  • 覆盖的函数前必须加关键字virtual,重载和virtual没有任何关系。

关于C++的隐藏规则

  • 若是派生类的函数与基类的函数同名,可是参数不一样。此时,不管有无virtual关键字,积累的函数将被隐藏(注意别与重载混淆)。
  • 若是派生类的函数与积累的函数同名,而且参数也相同,可是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

纯虚函数

C++语言为咱们提供了一种语法结构,经过它能够指明,一个虚拟函数只是提供了一个可被子类型改写的接口。可是,它自己并不能经过虚拟机制被调用,这就是纯虚拟函数(pure virtual function)。纯虚函数的声明以下所示:函数

class Shape
{
  public:
        virtual double calcArea()//虚函数
        {  
                ////////
        }
      virtual double calcPerimeter() = 0;//纯虚函数
        ////纯虚函数没有函数体,同时在定义的时候函数名后腰加 ”=0“
}

虚函数的实现原理

什么是函数指针?
指针指向函数就叫函数指针。函数的本质是一段二进制代码,咱们能够经过指针指向这段代码的开头。计算就会从这个开头开始执行,直到函数结束为止。
函数的指针和普通的指针本质上是同样的,都是由4个基本的内存单元组成,存储着内存的地址,这个地址就是函数的首地址。
多态的实现原理。指针

  • 虚函数指针类中除了定义的函数成员,还有一个成员是虚函数表指针(占4个基本内存单元),这个指针指向一个虚函数表的起始位置,这个表会与类的定义同时出现,这个表存放着该类的虚函数指针,调用的时候能够找到该类的虚函数表指针,经过虚函数表指针找到虚函数表。经过虚函数表的偏移找到函数的入口地址,从而找到要使用的虚函数。
  • 当实例化一个该类的子类对象的时候,若是该类的子类没有定义虚函数,可是却从父类中继承了虚函数,因此在实例化该类子类对象的时候也会产生一个虚函数表,这个虚函数表是子类的虚函数表,可是记录的子类的虚函数地址倒是与父类是同样的。因此,经过子类对象的虚函数表指针找到本身的虚函数表,在本身的虚函数表找到的要执行的函数指针也是父类的相应函数入口的地址。
  • 若是咱们在子类中定义了从父类继承来的虚函数,对于父类来讲状况是不变的,对于子类来讲它的虚函数表与以前的虚函数表是同样的,可是此时子类定义了本身的(从父类那继承来的)相应函数,因此它的虚函数表当中关于这个函数的指针就会覆盖掉原有的指向父类函数的指针的值。
    换句话说就是指向了本身定义的相应函数,这样若是用父类的指针,指向子类的对象,就会经过子类对象当中的虚函数表指针找到子类的虚函数表,从而经过子类的虚函数表找到子类的相应虚函数的地址,而此时的地址已是该函数本身定义的虚函数入口地址,而不是父类的相应虚函数入口地址,因此执行的将会是子类当中的虚函数,这就是多态的原理。

函数的覆盖和隐藏

父类和子类出现同名函数称为隐藏code

  • 父类对象.函数名()
    调用父类的函数
  • 子类对象.函数名()
    调用子类函数
  • 子类对象.父类名::函数名()
    子类调用从父类继承来的函数

父类和子类出现同名虚函数称为覆盖对象

  • 父类指针=new 子类名(……);
  • 父类指针->函数名(……);
    调用子类的虚函数

虚析构函数的实现原理继承

  • 虚析构函数的实现原理:
    当咱们在父类中经过virtual修饰析构函数以后,经过父类指针指向子类对象,经过delete接父类指针就能够释放掉子类对象。
    执行完子类的析构函数就会执行父类的析构函数。
    原理:
    若是父类当中定义了虚析构函数,那么父类的析构函数表当中就会有一个父类的虚析构函数指针,指向的是父类的虚析构函数,子类虚析构函数表当中也会产生一个子类的虚析构函数的入口指针,指向的是子类的虚析构函数,这个时候使用父类的指针指向子类的对象,delete接父类指针,就会经过指向的子类的对象找到子类的虚函数表指针,从而找到虚函数表,在虚函数表中找到子类的虚析构函数,从而使得子类的析构函数得以执行,子类的析构函数执行以后系统会自动执行父类的虚析构函数。这个是虚析构函数的实现原理。

纯虚函数的实现原理
在虚函数原理的基础上,虚函数表中,虚函数的地址是一个有意义的值,若是是纯虚函数就实实在在的写一个0 含有纯虚函数的类被称为抽象类
含有纯虚函数的类被称为抽象类。哪怕类中只有一个纯虚函数,那么这个类也是一个抽象类,纯虚函数没有函数体,因此抽象类不容许实例化对象,抽象类的子类也能够是一个抽象类。
抽象子类只有把抽象类中全部纯虚函数都作了实现才能够实例化对象。接口

仅含有纯虚函数的类称为接口类
若是在抽象类中仅含有纯虚函数而不含其余东西,咱们称之为接口类。内存

  • 没有数据成员
  • 仅有成员函数
  • 成员函数都是纯虚函数
class Shape
{
       virtual double calcArea() = 0;
         virtual double calcPerimeter() = 0;
}
相关文章
相关标签/搜索