前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

virtual

作者头像
青木
发布2018-05-28 15:25:07
6240
发布2018-05-28 15:25:07
举报

简单说明:

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

virtual在函数中的使用限制

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

函数重载和虚函数的不同

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

关于C++的隐藏规则

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

纯虚函数

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

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

虚函数的实现原理

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

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

函数的覆盖和隐藏

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

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

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

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

虚析构函数的实现原理

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

纯虚函数的实现原理 在虚函数原理的基础上,虚函数表中,虚函数的地址是一个有意义的值,如果是纯虚函数就实实在在的写一个0 含有纯虚函数的类被称为抽象类 含有纯虚函数的类被称为抽象类。哪怕类中只有一个纯虚函数,那么这个类也是一个抽象类,纯虚函数没有函数体,所以抽象类不允许实例化对象,抽象类的子类也可以是一个抽象类。 抽象子类只有把抽象类中所有纯虚函数都做了实现才可以实例化对象。

仅含有纯虚函数的类称为接口类 如果在抽象类中仅含有纯虚函数而不含其他东西,我们称之为接口类。

  • 没有数据成员
  • 仅有成员函数
  • 成员函数都是纯虚函数
代码语言:javascript
复制
class Shape
{
       virtual double calcArea() = 0;
         virtual double calcPerimeter() = 0;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简单说明:
  • virtual在函数中的使用限制
  • 函数重载和虚函数的不同
  • 关于C++的隐藏规则
  • 纯虚函数
  • 虚函数的实现原理
  • 函数的覆盖和隐藏
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档